被食人蝇咬过图片:共享内存应用中的问题及解决方法

来源:百度文库 编辑:九乡新闻网 时间:2024/10/05 21:59:14
SystemV进程间通信(IPC)包括3种机制:消息队列、信号量、共享内存。消息队列和信号量均是内核空间的系统对象,经由它们的数据需要在内核和用户空间进行额外的数据拷贝;而共享内存和访问它的所有应用程序均同处于用户空间,应用进程可以通过地址映射的方式直接读写内存,从而获得非常高的通信效率。
System V 为共享内存定义了下列API接口函数:
# include
# include
# include
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 afile that has been removed while keys still refer to it, the ftoksubroutine returns an error. If that file is then re-created, the ftoksubroutine will probably return a key different from the original one.
Linux没有提供类似的明确声明,但我们可以通过下面的简单例程test01.c,得到相同的印证:
#include
#include
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/mykeyfile
Key generated by ftok: 0x101000b
然后,将/tmp/mykeyfile删除,并且通过vi命令重新创建该文件,再次生成键值:
# ./test01 /tmp/mykeyfile
Key generated by ftok: 0x1010017
我们可以看到,虽然文件名称都是 /tmp/mykeyfile,并未改变,但由于中间发生了文件删除并重新创建的操作,前后两次所得到的键值已经不再相同。
避免此类问题最根本的方法,就是采取措施保证pathname所指定的文件(或目录)在共享内存的使用期间不被删除,不要使用有可能被删除的文件;或者干脆直接指定键值,而不借助ftok来获取键值。