8. msgsnd - 发送消息到消息队列 Link to heading
函数介绍 Link to heading
msgsnd
系统调用用于向System V消息队列发送消息。它是进程间通信的重要机制,允许进程将数据打包成消息的形式发送到消息队列中,供其他进程接收和处理。
函数原型 Link to heading
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
功能 Link to heading
将消息发送到指定的消息队列中。
参数 Link to heading
int msqid
: 消息队列的标识符(由msgget返回)const void *msgp
: 指向消息结构体的指针,消息结构体的第一个成员必须是long msg_type
size_t msgsz
: 消息正文的字节数(不包括msg_type字段)int msgflg
: 控制标志0
: 阻塞模式,如果队列满则等待IPC_NOWAIT
: 非阻塞模式,如果队列满则立即返回错误
返回值 Link to heading
- 成功时返回0
- 失败时返回-1,并设置errno:
EAGAIN
: 非阻塞模式下队列满EACCES
: 没有写权限EIDRM
: 消息队列已被删除EINTR
: 系统调用被信号中断EINVAL
: 参数无效ENOMEM
: 系统内存不足
相似函数 Link to heading
msgrcv()
: 从消息队列接收消息msgget()
: 获取消息队列标识符msgctl()
: 控制消息队列
示例代码 Link to heading
#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;
}