System V IPC & POSIX IPC(一):消息队列

System V IPC & POSIX IPC(一):消息队列


消息队列允许进程之间以消息的形式交换数据,是一种常见的进程之间的通信机制。

1. 消息队列的创建

System V IPC:

int msgget(key_t key, int msgflg);
返回值:message queue identifier on success, or -1 error
参数key:key一般是通过ftok()返回的一个键或者IPC_PRIVATE,IPC_PRIVATE产生的唯一的key在fork调用的父子进程之间比较有用。
参数msgflg:msgflg可取的值有IPC_CREAT、IPC_EXCL,分别表示新建队列、如果同时指定了IPC_CREAT并且指定的key对应的队列已经存在,那么调用就会失败。

POSIX IPC:

mqd_t mq_open(const char *name, int oflag,
                         /* mode_t mode, struct mq_attr attr/);
返回值:message queue descriptor on success, or (mqd_t)-1 on error
参数name:name参数标示出了消息队列,一个可以移植的name命名方式是使用以斜线打头后面跟着一个或多个非斜线字符的名字,如:/mq-msg。
参数oflag:它控制着mq_open()操作的各个方面,可以包含的值O_CREAT、O_EXCL(跟前面msgget中介绍的同样的意义)、O_RDONLY、O_WRONLY、O_RDWR(控制着进程在消息队列上面的访问方式,跟文件打开系统调用open同样的取值和意义)、O_NONBLOCK(以非阻塞模式打开)。
参数mode:mode是一个位掩码,指定了新消息队列的权限。跟文件打开系统调用open同样的取值和意义。
参数attr:是一个mq_attr结构,指定了新消息队列的特性,后面再详细介绍。

从上面的创建过程来看POSIX IPC的消息队列跟我们熟知的文件open()调用很相似,在mq_open创建队列的同时可以同时指定队列的权限,但是System V IPC却不具备这种特性,它只能通过另外的一个调用msgctl来指定队列的权限,后面我们再介绍。

2. 发送消息

System V IPC

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
返回值:On failure return -1,otherwise return 0
参数msqid:上文msgget返回的id。
参数msgp:是由程序员自定义的一个结构体指针,通常的格式如下:
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[]; /* message data */
};
结构体的第一个部分包含了消息类型,消息的剩余部分则是由程序员定义的一个结构,其长度和类型可以是任意的。这里的消息类型在后面消息接收部分我们能看到其作用。
参数msgsz:指定了上文mtext结构中包含的字节数。
参数msgflg:是一组位掩码,用于控制msgsnd的操作,目前只定义了IPC_NOWAIT这一个标记,标示执行一个非阻塞发送操作。在上面POSIX的mq_open()调用中看到POSIX消息队列的非阻塞标志是在新建队列的时候确认的。

POSIX IPC

int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned int msg_prio);
返回值:On failure return -1,otherwise return 0。
参数mqdes:上文mq_open()调用返回的描述符。
参数msg_ptr:指向发送消息缓冲区的指针,类似msgsnd()中msgp结构体的mtext。
参数msg_len:msg_ptr缓冲区的长度。
参数msg_prio:消息的优先级,类似msgsnd()中msgp结构体的mtype,详细的作用见下面的消息接收部分。

System V IPC和POSIX IPC中关于消息的发送最大的不同可能就是消息的类型和优先级之间的区别,这个留待后面来详细区分。

3. 接收消息

System V IPC

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
返回值:成功读取写入msgp->mtext结构中的字节数。
参数msqid:上文msgget返回的id。
参数msgp:同msgsnd中的msgbuf结构体。
参数msgsz:msgp->mtext结构的最大可用空间,如果队列中待删除的消息体大小超过了msgsz字节,那么就不会从队列中删除消息,msgrcv返回E2BIG错误码。
参数msgtyp:读取消息的顺序无需与消息被发送的一致。可以根据mtype字段的值来选择消息,而这个选择过程是由msgtyp参数来控制的。msgtyp可选的值有三种:
    a. 如果msgtyp等于0,那么删除队列中的第一条消息并将其返回给调用进程。
    b. 如果msgtyp大于0,那么将队列中第一条mtype等于msgtyp的消息删除并将其返回给调用进程。通过指定不同的msgtyp,多个进程能够从同一个消息队列中读取消息而不会出现竞争读取同一条消息的情况,我们也可以越过数据进行读取,而无需顺序读取。
    c. 如果msgtyp小于0,那么就会将等待消息当成优先队列来处理。队列中mtype最小并且其值小于等于msgtyp的绝对值的第一条消息会被删除并返回给调用进程。
参数msgflg:位掩码,用来控制msgrcv的操作,可选的值有IPC_NOWAIT、MSG_EXCEPT、MSG_NOERROR。

POSIX IPC

ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned int *msg_prio);
返回值:成功读取的字节数。
参数mqdes:前面mq_open()返回的句柄。
参数msg_ptr:指向接收消息的缓冲区。
参数msg_len:接收缓冲区msg_ptr的可用字节数。
参数msg_prio:POSIX消息队列中的每一个消息都有一个非负整数表示的优先级,在发送消息的时候通过msg_prio参数指定。消息在队列中是按照优先级倒序排序的(即0表示优先级最低)。当一条消息被添加到队列中时,它会放置在队列中具有相同优先级的所有消息之后。在消息接收函数mq_receive()中根据msg_prio优先级从消息队列中删除一条优先级为msg_prio、存在时间最长的消息。

上面是System V IPC和POSIX IPC的两个通用的消息发送接口,两者的接口参数都很类似,需要重点区分的就是消息类型、消息优先级参数。
POSIX IPC的消息发送、接收接口还有另外一个超时的版本,mq_timedsend()和mq_timedreceive()函数和mq_send()、mq_receive()几乎完全一样的,唯一的差别在于如果操作无法立即执行,并且没有设置O_NONBLOCK标记,那么在指定的超时达到后函数就会超时返回。
POSIX消息队列和System V消息队列的另外一个重要区别就是POSIX消息队列能够在接收之前为空的队列上有可用消息的异步通知(即队列从空变成非空)。这个特性允许调用者异步的来接收消息,而无需执行一个阻塞调用或者在非阻塞队列上面定期执行mq_receive()调用了。这个消息通知函数是int mq_notify(mqd_t mqdes, const struct sigevent *sevp)。

4. 消息队列的属性控制

每一个消息队列都具有一系列的属性特征,有些是跟队列相关的,有些是系统全局相关的。
System V IPC

int msgctl(int msqid, int cmd, struct msqid_ds *buf);
返回值:Return 0 on success, or -1 on error
参数msqid:上文msgget返回的id。
参数cmd:cmd参数指定了在队列上执行的操作,可选的值如下
    a. IPC_STAT:将与消息队列相关联的msqid_ds数据结构的副本放到buf指向的缓冲区。
    b. IPC_SET:使用buf指向的缓冲区提供的值更新这个消息队列的msqid_ds结构。
    c. IPC_RMID:立即删除消息队列对象及其关联的msqid_ds结构。
    d. IPC_INFO、MSG_INFO、MSG_STAT:linux系统特有的cmd值,可以用这三个cmd值来获取系统所有的System V消息队列。
参数msqid_ds:消息队列的属性结构体,具体的结构体如下:
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) */
};
上面结构体的每一个属性可以参考上面的注释。

POSIX IPC

int mq_getattr(mqd_t mqdes, struct mq_attr *attr);
int mq_setattr(mqd_t mqdes, const struct mq_attr *newattr, struct mq_attr *oldattr);
返回值:Return 0 on success, or -1 on error
参数mqdes:前面mq_open()返回的句柄。
参数attr:消息队列的属性结构体,具体如下:
struct mq_attr {
    long mq_flags; /* Flags: 0 or O_NONBLOCK */
    long mq_maxmsg; /* Max. # of messages on queue */
    long mq_msgsize; /* Max. message size (bytes) */
     long mq_curmsgs; /* # of messages currently in queue */
};

你可能感兴趣的:(Linux,c)