使用 System V message queue 实现进程间通信

被问到“Linux上用什么命令删除一个MQ”. 为了验证这个问题需要先整出来一个MQ,然后删除它。

相关系统调用

// ftok(): convert a pathname and a project identifier to a System V IPC key.
 #include <sys/types.h>
 #include <sys/ipc.h>
 
 key_t ftok(const char *pathname, int proj_id);
// msgget(): either returns the message queue identifier for a newly created message queue or returns the identifiers for a queue which exists with the same key value.
 #include <sys/types.h>
 #include <sys/ipc.h>
 #include <sys/msg.h>
 
 int msgget(key_t key, int msgflg);
// msgsnd(): Data is placed on to a message queue by calling msgsnd().
 // msgrcv(): messages are retrieved from a queue.
 #include <sys/types.h>
 #include <sys/ipc.h>
 #include <sys/msg.h>
 
 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);
// msgctl(): It performs various operations on a queue. Generally it is use to destroy message queue.
 #include <sys/types.h>
 #include <sys/ipc.h>
 #include <sys/msg.h>
 
 int msgctl(int msqid, int cmd, struct msqid_ds *buf);

Writer - 往MQ里写入消息

代码是从网上抄的[1],挺好用。

// C Program for Message Queue (Writer Process)
 #include <stdio.h>
 #include <sys/ipc.h>
 #include <sys/msg.h>
  
 // structure for message queue
 struct mesg_buffer {
     long mesg_type;
     char mesg_text[100];
 } message;
  
 int main()
 {
     key_t key;
     int msgid;
  
     // ftok to generate unique key
     // 每个程序都可以用相同的 pathname 和 proj_id 来获取相同的 key.
     key = ftok("progfile", 65);
  
     // msgget creates a message queue
     // and returns identifier
     msgid = msgget(key, 0666 | IPC_CREAT);
     message.mesg_type = 1;
  
     // 获取用户输入
     printf("Write Data : ");
     gets(message.mesg_text);
  
     // msgsnd to send message
     msgsnd(msgid, &message, sizeof(message), 0);
  
     // display the message
     printf("Data send is : %s \n", message.mesg_text);
  
     return 0;
 }

Reader - 从MQ中读取消息

// C Program for Message Queue (Reader Process)
 #include <stdio.h>
 #include <sys/ipc.h>
 #include <sys/msg.h>
  
 // structure for message queue
 struct mesg_buffer {
     long mesg_type;
     char mesg_text[100];
 } message;
  
 int main()
 {
     key_t key;
     int msgid;
  
     // ftok to generate unique key
     // 使用跟 writer 相同的 pathname 和 proj_id 来获取 key.
     key = ftok("progfile", 65);
  
     // msgget creates a message queue
     // and returns identifier
     msgid = msgget(key, 0666 | IPC_CREAT);
  
     // msgrcv to receive message
     msgrcv(msgid, &message, sizeof(message), 1, 0);
  
     // display the message
     printf("Data Received is : %s \n", 
                     message.mesg_text);
  
     // to destroy the message queue
     msgctl(msgid, IPC_RMID, NULL);
  
     return 0;
 }

测试验证

运行 writer 往 mq 写信息,reader 可以接收到这个信息。

# echo "test message" | ./writer 
 Write Data : Data send is : test message 
 # ./reader 
 Data Received is : test message

从 writer 的 strace 可以看到,它调用了 msgget() 和 msgsnd() 两个 system call.

stat("progfile", 0x7ffe6e4f1560)        = -1 ENOENT (No such file or directory)
 msgget(0xffffffff, IPC_CREAT|0666)      = 32768
 msgsnd(32768, {1, "haha\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"...}, 112, 0) = 0

从 reader 的 strace 可以看到,它调用了 msgget() 和 msgrcv() 来获取数据,调用 msgctl() 来删除这个 queue.

stat("progfile", 0x7fff6b97ea70)        = -1 ENOENT (No such file or directory)
 msgget(0xffffffff, IPC_CREAT|0666)      = 98304
 msgrcv(98304, {1, "haha\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"...}, 112, 1, 0) = 112
 msgctl(98304, IPC_RMID, NULL)           = 0

删除一个MQ

好了,终于可以尝试手动删除MQ了。从上面可以知道,如果没有任何一个程序调用 msgctl() 来删除一个MQ,而所有用这个MQ的程序都退出了,这个MQ的记录就一直存在于内存里了。

# ipcs 
 
 ------ Message Queues --------
 key        msqid      owner      perms      used-bytes   messages    
 0xffffffff 0          root       666        112          1           
 
 ------ Shared Memory Segments --------
 key        shmid      owner      perms      bytes      nattch     status      
 
 ------ Semaphore Arrays --------
 key        semid      owner      perms      nsems

用 ipcrm -q 命令可以删除这个 queue.

# ipcrm -q 0

从 strace 中也能看到,它其实也是调用 msgctl().

msgctl(163840, IPC_RMID, NULL)          = 0

MQ属于什么内存类型?

看到我写这文章,同事补了个问题,这些 Message Queue 算是什么类型的内存? 匿名页还是共享页?

我觉得是共享页,先挖坑,后面再举证。

参考

[1] https://www.geeksforgeeks.org/ipc-using-message-queues/

[2] https://stackoverflow.com/questions/3056307/how-do-i-use-mqueue-in-a-c-program-on-a-linux-based-system

[3] Chapter 15, Advanced Programming in the UNIX Environment 3rd Edition

我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章