当前位置: 首页 > news >正文

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;
}
http://www.lryc.cn/news/611457.html

相关文章:

  • RC和RR的区别
  • 一文搞定JavaServerPages基础,从0开始写一个登录与人数统计页面
  • Python 函数详解
  • SpringCloud学习------Hystrix详解
  • 通俗版23种设计模式解析
  • 苍穹外卖Day10
  • 智慧酒店:科技赋能下的未来住宿新体验
  • Datawhale AI夏令营 第三期 task2 稍微改进
  • 山东省天地图API申请并加载到QGIS和ArcGIS Pro中
  • 数据结构 实现单链表
  • LeetCode347.前K个高频元素(hash表+桶排序)
  • Chisel芯片开发入门系列 -- 18. CPU芯片开发和解释8(流水线架构的代码级理解)
  • 思途Mybatis学习 0805
  • LeetCode 刷题【31. 下一个排列】
  • 《Python基础》第3期:使用PyCharm编写Hello World
  • C++ 变量初始化方式总结 | 拷贝初始化 | 列表初始化 | 值初始化
  • 【C语言】动态内存管理详解
  • Kafka 的基本操作(1)
  • 国内办公安全平台新标杆:iOA一体化办公安全解决方案
  • 【基础】第八篇 Java 位运算符详解:从基础到实战应用
  • 【java】大数据insert的几种技术方案和优缺点
  • 一种基于机器学习的关键安全软件WCET分析方法概述与实际工作原理举例
  • 多传感器融合
  • 机器人权利:真实还是虚幻,机器人权利研究如何可能,道德权利与法律权利
  • nodejs 编程基础01-NPM包管理
  • 《计算机“十万个为什么”》之 面向对象 vs 面向过程:编程世界的积木与流水线
  • 【android bluetooth 协议分析 01】【HCI 层介绍 30】【hci_event和le_meta_event如何上报到btu层】
  • 零基础人工智能学习规划之路
  • 电路基础相关知识
  • HBM Basic(VCU128)