在使用Linux的一些IPC手段中,例如共享内存、消息队列等,有一个参数key。
下面以共享内存的shmget函数为例,如下所示:
/* shmget - allocates a System V shared memory segment
* shmget() returns the identifier of the System V shared memory segment associated with the value of the argument key.
*/
int shmget(key_t key, size_t size, int shmflg);
从描述看,分配的共享内存与参数key值有关。
首先,我们实现一个共享内存的demo,两个角色:reader和writer。
首先让shmreader进程跑起来,然后运行shmwriter,随便写个字符串后回车,shmreader也能立即收到消息(这里不展示demo代码了,不是这里的重点。),如下图所示。
上述的demo中,我通过ftok("./file", 'm')这样的方式来获取key值。
下面看一下ftok函数的描述——ftok() 函数使用给定路径名命名的文件的标识(必须引用现有的、可访问的文件)和 proj_id 来生成 key_t 类型的 System V IPC 密钥。
FTOK(3) Linux Programmer's Manual FTOK(3)
NAME
ftok - convert a pathname and a project identifier to a System V IPC key
SYNOPSIS
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
DESCRIPTION
The ftok() function uses the identity of the file named by the given pathname (which must
refer to an existing, accessible file) and the least significant 8 bits of proj_id (which
must be nonzero) to generate a key_t type System V IPC key, suitable for use with
msgget(2), semget(2), or shmget(2).
The resulting value is the same for all pathnames that name the same file, when the same
value of proj_id is used. The value returned should be different when the (simultaneously
existing) files or the project IDs differ.
RETURN VALUE
On success, the generated key_t value is returned. On failure -1 is returned, with errno
indicating the error as for the stat(2) system call.
从代码看key生成的逻辑:
(1)id取低8位后左移24位。
(2)获取文件设备号的低8位,并将其左移16位,作为键值的中间8位。
(3)获取文件的inode号后16位,作为键值的低16位。
key_t
ftok(const char *path, int id)
{
const unsigned int u_id = id;
struct stat st;
if (stat(path, &st) == -1)
return (key_t)-1;
return (key_t)
((u_id & 0xff) << 24 | (st.st_dev & 0xff) << 16 | (st.st_ino & 0xffff));
}
下面看一个实例,获取key = ftok("./file", 0):
(1)设备号传入0,也就是高24位以后都是0。
(2)运行下面命令,左边第一个数字即为设备号。
>>> ls -l file
-r-------- 1 root root 4 Jul 27 17:51 file
(3)运行下面命令,返回197920582,是文件inode,取低16位为0x0746。
>>> ls -i file
197920582 file
最终得出key值为0x00010746,即67398。
>>> ipcs -m
------------ 共享内存段 --------------
键 shmid 拥有者 权限 字节 连接数 状态
0x000a4b95 0 postgres 600 56 6
0x00000000 1212420 mi 600 7654700 2 目标
0x00000000 1212421 mi 600 79560 2 目标
0x51130023 6 mi 600 152 1
0x00000000 9 mi 600 524288 2 目标
0x00000000 327691 mi 600 393216 2 目标
0x00000000 12 mi 600 524288 2 目标
0x00000000 15 mi 600 524288 2 目标
0x00000000 18 mi 600 524288 2 目标
0x00000000 19 mi 600 524288 2 目标
0x00000000 20 mi 600 524288 2 目标
0x00000000 21 mi 600 524288 2 目标
0x00000000 491547 mi 600 16777216 2 目标
0x00000000 458786 mi 600 524288 2 目标
0x00010746 2326563 mi 666 68 0
0x00000000 40 mi 600 524288 2 目标
0x00000000 43 mi 600 524288 2 目标
0x00000000 44 mi 600 524288 2 目标
0x00000000 45 mi 600 17360 2 目标
0x6d130001 557103 mi 666 68 0
0x00000000 2457648 mi 600 70800 2 目标
0x6d010614 458801 mi 666 68 0
0x00000000 1703986 mi 600 524288 2 目标
0x00000000 524339 mi 600 1405256 2 目标
0x6d010745 524340 mi 666 68 0
0x6d010746 557109 mi 666 68 0
0x00000000 2097207 mi 600 7654700 2 目标
0x00000000 65598 mi 600 524288 2 目标
测试debug发现:
- id即使写0,也能获取到key值,也能用于共享内存通信;
- 传入的pathname,如果是一个不存在的文件,则无法获取到key值;
- 传入的pathname,即使进程没有权限访问(前提是文件存在),即使文件权限为000,也可以获取到key值;
- 传入的pathname,即使文件内容有所修改,key值也还是不会变化的;
- 即使shmget传入的key值不变,只要共享内存销毁后再重新创建,对应的shmid也会发生变化;