共享内存应用中的问题及解决方法

来源:互联网 发布:java增删改查实例项目 编辑:程序博客网 时间:2024/06/09 15:23

System V 进程间通信(IPC)包括3种机制:消息队列、信号量、共享内存。消息队列和信号量均是内核空间的系统对象,经由它们的数据需要在内核和用户空间进行额外的数据拷贝;而共享内存和访问它的所有应用程序均同处于用户空间,应用进程可以通过地址映射的方式直接读写内存,从而获得非常高的通信效率。

System V 为共享内存定义了下列API接口函数:

# include <sys/types.h># include <sys/ipc.h># include <sys/shm.h>key_t ftok(const char *pathname, int proj_id);int shmget(key_t key, int size, int shmflg);void* shmat(int shmid, const void *shmaddr, int shmflg);int shmdt(void *shmaddr);int shmctl(int shmid, int cmd, struct shmid_ds *buf);

ftok 函数用于生成一个键值:key_t key,该键值将作为共享内存对象的唯一性标识符,并提供给为shmget函数作为其输入参数;ftok 函数的输入参数包括一个文件(或目录)路径名:pathname,以及一个额外的数字:proj_id,其中pathname所指定的文件(或目录)要求必须已经存在,且proj_id不可为0; shmget 函数用于创建(或者获取)一个由key键值指定的共享内存对象,返回该对象的系统标识符:shmid; shmat 函数用于建立调用进程与由标识符shmid指定的共享内存对象之间的连接; shmdt 函数用于断开调用进程与共享内存对象之间的连接; shmctl 函数用于对已创建的共享内存对象进行查询、设值、删除等操作;

2. ftok的陷阱

根据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,得到相同的印证:


#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));}

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


# touch  /tmp/mykeyfile# ./test01 /tmp/mykeyfileKey generated by ftok:  0x101000b

然后,将/tmp/mykeyfile删除,并且通过vi命令重新创建该文件,再次生成键值:


# ./test01 /tmp/mykeyfileKey generated by ftok:  0x1010017

我们可以看到,虽然文件名称都是 /tmp/mykeyfile,并未改变,但由于中间发生了文件删除并重新创建的操作,前后两次所得到的键值已经不再相同。

避免此类问题最根本的方法,就是采取措施保证pathname所指定的文件(或目录)在共享内存的使用期间不被删除,不要使用有可能被删除的文件;或者干脆直接指定键值,而不借助ftok来获取键值。

原创粉丝点击