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

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.xdnf.cn/news/17091.html

相关文章:

  • Java开发时出现的问题---并发与资源管理深层问题
  • 在具身智能火热加持下,看 2025 年机器人学术年会中的热点主题。PNP机器人展示力控、灵巧手捕捉等案例。
  • Android Studio下载及安装配置
  • 计算机视觉的四项基本任务辨析
  • Android audio之 AudioDeviceInventory
  • 飞算JavaAI需求转SpringBoot项目:从零到一的沉浸式开发之旅
  • 人工智能之数学基础:利用全概率公式如何将复杂事件转为简单事件
  • 学习游戏制作记录(将各种属性应用于战斗以及实体的死亡)8.5
  • DM8日常运维命令总结(四)
  • Go语言 string
  • 数据结构——双向链表
  • Linux 调度器函数sched_*系统调用及示例
  • 【音视频】WebRTC 一对一通话-信令服
  • Go语言实战案例:使用context控制协程取消
  • 算法训练之哈希表
  • Java后端高频面试题
  • React在使用create-react-app创建项目慢的解决办法
  • python的高校考研交流系统
  • 基于ARM+FPGA多通道超声信号采集与传输系统设计
  • 广州客户 戴尔R720服务器 liunx系统 RAID5无损升级扩容
  • 注意点:Git 从安装到分支协作、冲突解决的完整步骤 ---待修改,没看这个步骤,需要重新整理步骤
  • JavaWeb(苍穹外卖)--学习笔记17(Websocket)
  • 国产三防平板电脑是什么?三防平板推荐
  • 前端包管理器深度对比
  • VUE2 学习笔记18 路由守卫
  • Mysql使用Canal服务同步数据->ElasticSearch
  • 数据挖掘,到底是在挖掘什么?
  • Golang 基本数据类型
  • 智慧工业复杂目标检测精度跃升:陌讯多模态融合算法实战解析
  • mac前端环境安装