10. msgctl - 消息队列控制 链接到标题
函数介绍 链接到标题
msgctl
系统调用用于控制System V消息队列,可以获取和设置消息队列的各种属性和状态。它是消息队列管理的重要工具,用于查询队列信息、修改权限、删除队列等。
函数原型 链接到标题
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
功能 链接到标题
对消息队列执行各种控制操作,包括获取状态、设置属性、删除队列等。
参数 链接到标题
int msqid
: 消息队列的标识符int cmd
: 要执行的控制命令IPC_STAT
: 获取消息队列状态信息IPC_SET
: 设置消息队列属性IPC_RMID
: 删除消息队列IPC_INFO
: 获取系统消息队列限制信息
struct msqid_ds *buf
: 指向msqid_ds结构体的指针(根据命令不同而不同)
返回值 链接到标题
- 成功时返回0(IPC_STAT, IPC_SET, IPC_RMID)或正数(IPC_INFO)
- 失败时返回-1,并设置errno
相似函数 链接到标题
msgget()
: 获取消息队列标识符msgsnd()
: 发送消息到消息队列msgrcv()
: 从消息队列接收消息
示例代码 链接到标题
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>
// 消息结构体
struct message {
long msg_type;
char msg_data[100];
};
int main() {
key_t key;
int msgid;
struct msqid_ds queue_info;
struct message msg;
printf("=== Msgctl函数示例 ===\n");
// 创建消息队列
key = ftok(".", 'i');
if (key == -1) {
perror("ftok失败");
exit(EXIT_FAILURE);
}
// 先清理可能存在的同名消息队列
msgid = msgget(key, 0666);
if (msgid != -1) {
msgctl(msgid, IPC_RMID, NULL);
}
// 创建消息队列
msgid = msgget(key, 0666 | IPC_CREAT | IPC_EXCL);
if (msgid == -1) {
perror("创建消息队列失败");
exit(EXIT_FAILURE);
}
printf("成功创建消息队列,标识符: %d\n", msgid);
// 示例1: 获取消息队列状态信息(IPC_STAT)
printf("\n示例1: 获取消息队列状态信息\n");
if (msgctl(msgid, IPC_STAT, &queue_info) == -1) {
perror("获取队列状态失败");
} else {
printf(" 消息队列状态信息:\n");
printf(" 键值: %d\n", queue_info.msg_perm.__key);
printf(" 拥有者UID: %d\n", queue_info.msg_perm.uid);
printf(" 拥有者GID: %d\n", queue_info.msg_perm.gid);
printf(" 创建者UID: %d\n", queue_info.msg_perm.cuid);
printf(" 创建者GID: %d\n", queue_info.msg_perm.cgid);
printf(" 权限: %o\n", queue_info.msg_perm.mode);
printf(" 队列中的消息数: %ld\n", queue_info.msg_qnum);
printf(" 队列中字节数: %ld\n", queue_info.msg_cbytes);
printf(" 队列最大字节数: %ld\n", queue_info.msg_qbytes);
printf(" 最大消息大小: %ld\n", queue_info.msg_mbytes);
printf(" 最后发送时间: %ld\n", queue_info.msg_stime);
printf(" 最后接收时间: %ld\n", queue_info.msg_rtime);
printf(" 最后修改时间: %ld\n", queue_info.msg_ctime);
}
// 示例2: 发送一些消息用于测试
printf("\n示例2: 发送测试消息\n");
for (int i = 1; i <= 5; i++) {
msg.msg_type = i;
sprintf(msg.msg_data, "这是第%d条测试消息", i);
if (msgsnd(msgid, &msg, sizeof(msg.msg_data), 0) == -1) {
perror("发送测试消息失败");
} else {
printf(" 发送消息%d: %s\n", i, msg.msg_data);
}
}
// 再次获取状态信息,查看变化
if (msgctl(msgid, IPC_STAT, &queue_info) == -1) {
perror("再次获取队列状态失败");
} else {
printf(" 发送消息后队列状态:\n");
printf(" 队列中的消息数: %ld\n", queue_info.msg_qnum);
printf(" 队列中字节数: %ld\n", queue_info.msg_cbytes);
}
// 示例3: 修改消息队列权限(IPC_SET)
printf("\n示例3: 修改消息队列权限\n");
// 先保存原始权限
mode_t original_mode = queue_info.msg_perm.mode;
printf(" 原始权限: %o\n", original_mode);
// 修改权限为只读
queue_info.msg_perm.mode = 0444;
if (msgctl(msgid, IPC_SET, &queue_info) == -1) {
perror("修改权限失败");
} else {
printf(" 成功修改权限为只读: %o\n", queue_info.msg_perm.mode);
}
// 验证权限修改效果
struct msqid_ds new_info;
if (msgctl(msgid, IPC_STAT, &new_info) == -1) {
perror("验证权限修改失败");
} else {
printf(" 验证权限: %o\n", new_info.msg_perm.mode);
}
// 恢复原始权限
new_info.msg_perm.mode = original_mode;
if (msgctl(msgid, IPC_SET, &new_info) == -1) {
perror("恢复权限失败");
} else {
printf(" 成功恢复原始权限: %o\n", new_info.msg_perm.mode);
}
// 示例4: 获取系统消息队列限制信息(IPC_INFO)
printf("\n示例4: 获取系统消息队列限制信息\n");
struct msginfo msg_info;
if (msgctl(msgid, IPC_INFO, (struct msqid_ds *)&msg_info) == -1) {
perror("获取系统信息失败");
} else {
printf(" 系统消息队列限制信息:\n");
printf(" 最大消息队列数: %d\n", msg_info.msgpool);
printf(" 最大消息总数: %d\n", msg_info.msgmap);
printf(" 最大队列字节数: %d\n", msg_info.msgmax);
printf(" 最大消息字节数: %d\n", msg_info.msgmnb);
printf(" 最大消息段数: %d\n", msg_info.msgmni);
printf(" 最大消息大小: %d\n", msg_info.msgssz);
printf(" 消息队列数: %d\n", msg_info.msgtql);
printf(" 版本: %d\n", msg_info.msgseg);
}
// 示例5: 错误处理演示
printf("\n示例5: 错误处理演示\n");
// 尝试操作不存在的消息队列
if (msgctl(999999, IPC_STAT, &queue_info) == -1) {
printf(" 操作不存在的队列: %s\n", strerror(errno));
}
// 尝试使用无效命令
if (msgctl(msgid, 999, &queue_info) == -1) {
printf(" 使用无效命令: %s\n", strerror(errno));
}
// 示例6: 删除消息队列(IPC_RMID)
printf("\n示例6: 删除消息队列\n");
// 在删除前先获取一次状态
if (msgctl(msgid, IPC_STAT, &queue_info) == -1) {
perror("删除前获取状态失败");
} else {
printf(" 删除前队列状态正常\n");
}
// 删除消息队列
if (msgctl(msgid, IPC_RMID, NULL) == -1) {
perror("删除消息队列失败");
} else {
printf(" 成功删除消息队列 %d\n", msgid);
}
// 验证删除结果
if (msgctl(msgid, IPC_STAT, &queue_info) == -1) {
printf(" 验证: 消息队列已不存在 - %s\n", strerror(errno));
}
// 示例7: 创建多个队列并管理
printf("\n示例7: 管理多个消息队列\n");
int msgids[3];
key_t keys[3];
// 创建3个消息队列
for (int i = 0; i < 3; i++) {
keys[i] = ftok(".", 'j' + i);
if (keys[i] == -1) {
perror("生成键值失败");
continue;
}
// 清理可能存在的同名队列
int temp_id = msgget(keys[i], 0666);
if (temp_id != -1) {
msgctl(temp_id, IPC_RMID, NULL);
}
msgids[i] = msgget(keys[i], 0666 | IPC_CREAT);
if (msgids[i] == -1) {
perror("创建消息队列失败");
} else {
printf(" 创建消息队列%d,标识符: %d\n", i+1, msgids[i]);
}
}
// 获取每个队列的信息
for (int i = 0; i < 3; i++) {
if (msgids[i] != -1) {
struct msqid_ds info;
if (msgctl(msgids[i], IPC_STAT, &info) == -1) {
perror("获取队列信息失败");
} else {
printf(" 队列%d信息 - 键值: %d, 消息数: %ld\n",
i+1, info.msg_perm.__key, info.msg_qnum);
}
}
}
// 删除所有队列
printf(" 删除所有队列...\n");
for (int i = 0; i < 3; i++) {
if (msgids[i] != -1) {
if (msgctl(msgids[i], IPC_RMID, NULL) == -1) {
perror("删除队列失败");
} else {
printf(" 成功删除队列%d\n", i+1);
}
}
}
return 0;
}