《UNIX网络编程:卷2》P108-P110:图6-4、6-5、6-6、6-7
----------------------------------------------------------------------
创建一个消息队列。
把必须由用户作为命令行参数提供的路径名作为参数传递给ftok。导出的键由msgget转换成一个标识符。
/* * msgcreate.c * P108 图6-4 创建一个System V消息队列 */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <errno.h> #include <sys/msg.h> #include <sys/ipc.h> #define MSG_R 0400 #define MSG_W 0200 #define SVMSG_MODE (MSG_R | MSG_W | MSG_R>>3 | MSG_R>>6) int main(int argc, char *argv[]) { int c, oflag; key_t key; oflag = SVMSG_MODE | IPC_CREAT; while ((c = getopt(argc, argv, "e")) != -1) { switch(c) { // 使用-e选项指定IPC_EXCL标志 case 'e': oflag |= IPC_EXCL; break; } } if (optind != argc - 1) { fprintf(stderr, "usage: msgcreate [-e] <pathname>\n"); exit(1); } if ((key = ftok(argv[optind], 0)) < 0) { fprintf(stderr, "ftok error: %s\n", strerror(errno)); exit(1); } // 创建一个新的消息队列 if (msgget(key, oflag) < 0) { fprintf(stderr, "msgget error: %s\n", strerror(errno)); exit(1); } exit(0); }
----------------------------------------------------------------------
该程序把一个指定了长度和类型的消息放置到某个队列中。
/* * msgsnd.c * P108 图6-5 往一个System V消息队列中加一个消息 */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <sys/msg.h> #include <sys/ipc.h> #define MSG_R 0400 #define MSG_W 0200 #define SVMSG_MODE (MSG_R | MSG_W | MSG_R>>3 | MSG_R>>6) struct msgbuf { long mtype; // 消息类型 char mtext[1]; // 消息数据 }; int main(int argc, char *argv[]) { int mqid; size_t len; long type; struct msgbuf *ptr; key_t key; if (argc != 4) { fprintf(stderr, "usage: msgsnd <pathname> <#bytes> <type>\n"); exit(1); } len = atoi(argv[2]); type = atoi(argv[3]); if ((key = ftok(argv[1], 0)) < 0) { fprintf(stderr, "ftok error: %s\n", strerror(errno)); exit(1); } // 打开一个消息队列 if ((mqid = msgget(key, MSG_W)) < 0) { fprintf(stderr, "msgget error: %s\n", strerror(errno)); exit(1); } // 分配缓冲区 if ((ptr = calloc(sizeof(long) + len, sizeof(char))) == NULL) { fprintf(stderr, "calloc error: %s\n", strerror(errno)); exit(1); } ptr->mtype = type; // 放置一个消息到队列中 if (msgsnd(mqid, ptr, len, 0) < 0) { fprintf(stderr, "msqid error: %s\n", strerror(errno)); exit(1); } exit(0); }
----------------------------------------------------------------------
该程序从队列中读出一个消息。-n命令行选项指定非阻塞,-t命令行选项指定msgrcv函数的type参数。
/* * msgrcv.c * P109 图6-6 从一个System V消息队列中读出一个消息 */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <errno.h> #include <sys/msg.h> #include <sys/ipc.h> #define MSG_R 0400 #define MSG_W 0200 #define SVMSG_MODE (MSG_R | MSG_W | MSG_R>>3 | MSG_R>>6) #define MAXMSG (8192 + sizeof(long)) struct msgbuf { long mtype; // 消息类型 char mtext[1]; // 消息数据 }; int main(int argc, char *argv[]) { int c, flag, mqid; long type; ssize_t n; struct msgbuf *buff; key_t key; type = flag =0; while ((c = getopt(argc, argv, "nt:")) != -1) { switch(c) { case 'n': // 非阻塞标志 flag |= IPC_NOWAIT; break; case 't': // 消息类型 type = atol(optarg); break; } } if (optind != argc - 1) { fprintf(stderr, "usage: msgrcv [-n] [-t type] <pathname>\n"); exit(1); } if ((key = ftok(argv[optind], 0)) < 0) { fprintf(stderr, "ftok error: %s\n", strerror(errno)); exit(1); } // 打开一个消息队列 if ((mqid = msgget(key, MSG_R)) < 0) { fprintf(stderr, "msgget error: %s\n", strerror(errno)); exit(1); } // 分配缓冲区 if ((buff = malloc(MAXMSG)) == NULL) { fprintf(stderr, "malloc error: %s\n", strerror(errno)); exit(1); } // 从队列中读出一个消息 if ((n = msgrcv(mqid, buff, MAXMSG, type, flag)) < 0) { fprintf(stderr, "msgrcv error: %s\n", strerror(errno)); exit(1); } printf("read %ld types, type = %ld\n", n, buff->mtype); exit(0); }
----------------------------------------------------------------------
删除一个消息队列。
/* * msgrmid.c * P109 图6-7 删除一个System V消息队列 */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <errno.h> #include <sys/msg.h> #include <sys/ipc.h> int main(int argc, char *argv[]) { int mqid; key_t key; if (argc != 2) { fprintf(stderr, "usage: msgrmid <pathname>\n"); exit(1); } if ((key = ftok(argv[1], 0)) < 0) { fprintf(stderr, "ftok error: %s\n", strerror(errno)); exit(1); } // 打开一个消息队列 if ((mqid = msgget(key, 0)) < 0) { fprintf(stderr, "msgget error: %s\n", strerror(errno)); exit(1); } if (msgctl(mqid, IPC_RMID, NULL) < 0) { fprintf(stderr, "msgctl error: %s\n", strerror(errno)); exit(1); } exit(0); }
----------------------------------------------------------------------
msgcreate: gcc msgcreate.c -o msgcreate -Wall msgsnd: gcc msgsnd.c -o msgsnd -Wall msgrcv: gcc msgrcv.c -o msgrcv -Wall msgrmid: gcc msgrmid.c -o msgrmid -Wall all: make msgcreate msgsnd msgrcv msgrmid clean: rm msgcreate msgsnd msgrcv msgrmid
----------------------------------------------------------------------
$ make all make msgcreate msgsnd msgrcv msgrmid make[1]: 正在进入目录 `/home/user/workspace/unp2/chap06' gcc msgcreate.c -o msgcreate -Wall gcc msgsnd.c -o msgsnd -Wall gcc msgrcv.c -o msgrcv -Wall gcc msgrmid.c -o msgrmid -Wall make[1]:正在离开目录 `/home/user/workspace/unp2/chap06'
----------------------------------------------------------------------
现在使用刚刚给出的四个程序。首先创建一个消息队列并往其中写入三个消息。
$ ./msgcreate /tmp/no/such/file 使用不存在的路径创建消息队列 ftok error: No such file or directory $ touch /tmp/test1 新建一个文件 $ ./msgcreate /tmp/test1 使用已存在的文件创建一个消息队列 $ ./msgsnd /tmp/test1 1 100 写入消息 $ ./msgsnd /tmp/test1 2 200 $ ./msgsnd /tmp/test1 3 300 $ ipcs -q 查看消息队列信息 --------- 消息队列 ----------- 键 msqid 拥有者 权限 已用字节数 消息 0x0008fe95 65536 user 644 6 3
现在展示不以FIFO顺序读出消息时msgrcv的type参数的使用。
$ ./msgrcv -t 200 /tmp/test1 请求读出类型为200的消息 read 2 types, type = 200 $ ./msgrcv -t -300 /tmp/test1 读出类型小于或等于300且是最小的消息 read 1 types, type = 100 $ ./msgrcv /tmp/test1 请求读出该队列中的第一个消息 read 3 types, type = 300 $ ./msgrcv -n /tmp/test1 无阻塞读取消息 msgrcv error: No message of desired type
测试读取不存在的消息
$ ./msgsnd /tmp/test1 1 100 放置一个长度为1、类型为100的消息 $ ./msgrcv -t 999 /tmp/test1 读取不存在的消息类型 ^C Ctrl+C 终止程序 $ ./msgrcv -n -t 999 /tmp/test1 使用无阻塞读取不存在的消息类型 msgrcv error: No message of desired type $ ./msgrmid /tmp/test1 删除消息队列