进程中的标识符和键(ftok函数创建)(共享内存,消息队列,信号量用到的)

xiaoxiao2021-02-28  57

每个内核中的IPC结构(消息队列、信号量和共享内存)都用一个非负整数的标识符(identifier)加以引用。

    也就是说,一个非负整数代表一个IPC结构。  

比如,为了对一个消息队列发送或者取消息,只需要知道其队列标识符,与文件描述符不同,IPC标识符不是小的正数。当一个IPC结构被创建,以后又被删除,与这种结构相关的标识符连续加1,直到达到整型数的最大正直,然后又回到0

对于同一个IPC结构,为了使多个进程都能获取标识符ID指向这个IPC结构,需要大家都能访问到文件路径和项目id来创建一个东西,拿这个东西来生成IPC的结构,这个东西就是:

键(key)。

举个例子,不同的人都想进同一个带锁的房间交流,是不是都需要相同钥匙打开?

 

1.键(key)的创建(使用ftok函数)

每次创建IPC结构,都必须指定一个键,键的数据类型是基本系统数据类型key_t,通常在头文件<sys/types.h>中,定位为整型(《Unix环境高级编程》说是长整型,但我编译时发现是整型)。键由内核变换成标识符。

 

       #include <sys/types.h>

       #include <sys/ipc.h>

 

       key_t ftok(const char *pathname, int proj_id);

 

ftok根据提供的路径pathname和项目proj_id生成一个键,如果返回-1,则出错。

值得一提的是,ftok可能会返回一个负数,只要不是-1就是有效的key

ftok函数只使用proj_id的低8位(0255的值),也就是说proj_id不管值多大,也只取最低8位。

pathname必须是一个有效的路径及可访问的。

 

生成的key是留给semget,msgget,shmget使用的。

一个key和一个整型的flag,满足以下条件之一,就创建一个新的IPC结构:

(1)keyIPC_PRIVATE

(2)key当前未与特定类型的IPC结构相结合,并且flag中指定了IPC_CREAT

 

如果希望创建一个新的IPC结构,而且要确保不是引用具有同一标识符的一个现已存在的IPC结构,那么必须在flag中同时指定IPC_CREATIPC_EXCL位,加上这两个标志位后,如果IPC结构已经存在,就是导致创建出错,返回EEXIST

 

如果访问一个现存的IPC结构,就必须知道与IPC结构相结合的标识符ID,通过ID去访问,

并且创建的键值不能 IPC_PRIVATE,因为 IPC_PRIVATE是拿来创建一个key的。

 

 

 

2.自己实现一个键的创建

ftok创建的键一般由以下方式构成:按给定的路径名取其stat结构,从该结构中取出部分st_devst_ino字段,然后再与项目ID组合起来,如果两个路径名引用不同的文件,那么对这两个路径名调用ftok通常返回不同的键。但是因为i节点号和键通常都放在长整型中,于是创建键时可能会丢失信息。这意味着如果使用同一项目ID,那么对于不同文件的两个路径名可能产生相同的键。

以下程序展示了键的创建原理:

#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <string.h> #include <time.h> #include <errno.h> #include <unistd.h> #include <sys/stat.h> #define ProgramId (0x11&0x00ff) int main() { key_t ftok_key=-1; struct stat stuStat; ftok_key = ftok("/share/1.tmp",ProgramId); printf("\n ftok_key[0x%x] errno=%d [%s]\n",ftok_key,errno,strerror(errno)); memset(&stuStat,0,sizeof(struct stat)); stat("/share/1.tmp",&stuStat); printf("\n st_dev[0x%x] st_ino[0x%x] ProgramId[0x%x] \n", (unsigned int )stuStat.st_dev,(unsigned int )stuStat.st_ino,ProgramId); return 0; }

运行结果:

root@ubuntu:/share# gcc test_ftok.c

root@ubuntu:/share#

root@ubuntu:/share#

root@ubuntu:/share#

root@ubuntu:/share# ./a.out         

 

 ftok_key[0x11014d87] errno=0   [Success]

 

 st_dev[0x801]  st_ino[0x64d87] ProgramId[0x11]

root@ubuntu:/share#

 

从结果来看,函数ftok产生的键值就是取ProgramId的低8bitst_dev的后8bitst_ino16bit,组成一个32位的整数。

 

附带提下stat函数和结构体:

#include <sys/types.h>

       #include <sys/stat.h>

       #include <unistd.h>

 

       int stat(const char *pathname, struct stat *buf);

 

  struct stat {

       dev_t  st_dev;  /* ID of device containing file */

       ino_t  st_ino;  /* inode number */

       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 ID (if special file) */

       off_t  st_size;  /* total size, in bytes */

       blksize_t st_blksize;  /* blocksize for filesystem I/O */

       blkcnt_t  st_blocks;  /* number of 512B blocks allocated */

 

       /* Since Linux 2.6, the kernel supports nanosecond

  precision for the following timestamp fields.

  For the details before Linux 2.6, see NOTES. */

 

       struct timespec st_atim;  /* time of last access */

       struct timespec st_mtim;  /* time of last modification */

       struct timespec st_ctim;  /* time of last status change */

 

   #define st_atime st_atim.tv_sec /* Backward compatibility */

   #define st_mtime st_mtim.tv_sec

   #define st_ctime st_ctim.tv_sec

   };

 

st_dev属于long long unsigned int类型

st_ino属于long unsigned int类型

使用的ftok注意防范隐患

由于ftok函数是根据路径的i节点和设备id、项目id来生成键值的,所以在ftok创建的键值,生成的进程ipc结构标识符后,不要去删除路径,然后再次生成路径,因为i节点可能会发生变化,导致另外进程生成的键值不一样,无法进行消息队列通信、共享内存、信号量等,这是非常大的隐患,如果产生bug,将会非常难查。

转载请注明原文地址: https://www.6miu.com/read-30726.html

最新回复(0)