进程间通信示例
使用消息队列在两个独立的 C 程序之间进行进程间通信。每个程序都包含父进程和子进程,父进程用于写消息到消息队列,子进程用于从消息队列读取消息。
程序1 (s1.c)
#include <myhead.h>
// 定义消息缓冲区结构体
struct msgbuf {
long mtype; // 消息类型
char mtext[1000]; // 消息正文
};
#define LENG (sizeof(struct msgbuf) - sizeof(long)) // 计算消息正文的长度
int main() {
key_t key1 = ftok("./", 'A'); // 创建key值1
key_t key2 = ftok("./", 'B'); // 创建key值2
if (key1 == -1 || key2 == -1) {
perror("ftok");
return -1;
}
int msgID1 = msgget(key1, IPC_CREAT | 0664); // 创建或获取消息队列1
int msgID2 = msgget(key2, IPC_CREAT | 0664); // 创建或获取消息队列2
if (msgID1 == -1 || msgID2 == -1) {
perror("msgget");
return -1;
}
pid_t pid = fork(); // 创建子进程
if (pid > 0) {
// 父进程发送消息到队列1
struct msgbuf send;
while (1) {
printf("s1父进程:请输入消息类型:\n");
scanf("%ld", &send.mtype); // 读取消息类型
getchar(); // 清除输入缓冲区中的换行符
printf("s1父进程:请输入消息的内容:\n");
fgets(send.mtext, sizeof(send.mtext), stdin); // 读取消息内容
send.mtext[strlen(send.mtext) - 1] = '\0'; // 去掉换行符
msgsnd(msgID1, &send, LENG, 0); // 发送消息到队列1
printf("s1父进程:发送消息:类型=%ld,内容=%s\n", send.mtype, send.mtext);
if (strcmp(send.mtext, "quit") == 0) { // 如果消息内容是"quit",则退出循环
break;
}
}
// 等待子进程结束
wait(NULL);
// 删除消息队列1
if (msgctl(msgID1, IPC_RMID, NULL) == -1) {
perror("msgctl");
return -1;
}
printf("s1父进程:消息队列1已删除\n");
} else if (pid == 0) {
// 子进程读取消息从队列2
struct msgbuf rcv;
while (1) {
msgrcv(msgID2, &rcv, LENG, 0, 0); // 从队列2读取消息,第一个0:读取第一个信息不论类型,第二个0:阻塞接收
printf("s1子进程:收到消息:类型=%ld,内容=%s\n", rcv.mtype, rcv.mtext);
if (strcmp(rcv.mtext, "quit") == 0) { // 如果消息内容是"quit",则退出循环
break;
}
}
printf("s1子进程:退出\n");
exit(0); // 子进程退出
} else {
perror("fork");
return -1;
}
return 0;
}
程序2 (s2.c)
#include <myhead.h> // 包含自定义头文件
// 定义消息缓冲区结构体
struct msgbuf {
long mtype; // 消息类型
char mtext[1000]; // 消息正文
};
#define LENG (sizeof(struct msgbuf) - sizeof(long)) // 计算消息正文的长度
int main() {
key_t key1 = ftok("./", 'A'); // 创建key值1
key_t key2 = ftok("./", 'B'); // 创建key值2
if (key1 == -1 || key2 == -1) {
perror("ftok");
return -1;
}
int msgID1 = msgget(key1, IPC_CREAT | 0664); // 创建或获取消息队列1
int msgID2 = msgget(key2, IPC_CREAT | 0664); // 创建或获取消息队列2
if (msgID1 == -1 || msgID2 == -1) {
perror("msgget");
return -1;
}
pid_t pid = fork(); // 创建子进程
if (pid > 0) {
// 父进程发送消息到队列2
struct msgbuf send;
while (1) {
printf("s2父进程:请输入消息类型:\n");
scanf("%ld", &send.mtype); // 读取消息类型
getchar(); // 清除输入缓冲区中的换行符
printf("s2父进程:请输入消息的内容:\n");
fgets(send.mtext, sizeof(send.mtext), stdin); // 读取消息内容
send.mtext[strlen(send.mtext) - 1] = '\0'; // 去掉换行符
msgsnd(msgID2, &send, LENG, 0); // 发送消息到队列2
printf("s2父进程:发送消息:类型=%ld,内容=%s\n", send.mtype, send.mtext);
if (strcmp(send.mtext, "quit") == 0) { // 如果消息内容是"quit",则退出循环
break;
}
}
// 等待子进程结束
wait(NULL);
// 删除消息队列2
if (msgctl(msgID2, IPC_RMID, NULL) == -1) {
perror("msgctl");
return -1;
}
printf("s2父进程:消息队列2已删除\n");
} else if (pid == 0) {
// 子进程读取消息从队列1
struct msgbuf rcv;
while (1) {
msgrcv(msgID1, &rcv, LENG, 0, 0); // 从队列1读取消息,第一个0:读取第一个信息不论类型,第二个0:阻塞接收
printf("s2子进程:收到消息:类型=%ld,内容=%s\n", rcv.mtype, rcv.mtext);
if (strcmp(rcv.mtext, "quit") == 0) { // 如果消息内容是"quit",则退出循环
break;
}
}
printf("s2子进程:退出\n");
exit(0); // 子进程退出
} else {
perror("fork");
return -1;
}
return 0;
}
解释
-
定义消息缓冲区结构体:
struct msgbuf { long mtype; // 消息类型 char mtext[1000]; // 消息正文 };
-
计算消息正文的长度:
#define LENG (sizeof(struct msgbuf) - sizeof(long))
-
主函数:
- 使用
ftok
生成两个唯一的键值。 - 使用
msgget
创建或获取两个消息队列。 - 使用
fork
创建子进程。
- 使用
-
父进程发送消息:
- 父进程负责发送消息到消息队列。
- 循环读取用户输入的消息类型和消息正文,并发送到消息队列。
- 如果消息正文是 “quit”,则退出循环并等待子进程结束。
- 删除消息队列。
-
子进程读取消息:
- 子进程负责从消息队列中读取消息。
- 循环接收消息队列中的消息,并打印消息类型和消息正文。
- 如果消息正文是 “quit”,则退出循环。
这两个程序,每个程序都包含父进程和子进程,父进程用于写消息到消息队列,子进程用于从消息队列中读取消息,实现了两个程序之间的进程间通信。