重新审视进程间的通信(二)

在UNIX网络编程中有关消息队列的内容分两章,一章Posix标准,一章System V(包括之后的内容都是)。

先看System V。


struct msqid_ds
{
  struct ipc_perm msg_perm;	/* structure describing operation permission */
  __time_t msg_stime;		/* time of last msgsnd command */
#ifndef __x86_64__
  unsigned long int __glibc_reserved1;
#endif
  __time_t msg_rtime;		/* time of last msgrcv command */
#ifndef __x86_64__
  unsigned long int __glibc_reserved2;
#endif
  __time_t msg_ctime;		/* time of last change */
#ifndef __x86_64__
  unsigned long int __glibc_reserved3;
#endif
  __syscall_ulong_t __msg_cbytes; /* current number of bytes on queue */
  msgqnum_t msg_qnum;		/* number of messages currently on queue */
  msglen_t msg_qbytes;		/* max number of bytes allowed on queue */
  __pid_t msg_lspid;		/* pid of last msgsnd() */
  __pid_t msg_lrpid;		/* pid of last msgrcv() */
  __syscall_ulong_t __glibc_reserved4;
  __syscall_ulong_t __glibc_reserved5;
};

Unix98不要求有msg_first、msg_last、msg_cbytes成员(我也没找到)msg_first、msg_last两个指针指向内核,对于应用基本没用。


创建或者访问一个队列

#include <sys/msg.h>

int msgget(key_t key, int oflag);

返回值是一个整数标识符,代指该队列

key可以使ftok()的返回值或者常数IPC_PRIVATE

oflag读写权限组合值,还可与IPC_CREATE(key不存在创建,存在则引用)和IPC_CREAT | IPC_EXCL(key不存在创建,存在则出错)进行异或

当创建时,msg_perm中的uid和cuid成员被设置为当前进程有效用户ID,gid和cgid被设置当前进程的有效组ID


放置消息

#include <sys/msg.h>

int msgsnd(int msqid, const void *ptr, size_t length, int flag);

mspid是msgget的返回值


ptr是一个结构指针指向msgbuf,该结构没有定式,但是有一定要求

struct msgbug {
    long mtype;
    char data[1];
}

该结构必须以一个长整型变量(表示类型)开始和数据,其他的自己看着办。

length为自定义的数据大小。

flag 为0(如果没有新消息的可用空间则阻塞)或者IPC_NOWAIT(如果数据放不下则返回错误)


读消息

ssize_t msgrcv(int msgid, void *ptr, size_t length, long type, int flag);

ptr指向一个结构踢和msgsnd一样,length为缓冲区中数据大小(最多能返回的数据量)


type给定读出什么样的消息

为0,返回第一个消息(最早的消息)

大于0,返回类型值为type的第一个消息

小于0,返回类型值小于或等于type绝对值的消息中类型值最小的那个消息。

flag:IPC_NOWAIT如果没有所请求类型的消息则返回错误,否则阻塞

MSG_NOERROR如果数据长度大于length则截取不返回错误,否则返回E2BIG错误


控制操作

int msgctl(int msgid, int cmd, struct msqid_ds *buff);
IPC_RMID:删除指定队列

IPC_SET:给指定队列设置msqid_ds结构中msg_perm.uid、msg_perm.gid、msg_perm.mode和msg_qbytes

IPC_STAT:给调用者返回指定消息队列的msqid_ds


再看Posix消息队列


每个消息有如下属性:

1:一个无符号整数优先级(Posix)或长整数类型(System V)

2:数据部分长度

3:数据本身


创建新消息队列或打开一个队列

#include <mqueue.h>

mqd_t mq_open(const char *name, int oflag, .../*mode_t mode, struct attr *attr*/);
oflag标志位

O_RDONLY(只读) O_WRONLY(只写) O_RDWR(可读可写)可与以下异或

O_CREAT(创建) O_EXCL (当消息已存在时,返回EEXIST错误到errno中)O_NONBLOCK(设置非阻塞)

mode参数

S_IRUSR用户(属主)读

S_IWUSR用户(属主)读

S_IRGRP组成员读

S_IWGRP组成员写

S_IROTH其他用户读

S_IWOTH其他用户写

attr用于给新队列指定某些属性,NULL为默认属性

返回值为消息队列描述符

关闭

int mq_close(mqd_t mqdes);
很简单,当一个进程终止它所打开的消息队列都关闭,就像调用mq_close一样


int mq_unlink(const char *name);
每个消息队列都有一个保存当前打开着描述符数的引用计数器,当一个消息队列的引用计数大于0就可以用此函数删除name,该队列析构要到最后一个mq_close

消息队列属性

struct mq_attr

{

long int mq_flags;    /* Message queue flags0O_NONBLOCK */

long int mq_maxmsg;   /* Maximum number of messages.  */

long int mq_msgsize;  /* Maximum message size.  */

long int mq_curmsgs;  /* Number of messages currently queued.  */

long int __pad[4];

};

int mq_getattr(mqd_t mqdes, struct mq_attr *attr);
int mq_setattr(mqd_t mqdes, const struct mq_attr *attr, struct mq_attr *oattr);

mq_setattr函数只使用attr指向的结构的mq_flags成员,对oattr指向的结构进行设置

如果oattr非空,则队列的先前属性和当前状态都返回到该数组中。


int mq_send(mqd_t mqdes, char *ptr, size_t len, unsigned int prio);
ssize_t mq_receiver(mqd_t mqdes, char *ptr, size_t len, unsigned int *priop);

发送和接收函数

mq_receive中的len不能小于能加到所指队列中的消息的最大大小(mq_msgsize成员)

mq_send函数中prio是待发送消息的优先级,其值必须小于MQ_PRIO_MAX

如果mq_recceive中的priop非空则用来保存返回消息的优先级。

Posix消息队列允许异步事件通知(asynchronous event notification),以告知何时有一个消息放置到了某个队列中。

int mq_notify(mqd_t mqdes, const struct sigevent *notification);

该函数通知进程何时可以接受一条消息

sigevent结构体

struct sigevent {
    int           sigev_notify;            //Notification type. 
    int           sigev_signo;            //Signal number. 
    union sigval  sigev_value;             //Signal value. 
    void         (*sigev_notify_function)(union sigval); //Notification function. 
    pthread_attr_t *sigev_notify_attributes;  //Notification attributes. 
};
 sigev_notify 的取值:

SIGEV_NONE 事件发生时,什么也不做. SIGEV_SIGNAL 事件发生时,将sigev_signo 指定的信号(A queued signal)发送给指定的进程. SIGEV_THREAD 事件发生时,内核会(在此进程内)以sigev_notification_attributes为线程属性创建一个线程,并且让它执行sigev_notify_function 传入sigev_value作为为一个参数.
union sigval
{
    int sival_int;
    void *sival_ptr;
};
mq_notify函数有一下适用规则

(1)如果notification参数非空,那么当前进程希望在有一个消息达到所指定的先前为空的队列时得到通知

(2)如果notification参数为空,而且当前进程目前被注册为接收所指定队列的通知,那么已存在的注册将被撤销

(3)任意时刻只有一个进程可以被注册为接收某个给定队列的通知

(4)当有一个消息到达某个先前为空的队列,而且已有一个进程被注册为接收该队列的通知时,只有在没有任何线程阻塞在该队列的mq_receive调用中的前提下,通知才会发出。mq_receive调用比任何通知的注册都优先。

(5)当该通知被发送给它的注册过程时,其注册即被注销。必须再次调用mq_notify以重新注册。


Posix消息队列和System V消息队列差别

(1)Posix消息队列总是返回优先级最高的最早消息,System V消息队列可以返回指定的任意消息;

(2)当往空队列中添加消息时,Posix消息队列允许产生一个信号或者启动一个线程。


PSSSSSSSSSSSS:为啥子CSDN博客总是无法正常显示呢,好尴尬。。。。修改几次了





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章