系统内核维护了一个存访消息的队列,不同用户可以向队列中发送消息,或者从队列中接收消息。
#include <sys/types.h>
#include sys/ipc.h>
#include <sys/msg.h>
key_t ftok(const char *pathname, int proj_id);
int msgget(key_t key, int msgflg);
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
可以用ipcs -q命令查看消息队列信息,用ipcrm -q msqid命令删除消息队列。
两个进程,一个进程创建消息队列,写消息,另一个进程读消息队列中的信息。
流程如下图:
代码演示:
msgsnd.c
#include "./common/head.h" /*功能: *创建一个消息队列,并写两条信息。 */ #define MSGLEN 20 //消息队列结构体,将类型和消息填入该结构体,调用msgsnd函数即可。 typedef struct msgbuf{ long mtype; //必须大于0 char mtext[MSGLEN]; //存放消息队列内容 }MSG; int main() { key_t key = ftok("./exec.c", 9); //第一个参数是文件名(任意路径),第二个参数是项目编号,随便传。ftok函数会根据这两个参数,用一个算法生成key值,只要传入的这两个参数是一样的,必定会得到相同的key值。在读取进程中,也是利用这一特性,保证读写操作的是同一个消息队列 printf("key = %#x\n", key); int msgqid = msgget(key, IPC_CREAT | 0666); //根据key值得到消息队列的id printf("msgqid = %d\n", msgqid); MSG msg; msg.mtype = 1; //类型 strncpy(msg.mtext, "online", MGSLEN); //最多拷贝MSGLEN个字节,不用担心越界 msgsnd(msgqid, &msg, MSGLEN, 0); //最后一个参数是权限,默认传0即可。 msg.mtype = 2; strncpy(msg.mtext, "offline", MGSLEN); msgsnd(msgqid, &msg, MSGLEN, 0); //运行到这里,将两条消息都通过消息队列发送了。等待接收端按照类型接收 return 0; }
$ gcc msgsnd.c -o msgsnd
$ ./msgsnd
$ ipcs -q
可以看到,目前已经有2条消息在消息队列中了。
msgrcv.c
#include "./common/head.h" /*功能: *接收消息队列中的两条消息。 */ #define MSGLEN 20 //消息队列结构体,调用msgrcv函数即可解析出消息队列的类型和内容 typedef struct msgbuf{ long mtype; char mtext[MSGLEN]; }MSG; int main() { key_t key = ftok("./exec.c", 9); //参数必须和发送函数中的一样,才能生成相同的key值 printf("key = %#x\n", key); int msgqid = msgget(key, IPC_CREAT | 0666); printf("msgqid = %d\n", msgqid); MSG msg; msgrcv(msgqid, &msg, MSGLEN, 2, 0); //第4个参数为消息队列类型,读取类型为2的消息队列 printf("mtype = %ld\nmtext = %s\n", msg.mtype, msg.mtext); //将消息队列的类型和消息打印在终端 msgrcv(msgqid, &msg, MSGLEN, 1, 0); //读取类型为1的消息队列 printf("mtype = %ld\nmtext = %s\n", msg.mtype, msg.mtext); //打印在终端 return 0; }
$ gcc msgrcv.c -o msgrcv
$ ./msgrcv
$ ipcs -q
可以看到,接收进程正确收到了消息队列中的信息,并按照类型打印出来了。读取过后,消息队列中的消息数变为0。
当读消息队列进程读到没有发送的类型时,会阻塞在这里,等待发送进程发送该类型的信息。