8. msgsnd - 发送消息到消息队列 見出しへのリンク

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

msgsnd系统调用用于向System V消息队列发送消息。它是进程间通信的重要机制,允许进程将数据打包成消息的形式发送到消息队列中,供其他进程接收和处理。

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

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

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

功能 見出しへのリンク

将消息发送到指定的消息队列中。

参数 見出しへのリンク

  • int msqid: 消息队列的标识符(由msgget返回)
  • const void *msgp: 指向消息结构体的指针,消息结构体的第一个成员必须是long msg_type
  • size_t msgsz: 消息正文的字节数(不包括msg_type字段)
  • int msgflg: 控制标志
    • 0: 阻塞模式,如果队列满则等待
    • IPC_NOWAIT: 非阻塞模式,如果队列满则立即返回错误

返回值 見出しへのリンク

  • 成功时返回0
  • 失败时返回-1,并设置errno:
    • EAGAIN: 非阻塞模式下队列满
    • EACCES: 没有写权限
    • EIDRM: 消息队列已被删除
    • EINTR: 系统调用被信号中断
    • EINVAL: 参数无效
    • ENOMEM: 系统内存不足

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

  • msgrcv(): 从消息队列接收消息
  • msgget(): 获取消息队列标识符
  • msgctl(): 控制消息队列

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

#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>
#include <sys/wait.h>

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

int main() {
    key_t key;
    int msgid;
    struct message msg;
    
    printf("=== Msgsnd函数示例 ===\n");
    
    // 创建消息队列
    key = ftok(".", 'g');
    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: 基本的消息发送
    printf("\n示例1: 基本的消息发送\n");
    
    // 构造消息
    msg.msg_type = 1;  // 消息类型为1
    strcpy(msg.msg_data, "Hello, this is my first message!");
    
    // 发送消息
    if (msgsnd(msgid, &msg, sizeof(msg.msg_data), 0) == -1) {
        perror("发送消息失败");
    } else {
        printf("  成功发送消息:\n");
        printf("    类型: %ld\n", msg.msg_type);
        printf("    内容: %s\n", msg.msg_data);
    }
    
    // 示例2: 发送不同类型的消息
    printf("\n示例2: 发送不同类型的消息\n");
    
    // 发送类型为2的消息
    msg.msg_type = 2;
    strcpy(msg.msg_data, "This is message type 2");
    if (msgsnd(msgid, &msg, sizeof(msg.msg_data), 0) == -1) {
        perror("发送类型2消息失败");
    } else {
        printf("  成功发送类型2消息: %s\n", msg.msg_data);
    }
    
    // 发送类型为3的消息
    msg.msg_type = 3;
    strcpy(msg.msg_data, "This is message type 3");
    if (msgsnd(msgid, &msg, sizeof(msg.msg_data), 0) == -1) {
        perror("发送类型3消息失败");
    } else {
        printf("  成功发送类型3消息: %s\n", msg.msg_data);
    }
    
    // 示例3: 发送结构化数据
    printf("\n示例3: 发送结构化数据\n");
    
    // 定义学生信息结构体
    struct student_info {
        long msg_type;
        int student_id;
        char name[20];
        float score;
    } student_msg;
    
    // 构造学生信息消息
    student_msg.msg_type = 4;
    student_msg.student_id = 1001;
    strcpy(student_msg.name, "张三");
    student_msg.score = 95.5;
    
    // 发送学生信息消息
    if (msgsnd(msgid, &student_msg, sizeof(struct student_info) - sizeof(long), 0) == -1) {
        perror("发送学生信息失败");
    } else {
        printf("  成功发送学生信息:\n");
        printf("    学号: %d\n", student_msg.student_id);
        printf("    姓名: %s\n", student_msg.name);
        printf("    成绩: %.1f\n", student_msg.score);
    }
    
    // 示例4: 非阻塞发送模式
    printf("\n示例4: 非阻塞发送模式\n");
    
    // 获取消息队列信息,查看当前队列状态
    struct msqid_ds buf;
    if (msgctl(msgid, IPC_STAT, &buf) == -1) {
        perror("获取队列状态失败");
    } else {
        printf("  当前队列中的消息数: %ld\n", buf.msg_qnum);
        printf("  队列最大字节数: %ld\n", buf.msg_qbytes);
    }
    
    // 尝试非阻塞发送消息
    msg.msg_type = 5;
    strcpy(msg.msg_data, "Non-blocking message");
    
    if (msgsnd(msgid, &msg, sizeof(msg.msg_data), IPC_NOWAIT) == -1) {
        if (errno == EAGAIN) {
            printf("  非阻塞发送: 队列可能已满或无法立即发送\n");
        } else {
            perror("  非阻塞发送失败");
        }
    } else {
        printf("  非阻塞发送成功\n");
    }
    
    // 示例5: 多进程发送消息演示
    printf("\n示例5: 多进程发送消息演示\n");
    
    pid_t pid = fork();
    if (pid == -1) {
        perror("fork失败");
        msgctl(msgid, IPC_RMID, NULL);
        exit(EXIT_FAILURE);
    } else if (pid == 0) {
        // 子进程发送消息
        struct message child_msg;
        child_msg.msg_type = 10;
        sprintf(child_msg.msg_data, "消息来自子进程 %d", getpid());
        
        printf("子进程 %d 准备发送消息...\n", getpid());
        
        if (msgsnd(msgid, &child_msg, sizeof(child_msg.msg_data), 0) == -1) {
            perror("子进程发送消息失败");
        } else {
            printf("子进程 %d 成功发送消息: %s\n", getpid(), child_msg.msg_data);
        }
        
        exit(EXIT_SUCCESS);
    } else {
        // 父进程也发送消息
        sleep(1);  // 让子进程先发送
        
        msg.msg_type = 20;
        sprintf(msg.msg_data, "消息来自父进程 %d", getpid());
        
        printf("父进程 %d 准备发送消息...\n", getpid());
        
        if (msgsnd(msgid, &msg, sizeof(msg.msg_data), 0) == -1) {
            perror("父进程发送消息失败");
        } else {
            printf("父进程 %d 成功发送消息: %s\n", getpid(), msg.msg_data);
        }
        
        // 等待子进程结束
        wait(NULL);
    }
    
    // 查看队列中消息数量
    if (msgctl(msgid, IPC_STAT, &buf) == -1) {
        perror("获取队列状态失败");
    } else {
        printf("\n发送完成后队列中的消息数: %ld\n", buf.msg_qnum);
    }
    
    // 示例6: 错误处理演示
    printf("\n示例6: 错误处理演示\n");
    
    // 尝试向不存在的消息队列发送消息
    if (msgsnd(999999, &msg, sizeof(msg.msg_data), 0) == -1) {
        printf("  向不存在的队列发送消息: %s\n", strerror(errno));
    }
    
    // 发送类型为0或负数的消息(无效)
    msg.msg_type = 0;
    if (msgsnd(msgid, &msg, sizeof(msg.msg_data), 0) == -1) {
        printf("  发送类型为0的消息: %s\n", strerror(errno));
    }
    
    // 清理资源
    printf("\n清理资源...\n");
    if (msgctl(msgid, IPC_RMID, NULL) == -1) {
        perror("删除消息队列失败");
    } else {
        printf("成功删除消息队列\n");
    }
    
    return 0;
}