被问到“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);
#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);
#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);
// 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);
#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;
}
#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;
}
#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
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
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
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
------ 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
# 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