首页 > 代码库 > 信号量,消息队列,共享内存中ket_t键值的生成函数ftok。

信号量,消息队列,共享内存中ket_t键值的生成函数ftok。

在System V中,我们经常用用key_t的值来创建或者打开信号量,共享内存和消息队列。这个在IPC的环境中十分的重要,比如说,服务器创建了一个消息队列,等待 客户机发送请求。那么如何创建或者打开已有的消息队列呢?一般而言,我们对于服务器使用的路径和项目id(proj_id)是已知的,所以客户机可以获取 相同的key来打开 消息队列并进行操作。下面就是ftok的使用原型:

ftok函数

  函数ftok把一个已存在的路径名和一个整数标识得转换成一个key_t值,称为IPC键:

       # include 
       # include

       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。

    该函数把从pathname导出的信息与id的低序8位组合成一个整数IPC键。


# include <sys/types.h>
# include <sys/ipc.h>

key_t ftok(const char *pathname, int proj_id);
注意:
1)pathname一定要在系统中存在
2)pathname一定是使用进程能够访问的
3)proj_id是一个1-255之间的一个整数值,典型的值是一个ASCII值。
当成功执行的时候,一个key_t值将会被返回,否则-1被返回。

下面的程序简单的演示和打印如何使用ftok及其对应值

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
int main( void )
{
    for ( int i = 1; i < 256; ++ i )
        printf( "key = %ul/n", ftok( "/tmp", i  ) );
    return 0;
}

注意:

根据pathname指定的文件(或目录)名称,以及proj_id参数指定的数字,ftok函数为IPC对象生成一个唯一性的键值。在实际应 用中,很容易产生的一个理解是,在proj_id相同的情况下,只要文件(或目录)名称不变,就可以确保ftok返回始终一致的键值。然而,这个理解并非 完全正确,有可能给应用开发埋下很隐晦的陷阱。因为ftok的实现存在这样的风险,即在访问同一共享内存的多个进程先后调用ftok函数的时间段中,如果 pathname指定的文件(或目录)被删除且重新创建,则文件系统会赋予这个同名文件(或目录)新的i节点信息,于是这些进程所调用的ftok虽然都能 正常返回,但得到的键值却并不能保证相同。由此可能造成的后果是,原本这些进程意图访问一个相同的共享内存对象,然而由于它们各自得到的键值不同,实际上 进程指向的共享内存不再一致;如果这些共享内存都得到创建,则在整个应用运行的过程中表面上不会报出任何错误,然而通过一个共享内存对象进行数据传输的目 的将无法实现。
AIX、Solaris、HP-UX均明确指出,key文件被删除并重建后,不保证通过ftok得到的键值不变,比如AIX上ftok的man帮助信息即声明:
Attention: If the Path parameter of the ftok subroutine names a file that has been removed while keys still refer to it, the ftok subroutine returns an error. If that file is then re-created, the ftok subroutine will probably return a key different from the original one.
Linux没有提供类似的明确声明,但我们可以通过下面的简单例程test01.c,得到相同的印证:


<span style="background-color: rgb(255, 255, 255);">#include <stdio.h>
#include <sys/ipc.h>
void main(int argc, char* argv[])
{
if (argc !=2 ) {
		printf("Usage: %s KeyFile/n e.g. %s /tmp/mykeyfile/n", argv[0], argv[0]);
		return;
	}
	printf("Key generated by ftok:  0x%x/n", ftok(argv[1], 1));
}
</span>

将上述例程在Red Hat Enterprise Linux AS release 4平台上编程成可执行程序test01,并且通过touch命令在 /tmp目录下创建一个新文件mykeyfile,然后为该文件生成键值:
# touch  /tmp/mykeyfile
# ./test01 /tmp/mykeyfile
Key generated by ftok:  0x101000b


然后,将/tmp/mykeyfile删除,并且通过vi命令重新创建该文件,再次生成键值:
# ./test01 /tmp/mykeyfile
Key generated by ftok:  0x1010017

可以看到,虽然文件名称都是 /tmp/mykeyfile,并未改变,但由于中间发生了文件删除并重新创建的操作,前后两次所得到的键值已经不再相同。避免此类问题最根本的方法,就是采取措施保证pathname所指定的文件(或目录)在共享内存的使用期间不被删除,不要使用有可能被删除的文件;或者干脆直接指定键值,而不借助ftok来获取键值。

我在CENTOS5.2下实测结果:如果用同样的方式创建mykeyfile,得到的键值不变。
如果创建方式不同则键值不同。

关于ftok函数的几个疑问

系统建立IPC通讯(如消息队列、共享内存时)必须指定一个ID值。通常情况下,该id值通过ftok函数得到。
ftok原型如下:
key_t ftok( char * fname, int id )
fname就时你指定的文件名,id是子序号。

在一般的UNIX实现中,是将文件的索引节点号取出,前面加上子序号得到key_t的返回值。

如指定文件的索引节点号为65538,换算成16进制为0x010002,而你指定的ID值为38,换算成16进制为0x26,则最后的key_t返回值为0x26010002。
查询文件索引节点号的方法是: ls -i

当删除重建文件后,索引节点号由操作系统根据当时文件系统的使用情况分配,因此与原来不同,所以得到的索引节点号也不同。

如果要确保key_t值不变,要目确保ftok的文件不被删除,要么不用ftok,指定一个固定的key_t值。

另外说一句:在aix等操作系统上,有多个文件系统,会出现分布在不同的文件系统上的两个文件具有相同的索引节点号,此时用ftok对这两个文件进行操作,只要id参数不变,得到的key_t值相同,造成创建消息队列失败。不过这种情况相当少见罢了。

信号量,消息队列,共享内存中ket_t键值的生成函数ftok。