【linux高级程序设计】(第十一章)System V进程间通信 2
消息队列
消息队列是消息的链式队列,模型如下:
包括两种数据结构:
msqid_ds消息队列数据结构
msg消息队列数据结构
struct msg_msg{ struct list_head m_list; long m_type; //消息类型 int m_ts; //消息大小 struct msg_msgseg* next; //下一个消息位置 void *security; //真正消息位置 };
在/usr/include/linux/msg.h文件中定义了队列大小的限制。不同的Linux版本值不同
#define MSGMNI 16 //最大消息队列个数 #define MSGMAX 8192 //消息队列中每个消息最大为8192字节 #define MSGMNB 16384 //每个消息队列最大为16384字节
int msgget (key_t __key, int __msgflg):创建消息队列
第一个参数:由ftok创建的key值
第二个参数:低位确定消息队列的访问权限,最终值为perm&~umask. ??不懂
高位包含 #define IPC_CREAT 00001000 //如果key不存在,则创建,存在返回ID
#define IPC_EXCL 00002000 //如果key存在,返回失败
#define IPC_NOWAIT 00004000 //如果需要等待,直接返回错误
int msgctl (int __msqid, int __cmd, struct msqid_ds *__buf) :控制消息队列属性
第一个参数:消息队列标识符,即msgget的返回值
第二个参数:执行的控制命令,包括
- IPC_STAT 2:读取消息队列属性。取得队列的msqid_ds结构,放入第三个参数
- IPC_SET 1:设置消息队列属性。只有有效用户ID等于msg_perm.cuid或msg_perm.uid的进程或者超级用户特权进程可以设置。只有超级用户可以增加msg_qbytes。
- IPC_RMID: 删除消息队列。立即生效。仍使用这一消息队列的其他进程在下一次试图对此队列操作时,会出错返回EIDRM。同样只有两种用户可以使用该命令。
- IPC_INFO: 读取消息队列基本情况
第三个参数:存储读取或需要修改的消息队列的属性
int msgsnd (int __msqid, __const void * __msgp, size_t __msgsz, int __msgflg) :发送信息到消息队列,成功返回0,否则-1.同时msg_qnum递增1,msg_lspid设置为调用进程的进程ID,msg_stime设置为当前时间。
第一个参数:指定的消息队列标识符
第二个参数:指向用户定义缓冲区
struct msgbuf{ long mtype; //消息类型 char mtext[1]; //消息内容,在使用时自己重新定义此结构 };
第三个参数:接收信息的大小
第四个参数:指定达到系统限制时采取的操作。设置为IPC_NOWAIT,在需要等待时立即返回错误EAGAIN;设置为0,阻塞调用进程
int msgrcv (int __msqid, void *__msgp, size_t __msgsz, long int __msgtyp, int __msgflg) :从队列中取消息
具体说明太长了,上图:
例子,发送接收消息,读取限制信息
#include<stdio.h> #include<unistd.h> #include<stdlib.h> #include<sys/types.h> #include<sys/ipc.h> #include<string.h> #include<sys/msg.h> #define BUFSIZE 128 struct msg_buf //自定义消息结构体 { long type; char msg[BUFSIZE]; }; int main(int argc, char *argv[]) { key_t key; int msgid; struct msg_buf msg_snd, msg_rcv; struct msginfo buf; char *ptr = "helloworld"; memset(&msg_snd, '\0', sizeof(struct msg_buf)); memset(&msg_rcv, '\0', sizeof(struct msg_buf)); msg_rcv.type = 1; msg_snd.type = 1; memcpy(msg_snd.msg, ptr, strlen(ptr)); if((key = ftok(".",'A')) == -1) { perror("ftok"); exit(EXIT_FAILURE); } if((msgid=msgget(key,0600|IPC_CREAT)) == -1) { perror("msgget"); exit(EXIT_FAILURE); } printf("msgsnd_return = %d\n", msgsnd(msgid,(void *)&msg_snd, strlen(msg_snd.msg), 0)); msgctl(msgid, MSG_INFO, &buf); printf("buf.msgmax=%d\n", buf.msgmax); printf("buf.msgmnb=%d\n", buf.msgmnb); printf("buf.msgpool=%d\n", buf.msgpool); printf("buf.semmap=%d\n", buf.msgmap); printf("buf.msgmni=%d\n", buf.msgmni); printf("buf.msgssz=%d\n", buf.msgssz); printf("buf.msgtql=%d\n", buf.msgtql); printf("buf.msgseg=%d\n", buf.msgseg); printf("msgrcv_return = %d\n", msgrcv(msgid, (void *)&msg_rcv, BUFSIZE,msg_rcv.type,0)); printf("rev msg:%s\n", msg_rcv.msg); printf("msgctl_return=%d\n",msgctl(msgid,IPC_RMID,0)); }
下面是照书上抄的消息队列实现实时通信的例子
在我的电脑上无法接收消息????
send端
#include<stdio.h> #include<unistd.h> #include<stdlib.h> #include<sys/types.h> #include<sys/ipc.h> #include<string.h> #include<sys/msg.h> struct msgbuf{ int type; char ptr[1024]; }; int main(int argc, char *argv[]) { key_t key; key = ftok(argv[1], 100); int msgid; msgid = msgget(key, IPC_CREAT|0600); if(msgid == -1) { perror("msgget"); exit(EXIT_FAILURE); } printf("key = %d, msgid = %d\n", key, msgid); pid_t pid; pid = fork(); if(pid == 0) { struct msgbuf data; //书上是在while循环里每次malloc,后面free 但是我那样写发现每次msgsnd都失败,说identitier removed while(1) { printf("pls input msg to send:"); char buf[128]; fgets(buf, 128, stdin); data.type = 1; memcpy(data.ptr, buf, strlen(buf) + 1); if(msgsnd(msgid, (void *)&data, strlen(buf) + 1, 0) == -1) { perror("msgsnd"); exit(EXIT_FAILURE); } sleep(1); } } else { struct msgbuf mybuf; while(1) { memset(&mybuf, '\0', sizeof(mybuf)); if(msgrcv(msgid, &mybuf, 1024, 2, 0) == -1) { perror("msgrcv"); exit(EXIT_FAILURE); } printf("recv msg:%s\n", mybuf.ptr); } } }
receive端代码类似,就是发送和接收的信号类型不一样。