mq_unlink系统调用及示例
mq_unlink函数详解
1. 函数介绍
mq_unlink函数是Linux系统中用于删除POSIX消息队列的函数。可以把mq_unlink想象成一个"消息队列删除器",它能够从系统中移除指定名称的消息队列。
POSIX消息队列是进程间通信(IPC)的一种机制,允许不同进程通过队列发送和接收消息。mq_unlink的作用类似于文件系统的unlink函数,它删除消息队列的名称,但不会立即销毁队列本身。只有当所有打开该队列的进程都关闭了队列描述符后,队列才会被真正销毁。
使用场景:
- 进程间通信系统的清理
- 服务器程序的资源管理
- 系统维护和清理脚本
- 消息队列生命周期管理
2. 函数原型
#include <mqueue.h>int mq_unlink(const char *name);
3. 功能
mq_unlink函数的主要功能是删除指定名称的POSIX消息队列。它从系统中移除队列的名称,使得后续无法通过该名称打开队列,但已打开的队列描述符仍然有效。
4. 参数
- name: 消息队列名称
- 类型:const char*
- 含义:要删除的消息队列名称
- 名称必须以’/'开头,如"/my_queue"
5. 返回值
- 成功: 返回0
- 失败: 返回-1,并设置errno错误码
- EACCES:权限不足
- ENOENT:指定名称的消息队列不存在
- EINVAL:名称无效
6. 相似函数或关联函数
- mq_open(): 打开或创建消息队列
- mq_close(): 关闭消息队列描述符
- mq_send(): 发送消息
- mq_receive(): 接收消息
- mq_getattr(): 获取队列属性
- mq_setattr(): 设置队列属性
- unlink(): 删除文件
7. 示例代码
示例1:基础mq_unlink使用 - 简单队列删除
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <mqueue.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>// 创建消息队列
mqd_t create_message_queue(const char* name) {struct mq_attr attr = {.mq_flags = 0,.mq_maxmsg = 10,.mq_msgsize = 256,.mq_curmsgs = 0};mqd_t mq = mq_open(name, O_CREAT | O_RDWR, 0644, &attr);if (mq == (mqd_t)-1) {perror("创建消息队列失败");return -1;}printf("创建消息队列: %s (描述符: %d)\n", name, (int)mq);return mq;
}// 显示消息队列属性
void show_queue_attributes(mqd_t mq, const char* name) {struct mq_attr attr;if (mq_getattr(mq, &attr) == -1) {perror("获取队列属性失败");return;}printf("队列 %s 属性:\n", name);printf(" 最大消息数: %ld\n", attr.mq_maxmsg);printf(" 最大消息大小: %ld\n", attr.mq_msgsize);printf(" 当前消息数: %ld\n", attr.mq_curmsgs);printf(" 标志: %ld\n", attr.mq_flags);
}int main() {printf("=== 基础mq_unlink使用示例 ===\n");const char* queue_name = "/test_queue";// 创建消息队列printf("1. 创建消息队列:\n");mqd_t mq = create_message_queue(queue_name);if (mq == -1) {exit(EXIT_FAILURE);}show_queue_attributes(mq, queue_name);// 发送一些测试消息printf("\n2. 发送测试消息:\n");const char* messages[] = {"第一条测试消息","第二条测试消息","第三条测试消息"};for (int i = 0; i < 3; i++) {if (mq_send(mq, messages[i], strlen(messages[i]), 0) == -1) {perror("发送消息失败");} else {printf("发送消息: %s\n", messages[i]);}}show_queue_attributes(mq, queue_name);// 使用mq_unlink删除队列名称printf("\n3. 使用mq_unlink删除队列名称:\n");if (mq_unlink(queue_name) == 0) {printf("✓ 成功删除队列名称: %s\n", queue_name);printf("注意: 队列本身仍然存在,因为还有打开的描述符\n");} else {printf("✗ 删除队列名称失败: %s\n", strerror(errno));}// 验证队列名称已被删除printf("\n4. 验证队列名称删除效果:\n");mqd_t mq2 = mq_open(queue_name, O_RDONLY);if (mq2 == -1) {printf("✓ 无法通过名称重新打开队列 (预期行为): %s\n", strerror(errno));} else {printf("✗ 仍然可以通过名称打开队列\n");mq_close(mq2);}// 原有描述符仍然可以使用printf("\n5. 原有描述符仍然有效:\n");char buffer[256];ssize_t bytes_received;unsigned int priority;while ((bytes_received = mq_receive(mq, buffer, sizeof(buffer), &priority)) > 0) {buffer[bytes_received] = '\0';printf("接收到消息: %s (优先级: %u)\n", buffer, priority);}// 关闭队列描述符(此时队列才会被真正销毁)printf("\n6. 关闭队列描述符:\n");if (mq_close(mq) == 0) {printf("✓ 队列描述符已关闭,队列被真正销毁\n");} else {perror("关闭队列描述符失败");}printf("\n=== 基础mq_unlink演示完成 ===\n");return 0;
}
示例2:多个进程共享队列的删除管理
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <mqueue.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <string.h>
#include <errno.h>
#include <time.h>#define MAX_MESSAGES 5
#define MESSAGE_SIZE 256// 生产者进程
void producer_process(const char* queue_name, int producer_id) {printf("生产者 %d 启动\n", producer_id);// 打开已存在的队列mqd_t mq = mq_open(queue_name, O_WRONLY);if (mq == (mqd_t)-1) {perror("生产者打开队列失败");exit(EXIT_FAILURE);}srand(time(NULL) + producer_id);// 发送消息for (int i = 0; i < MAX_MESSAGES; i++) {char message[MESSAGE_SIZE];snprintf(message, sizeof(message), "生产者%d的消息%d", producer_id, i + 1);// 随机优先级unsigned int priority = rand() % 10;if (mq_send(mq, message, strlen(message), priority) == -1) {perror("发送消息失败");} else {printf("生产者 %d 发送: %s (优先级: %u)\n", producer_id, message, priority);}sleep(1); // 模拟处理时间}printf("生产者 %d 完成\n", producer_id);mq_close(mq);
}// 消费者进程
void consumer_process(const char* queue_name, int consumer_id) {printf("消费者 %d 启动\n", consumer_id);// 打开已存在的队列mqd_t mq = mq_open(queue_name, O_RDONLY);if (mq == (mqd_t)-1) {perror("消费者打开队列失败");exit(EXIT_FAILURE);}// 接收消息char buffer[MESSAGE_SIZE];ssize_t bytes_received;unsigned int priority;int message_count = 0;while (message_count < MAX_MESSAGES * 2) { // 期望接收所有生产者的消息bytes_received = mq_receive(mq, buffer, sizeof(buffer), &priority);if (bytes_received > 0) {buffer[bytes_received] = '\0';printf("消费者 %d 接收: %s (优先级: %u)\n", consumer_id, buffer, priority);message_count++;} else if (errno == EAGAIN) {// 非阻塞模式下没有消息printf("消费者 %d: 暂无消息\n", consumer_id);sleep(1);} else {perror("接收消息失败");break;}}printf("消费者 %d 完成,接收 %d 条消息\n", consumer_id, message_count);mq_close(mq);
}// 管理进程
void manager_process(const char* queue_name) {printf("管理进程启动\n");// 创建消息队列struct mq_attr attr = {.mq_flags = 0,.mq_maxmsg = 20,.mq_msgsize = MESSAGE_SIZE,.mq_curmsgs = 0};mqd_t mq = mq_open(queue_name, O_CREAT | O_RDWR, 0644, &attr);if (mq == (mqd_t)-1) {perror("管理进程创建队列失败");exit(EXIT_FAILURE);}printf("管理进程创建队列: %s\n", queue_name);// 启动生产者和消费者进程pid_t producers[2], consumers[2];// 启动生产者for (int i = 0; i < 2; i++) {producers[i] = fork();if (producers[i] == 0) {producer_process(queue_name, i + 1);exit(EXIT_SUCCESS);}}// 启动消费者for (int i = 0; i < 2; i++) {consumers[i] = fork();if (consumers[i] == 0) {consumer_process(queue_name, i + 1);exit(EXIT_SUCCESS);}}// 等待生产者完成printf("管理进程等待生产者完成...\n");for (int i = 0; i < 2; i++) {waitpid(producers[i], NULL, 0);}printf("所有生产者已完成\n");// 模拟一段时间让消费者处理完消息sleep(3);// 删除队列名称(但队列仍存在,因为消费者还在使用)printf("管理进程删除队列名称...\n");if (mq_unlink(queue_name) == 0) {printf("✓ 队列名称已删除,但队列仍存在(消费者仍在使用)\n");} else {printf("✗ 删除队列名称失败: %s\n", strerror(errno));}// 等待消费者完成printf("管理进程等待消费者完成...\n");for (int i = 0; i < 2; i++) {waitpid(consumers[i], NULL, 0);}printf("所有消费者已完成\n");// 现在队列才会被真正销毁(所有描述符都已关闭)printf("队列已被真正销毁\n");mq_close(mq);printf("管理进程完成\n");
}int main() {printf("=== 多进程共享队列删除管理示例 ===\n");const char* queue_name = "/shared_queue";// 启动管理进程pid_t manager = fork();if (manager == 0) {manager_process(queue_name);exit(EXIT_SUCCESS);}// 父进程等待管理进程完成waitpid(manager, NULL, 0);// 验证队列是否已被删除printf("\n验证队列删除效果:\n");mqd_t mq = mq_open(queue_name, O_RDONLY);if (mq == -1) {printf("✓ 队列已成功删除: %s\n", strerror(errno));} else {printf("✗ 队列仍然存在\n");mq_close(mq);}printf("\n=== 多进程队列管理演示完成 ===\n");return 0;
}