System V消息队列是传统的Linux消息队列机制,它使用一组系统调用来创建、发送和接收消息。它的特点是可以在不同进程之间共享消息队列,但是在使用时需要手动管理消息队列的创建和删除。
优点:
缺点:
ftok函数用于生成一个System V IPC对象(如消息队列、共享内存等)的key。它将pathname和proj_id组合起来,生成一个唯一的key,用于标识一个System V IPC对象。
key_t ftok(const char *pathname, int proj_id);
msgget函数用于创建一个新的消息队列或者获取一个已存在的消息队列。它接受一个key和一些标志作为参数。
int msgget(key_t key, int msgflg);
msgsnd函数用于向指定的消息队列中发送消息。它接受消息队列的标识符、消息缓冲区的指针、消息的大小和发送标志作为参数。
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
msgrcv函数用于从指定的消息队列中接收消息。它接受消息队列的标识符、消息缓冲区的指针、消息的大小、消息的类型和接收标志作为参数。
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
msgctl函数用于控制消息队列,可以用来删除消息队列、获取消息队列的状态信息等。它接受消息队列的标识符、控制命令和消息队列数据结构的指针作为参数,根据控制命令的不同执行相应的操作。
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
其中cmd取值如下:
其中struct msqid_ds 定义如下:
struct msqid_ds {
struct ipc_perm msg_perm; /* 消息队列的权限信息 */
time_t msg_stime; /* 上次发送消息的时间 */
time_t msg_rtime; /* 上次接收消息的时间 */
time_t msg_ctime; /* 上次变更状态的时间 */
unsigned long __msg_cbytes; /* 队列中的字节数 */
msgqnum_t msg_qnum; /* 队列中的消息数 */
msglen_t msg_qbytes; /* 队列的最大字节数 */
pid_t msg_lspid; /* 最后发送消息的进程ID */
pid_t msg_lrpid; /* 最后接收消息的进程ID */
};
IPC_CREAT
IPC_EXCL
IPC_NOWAIT
IPC_PRIVATE
ipcs命令用于显示系统中的IPC资源信息,包括消息队列、共享内存和信号量。-q选项表示只显示消息队列的信息:
ipcs -q
ipcrm -Q
ipcrm -Q 12345
测试代码如下:
#include
#include
#include
#include
#include
#include
#define MSG_SIZE 128
#define MSG_FILE_PATH "/home/msgq"
struct msg_buffer {
long msg_type;
char msg_text[MSG_SIZE];
};
int main(int argc, char *argv[])
{
int msqid;
struct msg_buffer message = {0};
key_t key;
// 文件不存在则创建文件
if (-1 == access(MSG_FILE_PATH, F_OK))
{
system("touch "MSG_FILE_PATH);
}
// 获取key
if((key = ftok(MSG_FILE_PATH, 'a')) < 0)
{
return 0;
}
// 命令行参数
// 第一个参数 0表示发送 1表示接收 2表示删除
// 第二个参数 0表示测试一次 1表示while循环测试
if (argc != 3)
{
printf("Usage: %s 0|1|2", argv[0]);
return 0;
}
if (!strcmp(argv[1], "0"))
{
do
{
// 发送消息
msqid = msgget(key, 0666 | IPC_CREAT);
message.msg_type = 1;
strcpy(message.msg_text, "Hello, this is a message.");
msgsnd(msqid, &message, sizeof(message.msg_text), 0);
printf("^^^ send ^^^\r\n msqid = %d\r\n type:%ld \r\n data:%s\n", msqid, message.msg_type, message.msg_text);
sleep(3);
}
while(atoi(argv[2]));
}
else if (!strcmp(argv[1], "1"))
{
// 接收消息
do
{
msqid = msgget(key, 0666);
msgrcv(msqid, &message, sizeof(message.msg_text), 1, 0);
printf("^^^ received ^^^\r\n msqid = %d\r\n type:%ld \r\n data:%s\n", msqid, message.msg_type, message.msg_text);
}
while(atoi(argv[2]));
}
else if (!strcmp(argv[1], "2"))
{
// 删除消息
msqid = msgget(key, 0666);
msgctl(msqid, IPC_RMID, NULL);
printf("^^^ delete ok ^^^\r\n");
}
else
{
printf("Invalid command\n");
return 1;
}
return 0;
}
根据执行程序命令行参数不同执行不同操作,第一个参数0表示发送,1表示读取,2表示删除,第二个参数0表示不用while循环,1表示启用循环,首先执行两次不启用while的发送操作:
再执行两次接收操作可以查看到消息已经被接收完毕:
执行删除操作可以看到消息队列被删除掉:
开启两个终端,启用while循环开启进程间通信的测试,可以看到发送端每3秒发送数据,接收端接收正常:
本文阐述了进程间通信之消息队列(System V)的定义,列举了编程中使用的接口和linux命令,编写了测试用例测试相关功能。