现在的位置: 首页 > 综合 > 正文

key_t IPC键和ftok函数详解和剖析

2017年12月11日 ⁄ 综合 ⁄ 共 3298字 ⁄ 字号 评论关闭

统建立IPC通讯(如消息队列、共享内存时)必须指定一个ID值。通常情况下,该id值通过ftok函数得到。
ftok原型如下:
key_t ftok( char * fname, int id )
fname就时你指定的文件名(该文件必须是存在而且可以访问的),id是子序号,虽然为int,但是只有8个比特被使用(0-255)。

当成功执行的时候,一个key_t值将会被返回,否则 -1 被返回。

在一般的UNIX实现中,是将文件的索引节点号取出,前面加上子序号得到key_t的返回值。如指定文件的索引节点号为65538,换算成16进制为 0x010002,而你指定的ID值为38,换算成16进制为0x26,则最后的key_t返回值为0x26010002。
查询文件索引节点号的方法是: ls -i

 

例如:

  #include        "unpipc.h"

  int main(int argc, char **argv)

  {

        struct stat     stat;

        if (argc != 2)

                err_quit("usage: ftok ");

        Stat(argv[1], &stat);

        printf("st_dev: %lx, st_ino: %lx, key: %x/n",

              (u_long) stat.st_dev,
(u_long) stat.st_ino,

              Ftok(argv[1], 0x57));

        exit(0);

  }

程序运行结果:

      [cbs@linux svipc]$ ./ftok  /tmp/mysql.sock

     st_dev: 802, st_ino: 34219, key: 57024219

ftok的典型实现调用stat函数,然后组合以下三个值:

1.pathname所在的文件系统的信息(stat结构的st_dev成员)

2.该文件在本文件系统内的索引节点号(stat结构的st_ino成员)

3. proj_id的低序8位(不能为0)
从程序运行的结果可以看出,ftok调用返回的整数IPC键由proj_id的低序8位(57),st_dev成员的低序8位(02),st_info的低序16位组合而成(4219)。
 

注:两进程如在pathname和proj_id上达成一致(或约定好),双方就都能够通过调用ftok函数得到同一个IPC键。
那么ftok是怎么实现的呢?《UNIX网络编程》上讲到,ftok的实现是组合了三个值:
  • pathname所在文件系统的信息(stat结构的st_dev成员)
  • pathname在本文件系统内的索引节点号(stat结构的st_ino成员)
  • id的低序8位(不能为0)
具体如何组合的,根据系统实现而不同。

使用ftok()需要注意的问题:

  • pathname指定的目录(文件)必须真实存在且调用进程可访问,否则ftok返回-1;
  • pathname指定的目录(文件)不能在程序运行期间删除或创建。因为文件每次创建时由系统赋予的索引节点可能不一样。这样一来,通过同一个pathname与proj_id就不能保证生成同一个IPC键。
附:文件属性结构

struct stat
{
    dev_t         st_dev;      /* device */
    ino_t         st_ino;      /* inode */
    mode_t        st_mode;     /* protection */
    nlink_t       st_nlink;    /* number of hard links */
    uid_t         st_uid;      /* user ID of owner */
    gid_t         st_gid;      /* group ID of owner */
    dev_t         st_rdev;     /* device type (if inode device) */
    off_t         st_size;     /* total size, in bytes */
    blksize_t     st_blksize;  /* blocksize for filesystem I/O */
    blkcnt_t      st_blocks;   /* number of blocks allocated */
    time_t        st_atime;    /* time of last access */
    time_t        st_mtime;    /* time of last modification */
    time_t        st_ctime;    /* time of last status change */
};

获取文件属性的函数有如下几个:

int stat(const char *file_name, struct stat *buf);
int fstat(int filedes, struct stat *buf);
int lstat(const char *file_name, struct stat *buf);

下面通过例子来看一下如何获取:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

int main()
{
    const char fname[] = "main.c";
    struct stat stat_info;

    if(0 != stat(fname, &stat_info))
    {
        perror("取得文件信息失败!");
        exit(1);
    }

    printf("文件所在设备编号:%ld\r\n", stat_info.st_dev);
    printf("文件所在文件系统索引:%ld\r\n", stat_info.st_ino);
    printf("文件的类型和存取的权限:%d\r\n", stat_info.st_mode);
    printf("连到该文件的硬连接数目:%d\r\n", stat_info.st_nlink);
    printf("文件所有者的用户识别码:%d\r\n", stat_info.st_uid);
    printf("文件所有者的组识别码:%d\r\n", stat_info.st_gid);
    printf("装置设备文件:%ld\r\n", stat_info.st_rdev);
    printf("文件大小:%ld\r\n", stat_info.st_size);
    printf("文件系统的I/O缓冲区大小:%ld\r\n", stat_info.st_blksize);
    printf("占用文件区块的个数(每一区块大小为512个字节):%ld\r\n", stat_info.st_blocks);
    printf("文件最近一次被存取或被执行的时间:%ld\r\n", stat_info.st_atime);
    printf("文件最后一次被修改的时间:%ld\r\n", stat_info.st_mtime);
    printf("最近一次被更改的时间:%ld\r\n", stat_info.st_ctime);
    return 0;
}

编译并执行输出:
$ gcc -o getstat main.c
$ ./getstat
文件所在设备编号:2051
文件所在文件系统索引:61079722
文件的类型和存取的权限:33188
连到该文件的硬连接数目:1
文件所有者的用户识别码:502
文件所有者的组识别码:503
装置设备文件:0
文件大小:1210
文件系统的I/O缓冲区大小:4096
占用文件区块的个数(每一区块大小为512个字节):8
文件最近一次被存取或被执行的时间:1320997735
文件最后一次被修改的时间:1320994242
最近一次被更改的时间:1320994242

抱歉!评论已关闭.