7. msgget - 获取消息队列标识符 見出しへのリンク

函数介绍 見出しへのリンク

msgget系统调用用于创建或访问System V消息队列。消息队列是一种进程间通信机制,允许进程通过发送和接收消息来交换数据,消息按先进先出(FIFO)的顺序处理。

函数原型 見出しへのリンク

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgget(key_t key, int msgflg);

功能 見出しへのリンク

创建新的消息队列或获取现有消息队列的标识符。

参数 見出しへのリンク

  • key_t key: 消息队列的键值
    • IPC_PRIVATE: 创建私有消息队列
    • 正整数: 通过ftok()函数生成的键值
  • int msgflg: 标志位组合
    • IPC_CREAT: 如果消息队列不存在则创建
    • IPC_EXCL: 与IPC_CREAT配合使用,如果已存在则失败
    • 权限位: 如0666(读写权限)

返回值 見出しへのリンク

  • 成功时返回消息队列的标识符(非负整数)
  • 失败时返回-1,并设置errno:
    • EACCES: 权限不足
    • EEXIST: 消息队列已存在(使用IPC_EXCL时)
    • EINVAL: 参数无效
    • ENOENT: 消息队列不存在且未指定IPC_CREAT
    • ENOSPC: 系统消息队列数量已达上限

相似函数 見出しへのリンク

  • msgsnd(): 发送消息到消息队列
  • msgrcv(): 从消息队列接收消息
  • msgctl(): 控制消息队列
  • ftok(): 生成System V IPC键值

示例代码 見出しへのリンク

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>
#include <string.h>

// 消息结构体
struct msg_buffer {
    long msg_type;    // 消息类型,必须为正整数
    char msg_text[100]; // 消息内容
};

int main() {
    key_t key;
    int msgid;
    
    printf("=== Msgget函数示例 ===\n");
    
    // 示例1: 使用IPC_PRIVATE创建私有消息队列
    printf("\n示例1: 创建私有消息队列\n");
    msgid = msgget(IPC_PRIVATE, 0666 | IPC_CREAT);
    if (msgid == -1) {
        perror("创建私有消息队列失败");
        exit(EXIT_FAILURE);
    }
    printf("  成功创建私有消息队列,标识符: %d\n", msgid);
    
    // 删除消息队列
    if (msgctl(msgid, IPC_RMID, NULL) == -1) {
        perror("删除消息队列失败");
    } else {
        printf("  成功删除消息队列\n");
    }
    
    // 示例2: 使用ftok生成键值创建消息队列
    printf("\n示例2: 使用ftok创建消息队列\n");
    
    // 生成键值
    key = ftok(".", 'e');
    if (key == -1) {
        perror("ftok失败");
        exit(EXIT_FAILURE);
    }
    printf("  生成的键值: %d\n", key);
    
    // 创建消息队列
    msgid = msgget(key, 0666 | IPC_CREAT);
    if (msgid == -1) {
        perror("创建消息队列失败");
        exit(EXIT_FAILURE);
    }
    printf("  成功创建消息队列,标识符: %d\n", msgid);
    
    // 示例3: 尝试创建已存在的消息队列
    printf("\n示例3: 尝试重复创建消息队列\n");
    int msgid2 = msgget(key, 0666 | IPC_CREAT | IPC_EXCL);
    if (msgid2 == -1) {
        if (errno == EEXIST) {
            printf("  消息队列已存在\n");
        } else {
            perror("  msgget失败");
        }
    } else {
        printf("  意外创建了新的消息队列: %d\n", msgid2);
        // 清理
        msgctl(msgid2, IPC_RMID, NULL);
    }
    
    // 示例4: 获取已存在的消息队列
    printf("\n示例4: 获取已存在的消息队列\n");
    int msgid3 = msgget(key, 0666);
    if (msgid3 == -1) {
        perror("获取消息队列失败");
    } else {
        printf("  成功获取消息队列,标识符: %d\n", msgid3);
        // 验证是否为同一个消息队列
        if (msgid3 == msgid) {
            printf("  确认为同一个消息队列\n");
        }
    }
    
    // 示例5: 错误处理演示
    printf("\n示例5: 错误处理\n");
    
    // 尝试访问不存在的消息队列(不创建)
    int msgid4 = msgget(12345, 0666);
    if (msgid4 == -1) {
        if (errno == ENOENT) {
            printf("  消息队列不存在: %s\n", strerror(errno));
        } else {
            perror("  其他错误");
        }
    }
    
    // 示例6: 权限测试
    printf("\n示例6: 权限测试\n");
    
    // 创建一个只读的消息队列
    key = ftok(".", 'f');
    if (key != -1) {
        int readonly_msgid = msgget(key, 0444 | IPC_CREAT);
        if (readonly_msgid != -1) {
            printf("  成功创建只读消息队列: %d\n", readonly_msgid);
            
            // 尝试获取写权限(应该失败)
            int write_msgid = msgget(key, 0222);
            if (write_msgid == -1) {
                printf("  获取写权限失败: %s\n", strerror(errno));
            }
            
            // 清理
            msgctl(readonly_msgid, IPC_RMID, NULL);
        }
    }
    
    // 清理:删除创建的消息队列
    printf("\n清理资源...\n");
    if (msgctl(msgid, IPC_RMID, NULL) == -1) {
        perror("清理消息队列失败");
    } else {
        printf("  成功清理消息队列\n");
    }
    
    return 0;
}