linux进程通信之消息队列:msg queue

shell 相关命令:ipcs -q -s -m#查看已建立的队列、信号量(semaphore,又译信号灯)、共享内存

ipcrm -q 123  #删除id=123的消息队列

 

一、接口函数:(下述内容基本上翻译的官方man手册,有一些我理解不了的句子,就直接把原文贴上来了)

1、key_t ftok(const char *pathname, int proj_id),由文件路径(文件必须是存在的,且有访问权限)+字符(不能为0,低8位有效,因为以前的linux版本中proj_id为char型,为了兼容)产生一个key值,产生的key值将被用于队列的msgget( ), 信号灯的semget(), or 共享内存的shmget。任何时候调用ftok,只要pathname指向的文件不变,且pathname实参为全路径名,proj_id不变,那么返回值就是相同的,这样就保证了不同进程调用ftok可以产生相同的key。

一个key可以关联多个消息队列

@fname:文件路径
@id: 任意字符,不能为0值,我也不懂为什么,手册原文:Typical usage has an ASCII character proj_id, that is why the behavior is said to be undefined when proj_id is zero.

返回:正常key值,or出错-1且设置errno

注意:该函数并不保证返回的key值是唯一的,也就是说,相同的文件、相同的形参,返回的key一定相同,但是,填入不同的形参返回的key值也有可能相同,Typically, a best effort attempt combines the given proj_id byte, the lower 16 bits of the inode  number,  and  the lower 8 bits of the device number into a 32-bit result.  Collisions may easily happen, for example between files on /dev/hda1 and files on       /dev/sda1.

2、int msgget(key_t key,int msgflg);打开创建一个消息队列

(1)两种情况下它会创建一个新的队列:①key设为IPC_PRIVATE;② key不是IPC_PRIVATE,且msgflg设为IPC_CREAT ,且不存在与该key关联的队列。

(2)若msgflg设为IPC_CREAT | IPC_EXCL,且已存在与key关联的队列,则出错返回-1,并设置errno;

(3)msgflg的值是IPC模式与权限的或运算,其中权限是必须要设置的,从下面3个IPC模式的宏定义也可以看出,模式仅仅定义了8进制的高5位,低3位就是用来设置权限的,权限的意义与格式可参考open函数,如果不设置权限,显然权限值=0000

(4)一旦队列创建成功,内核会维护该队列的信息(格式为structure msqid_ds),例如:当前队列中的字节数、该队列所容许的最大字节数、最后一次读写该队列的进程PID等,通过msgctl函数可以读取这些信息

(5)形参:@key:传入ftok生成的key值;或者传入IPC_PRIVATE(该宏值=0),linux官方手册认为这个宏的名字取的很失败,从名字上看好像是key值私有,不允许其他进程访问,实际上其作用却是:强制msgget函数产生一个新消息队列,因此手册建议用IPC_NEW来代替IPC_PRIVATE

@msgflg:可选的值为

#define IPC_CREAT  00001000   /* create if key is nonexistent */ 若不存在与设定的key相关联的队列,则创建队列;若已存在,则打开队列或者返回错误,到底是打开还是返回错误,取决于IPC_EXCL

 

#define IPC_EXCL   00002000   /* fail if key exists */ 如果key存在则返回错误,一般该值要与IPC_CREAT  配合使用,
#define IPC_NOWAIT 00004000   /* return error on wait */读消息时,若无消息可读,或者,写消息时,若队列已满无法写入,这两种情况则立即返回错误,而不是堵塞等待;

以上3个值或完以后,还可以再或上一个权限值,例如:IPC_CREAT | IPC_EXCL  | 0777

(6)返回:消息队列的id:msgid,在消息队列中该id是唯一的,但可能与IPC其他机制的id重复,例如信号灯id、共享内存id

4、int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);向消息队列中插入新消息/发送消息(必须保证进程对该队列有写权限)

(1)msgp是“用户定义的要发送的数据”的指针,要发送的数据的结构必须为:

       struct msgbuf {
               long mtype;       /* message type, must be > 0 */
               char mtext[1];    /* message data */
           };

 

该结构中mtext域的长度由形参msgsz来指定,msgsz是一个非负整数,设定为0也是允许的;mtype域必须设置为正数,不允许为0或负数,这个值用于供接收方确认数据类型。

(2)如果消息队列的空间足够,那么msgsnd发送消息时会立即成功。队列的消息容量由取决于跟队列相关的数据结构的一个域msg_qbytes。在创建队列时,msg_qbytes被初始化为MSGMNB,我们可以通过msgctl函数随时修改msg_qbytes的值。如果队列的空间不足,默认情况下msgsnd会堵塞,直到队列空间能够容纳本次消息,才解除堵塞;如果形参msgflg含有IPC_NOWAIT旗标,那么msgsnd不会堵塞,而是立即返回“稍后重试”错误:EAGAIN

(3)如果msgsnd被设置为堵塞模式,在堵塞过程中如果发生下列情况时会发生错误:

    ① 队列已被删除,这种情况下会返回EIDRM错误

    ② 堵塞过程中收到了信号signal,这种情况下会返回EINTR错误(参考signal函数获取更多信息)。msgsnd函数堵塞时,一旦被信号处理事件给打断后,是永远不会自动重发的,除非我们建立信号处理函数时设置了SA_RESTART标志(更多细节请参见sigaction函数),该标志的作用是,从信号处理函数返回后,会自动把刚才它打断的那个堵塞地系统调用,给重新执行一遍。

(4)消息一旦发送成功,消息队列数据结构会发生以下更更新:

    ① msg_lspid(last send pid)最后一次发送消息的进程,被更新为本进程;
    ② msg_qnum++;
    ③ msg_stime 消息的send时间被更新为当前时间

(5)返回值:失败会返回-1,并设置errno;成功则返回0

5、ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,  int msgflg); 接收消息

(1)接收的消息被放到形参msgp指定的地址中去,msgp数据结构与发送函数一致,也是struct msgbuf,参见上文。形参msgsz设定了读取的消息的最大字节数(注:指的是struct msgbuf结构的mtext域的最大字节数,而不含mtype域)。
(2)如果实际消息的字节数>msgsz,那么本函数的具体行为将取决于msgflg的值:如果msgflg包含了MSG_NOERROR旗标,那么本函数将从实际消息中截取前msgsz字节,后面的字节被丢弃;如果没有包含MSG_NOERROR旗标,那么队列中的消息不会被删除,msgrcv会立即返回-1,并设置errno为E2BIG。

(3)形参msgtyp用于指定所请求的消息类型:

    ① msgtyp=0时,读取队列中的第一条消息
    ② msgtyp>0时,读取队列中消息类型为msgtyp的第一条消息,除非形参msgflg包含了MSG_EXCEPT旗标,这时将会读取队列中消息类型为不等于msgtyp的第一条消息
    ③ msgtyp<0时,读取队列中消息类型≤|msgtyp|,且消息类型值最小的第一条消息

(4)形参msgflg可以用下列或运算来组建:

    ① IPC_NOWAIT 队列中没有所请求的消息时,立即返回-1,并设置errno为ENOMSG

    ② MSG_EXCEPT 该旗标一般与形参msgtyp的设置值同时使用,见上文msgtyp的讲解

    ③ MSG_NOERROR 消息的实际长度超过了形参msgsz的设置值,则截取

(5)如果队列中没有所请求的类型的消息,且msgflg不包含IPC_NOWAIT旗标,那么本函数将堵塞,直到下列情况发生时才解除堵塞:

    ① 队列中收到了被请求的类型的消息
    ② 队列被删除,这时函数会立即返回-1并设置errno为EIDRM

    ③ 本进程捕获到了信号signal,这时函数立即返回-1并设置errno为EINTR。 msgrcv函数堵塞时,如果被信号处理函数打断,那么msgrcv不会自动发起重新接收,除非建立信号处理时设置了SA_RESTART标志(参见sigaction函数)。

(6)消息接收成功之后,队列的数据结构会发生如下更新:
    ① msg_lrpid(last reveive pid)最后一次读队列的进程,被更新为当前进程
    ② msg_qnum++

    ③ msg_rtime(receive time)被更新为当前时间

(7)返回值:失败返回-1并设置errno;成功则返回读出的数据结构中mtext域的字节数

6、 int msgctl(int msqid, int cmd, struct msqid_ds *buf);队列的控制

(1)形参buf结构为:

 struct msqid_ds {
               struct ipc_perm msg_perm;     /* Ownership and permissions */
               time_t          msg_stime;    /* Time of last msgsnd(2) */
               time_t          msg_rtime;    /* Time of last msgrcv(2) */
               time_t          msg_ctime;    /* Time of last change */
               unsigned long   __msg_cbytes; /* Current number of bytes in queue (nonstandard) */
               msgqnum_t       msg_qnum;     /* Current number of messages in queue */
               msglen_t        msg_qbytes;   /* Maximum number of bytes allowed in queue */
               pid_t           msg_lspid;    /* PID of last msgsnd(2) */
               pid_t           msg_lrpid;    /* PID of last msgrcv(2) */
           };

msg_perm域的结构如下:(uid、gid、mode这三个域可以用形参cmd=IPC_SET来设置)
struct ipc_perm {
               key_t          __key;       /* Key supplied to msgget(2) */
               uid_t          uid;         /* Effective UID of owner */
               gid_t          gid;         /* Effective GID of owner */
               uid_t          cuid;        /* Effective UID of creator */
               gid_t          cgid;        /* Effective GID of creator */
               unsigned short mode;        /* Permissions */
               unsigned short __seq;       /* Sequence number */
           };

(2)形参cmd可取的值有:

    ① IPC_STAT,取出系统保存的消息队列的msqid_ds 数据,并将其存入参数buf 指向的msqid_ds 结构中。
    ② IPC_SET,设定消息队列的msqid_ds 数据中的msg_perm 成员。设定的值由buf 指向的msqid_ds结构给出。
    ③ IPC_RMID,将队列从系统内核中删除。
    ④ IPC_INFO,(没读明白,不翻译了)
    ⑤ MSG_INFO,功能类似于IPC_INFO,区别在于:
    ⑥ MSG_STAT,功能类似于IPC_STAT,区别在于msgid参数不再是真的msgid,而是内核中一个数组的索引,这个数组维护着所有消息队列的信息。

(3)返回值:当cmd为IPC_STAT、IPC_SET、IPC_RMID时,成功则返回0;当cmd为IPC_INFO、MSG_INFO时,成功则返回系统中记录所有队列信息的内核数组的最大的入口值;当cmd=MSG_STAT时,形参msgid输入的不再是消息id,而是一个数组索引,成功时,返回与数组索引相对应的实际的msgid。  出错时返回-1并设置errno

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值