Posix IPC

本文深入探讨PosixIPC的三大组成部分:消息队列、信号量和共享内存,介绍它们的创建、打开及权限管理等核心概念,并针对移植性问题提供了解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Posix IPC

2.1概述

以下三种类型的ipc合称为“POSIX IPC”

以下三种类型的ipc合称为“Posix IPC”

Posix消息队列
Posix信号量
Posix共享内存

posix IPC的所有函数:

        消息队列        信号量             共享内存
头文件 <mqueue.h>  <semaphore.h>   <sys/mman.h>

创建、   mq_open       sem_open            shm_open
打开  mq_close        sem_close           shm_unlink
或者  mq_unlink       sem_unlink
删除                  sem_init
的函                  sem_destroy
数

控制  mq_getattr                                 ftruncate
IPC的    mq_setattr                             fstat
操作
函数


IPC      mq_send                    sem_wait                     mmap
操作    mq_receive              sem_trywait                 munmap
            mq_notify                 sem_post 
                                                  sem_getvalue

IPC的名字

三种类型的posix IPC都使用POSIX IPC的名字进行标识。mq_open、sem_open和shm_open这三个函数的第一个参数就是这样的一个名字,它可能是某个文件系统中的一个真正的路径名,也可能不是,posix.1就是这么描述posix的ipc名字的.

它必须复合已有路径名规则(必须最多由PATH_MAX个字节构成,包括尾部的空字节).如果它以斜杠符号开头,那么对这些函数的不通调用将访问同一个队列

如果它以斜杠符号开头,那么对这些函数的不同调用将访问同一个队列。如果它不以斜杠符号开头,那么效果取决于现实.

名字中额外以斜杠符号的解释由现实定义.

因此,为了方便移植,POSIX IPC的名字必须用一个斜杠符号开头,并且不能再包含有任何其他的斜杠符号.遗憾的是这些规则还不够,仍然会出现移植性问题.

solaris 2.6要求有斜杠开头,但是不允许有另外的斜杠符打头,并且不能再含有任何其他的斜杠。遗憾的是这些规则还是不够的,仍然会出现移植性问题。

Solaris2.6 要求有斜杠打头,但是不允许有另外的斜杠,假设要创建一个消息队列,创建函数在/tmp中创建三个用.MQ开头的文件。例如给mq_open的纯属为/queue.1234,那么这三个文件分别为/tmp/.MQDqueue.1234,那么这三个文件分别是/tmp/.MQDqueue.1234、/tmp/.MQLqueue.1234和/tmp/.MQPqueue.1234。

当我们指定一个只有单个斜杠(作为首字符)的名字的时候,移植性问题就发生了:我们必须在根目录中具有写权限。例如,/tmp.1234复合Posix规则,在Solaris下也行,但是在Digttal unix却会试图创建这个文件,这时候处分我们在根目录中有写权限,负责这样做将会尝试失败。如果我们指定一个/tmp/.test.1234这样的名字,那么在该名字创建一个之中文件的所有系统上都将成功。前提是/tmp目录存在,而且我门在这个目录上有写权限,对于大多数linux都是正常情况,在solaris下就会失败.

为了避免这些移植性问题,我们应该把posix ipc的名字的#define 放在一个便于秀爱的头文件之中,这样在移植操作系统的时候就需要修改这个头文件

posix.1定义了下面三个宏

S_TYPEISMQ(buf)
S_TYPEISSEM(buf)
S_TYPEISSHM(buf)

他们单个参数是指向某个stat结构的指针,其内容由fstat、lstat或stat这三个函数填入。如果制定的ipc对象(消息队列,信号量或者是共享内存)是作为一个独特的文件类型实现的,而且参数指向的stat结构访问这样的文件类型,那么这三个宏计算出一个非0值,否则计算出的值为0.

px_ipc_name函数

解决上述问题的另一种办法是自定义一个px_ipc_name的函数,它为定位posix ipc名字而添加上正确的前缀目录

#include "unpipc.h"
char *px_ipc_name(const char *name);

本书中我们给自己定义的非标准系统函数都使用这样的版式:围绕函数圆形和返回值的方框都是虚框,name参数不能有任何的斜杠

px_ipc_name("test1");

char *px_ipc_name(char* name)
{
    char *dir,*dst,*slash;

    if((dst = malloc(PATH_MAX)) == NULL)
    {
    return  NULL;
    }

    if((dir = getenv("PX_IPC_NAME")) == NULL) {
#ifdef  POSIX_IPC_PREFIX
    dir = POSIX_IPC_PREFIX;
#else
    dir = "/tmp/";
#endif
    }

slash = (dir[strlen(dir)-1] == '/') ? "" : "/";

snprintf(dst,PATH_MAX,"%s%s%s",dir,slash,name);

return dst;
}

 

创建与打开ipc通道

mq_open、sem_open和shm_open这三个创建或打开一个ipc对象的函数,他们的名字为oflag的第二个参数制定怎么样打开所请求的对象。这与标准open函数的第二个参数类似.图2-3给出了可组合构成这个参数的各种常值.

前三行制定怎么样打开对象:只读、只写或读写.消息队列能以其中任何一种模式打开,信号量的打开不指定任何模式(任意信号量的操作,都需要读写访问权限),共享内存对象则不能用只写模式打开.

说明:

mq_open

可以使用

只读:O_RDONLY
只写:O_WRONLY
读写:O_RDWR
非阻塞模式,如果已经存在就截断:O_NONBLOCK

sem_open:
不能使用任何指定模式

shm_open
不能用只写模式打开可以使用只读或者读写模式打开O_RDONLY,O_RDWR

非阻塞模式,如果已经存在就截断:O_TRUNC

三个函数如果不存在则创建的排他性创建
O_CREAT
O_EXCL

O_CREAT:

O_CREAT若不存在则创建由函数第一个参数制定名字的消息队列,信号量或者共享内存区的对象,则创建一个新的消息队列、信号量或者共享内存的时候,至少需要另外一个称为mode的参数.这个参数指定权限位

常值
S_IRUSR 用户(属主)读
S_IWUSR 用户(属主)写

S_IRGRP (属)组成员读
S_IWGRP (属)组成员写

S_IROTH 其他用户读
S_IWOTH 其他用户写

这些常值定义在中,这个参数使用umask函数或者shell的umask命令来设置.

跟创建新的文件一样,当创建一个新的消息队列、信号量或者共享内存区对象时候,其用户id被设置为当前进程的有效用户id。信号量或共享内存区对象的组id被设置为当前进程的有效组id或者某个系统默认的id。新的消息队列对象的组id被设置为当前进程的有效组id.

O_EXCL:

如果这个标志和O_CREAT一起指定,那么ipc函数只在指定名字的消息队列,信号量或者共享内存对象不存在的时候才会创建新的对象。如果这个对象已经存在,而且指定了O_CREAT|o_excl,那么他会返回一个EEXIST的错误

主要问题是考虑到其他进程的存在,检查锁指定名字的消息队列、信号量或共享内存区对象的存在和创建它这两个操作必须是原子的。

O_NONBLOCK:

这个标志使得一个消息队列在队列为空或队列填满的时候不再会被阻塞,我们会在mq_receive和mq_send里详细讨论这个标志

O_TRUNC:

如果用只读模式打开一个已经存在的共享内存对象,那么这个标志将会使对象的长度被截断为0

图2.5 展示了打开一个ipc的真正的逻辑流程

1.创建ipc对象:

检查对象是否已经存在?如果不存在使用O_CREAT设置了?如果没有返回errno ENOENT,如果设置了,检查系统表格是否全满,满了返回errno ENOENT,否的话成功创建对象

O_CREAT和O_EXCL都设置了?是返回errno=EEXIST错误

不是的话检查权限是否允许,否的话errno = EACCES 是的话则成功

2.4IPC权限

新的消息队列、有名信号量或共享内存对象是由其oflag参数中含有O_CREAT标志的mq_open、sem_open、或者shm_open函数创建的。如图2-4所注,权限位与这些IPC类型的每个对象关联,与每个unix文件相互关联。

同样又这三个函数打开一个已经存在的消息队列、信号量或共享内存对象的时候定O_CREAT或制定O_CREAT但是没有制定O_EXCL同事对象已经存在,将基于下面的消息执行权限测试:

1)创建时候赋予这个ipc对象的权限位;
2)锁请求的访问类型(O_RDONLY、O_WRONLY或这O_RDWR);
3)调用进程的有效用户ID、有效组ID以及各个辅助组ID(若支持的话).

大多数unix内核按照如下步骤进行权限测试.

1)如果当前进程的有效用户ID为0(超级用户),那就允许访问.

2)在当前进程的有效用户ID等于这个ipc对象的属主的前提下,如果相应用户的权限位已经设置,那么就允许访问,否则会拒绝访问.

比如用户以读权限访问ipc的时候,那么这个用户的读权限位必须要设置

3)在当前进程的有效组ID或它的某个辅助组id等于这个ipc对象的组的id前提下,如果响应的组访问权限已经设置,那么就允许访问,否则拒绝.

4)如果相应的其他用户访问权限位已经设置,那么允许访问,否则禁止访问

这4个步骤按照所列的步骤尝试.如果当前的进程应有ipc对象的时候,那么访问权限的授予或者拒绝只依赖与用户访问权限----组访问权限不会考虑.类似的当前进程不应有这个ipc对象,但是它属于某个组,那么访问允许或者拒绝只依赖组,其他用户访问权限绝不会考虑。

小结:

三种posix ipc------消息队列,共享内存和信号量都是用标识符来标示的.但是这些路径名既可以是文件系统中实际的路径名字,也可以不是,这一点会导致移植性问题书中使用的是px_ipc_name函数.

当我们用open打开ipc对象的时候,我们必须给open的对象访问权限,当打开一个已经存在的ipc对象的时候,锁执行的权限测试与打开一个已经存在的文件时候一样.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值