写一个C语言程序,使用匿名管道实现父子进程通信。先将子进程输出的字符串结果,重定向到管道的写端,然后父进程检测这些字符串,一旦检测到stop,则使父进程kill掉子进程。
时间: 2025-08-06 09:59:25 浏览: 9
<think>我们有一个任务:编写C程序,使用匿名管道实现父子进程通信,并根据子进程发送的消息终止子进程。
要求:父进程创建子进程,通过匿名管道进行通信。子进程通过管道发送消息,当父进程接收到特定的消息(例如"stop")时,父进程终止子进程。
步骤:
1. 创建匿名管道:使用pipe()函数。
2. 创建子进程:使用fork()函数。
3. 在子进程中:
a. 关闭管道的读端(因为子进程只写)。
b. 将标准输出重定向到管道的写端(如果需要发送标准输出内容)?但是题目要求是发送消息,我们可以直接使用write()函数向管道写端写入数据。
c. 子进程循环发送消息,直到发送特定消息(比如"stop")后,子进程自己停止?但题目要求是父进程根据消息终止子进程,所以子进程发送消息后并不立即停止,而是等待父进程终止它。
d. 注意:子进程在发送"stop"消息后,可以继续运行直到被父进程终止,或者也可以选择在发送后等待一段时间(但题目要求父进程终止它,所以子进程在发送后可以继续做其他事情,直到被父进程杀死)。
4. 在父进程中:
a. 关闭管道的写端(因为父进程只读)。
b. 循环读取管道中的数据。
c. 当读取到特定消息(如"stop")时,父进程使用kill()函数向子进程发送SIGKILL信号终止子进程。
d. 父进程回收子进程(使用wait或waitpid)。
但是注意:管道是字节流,没有消息边界,所以我们需要设计消息格式。这里为了简单,我们可以每次发送固定长度的消息,或者以换行符分隔。这里我们选择以换行符分隔,即每次发送一条消息,每条消息以换行符结束。
另一种方法是:子进程发送字符串,父进程读取到字符串后判断是否包含"stop"。但是注意,读取的数据可能不是完整的一行,所以我们需要在父进程中处理字节流,按行读取。
我们可以使用stdio库中的fdopen和fgets来按行读取,或者自己用缓冲区按行分割。
为了简化,我们使用read系统调用,并自己处理行分割(因为管道是字节流,可能一次读取不完整一行,也可能一次读取多行)。但考虑到简单性,我们假设消息比较短,可以一次读取一行?或者我们使用行缓冲。
另一种方法是:子进程每次写入一个固定大小的缓冲区,但这样不够灵活。
我们选择:子进程每次发送一条消息,以换行符结尾。父进程循环读取,每次读取一个字符直到遇到换行符,然后判断整行是否为"stop"。
但是,更高效的方式是使用缓冲区,但为了简单,我们假设消息很短。
设计:
- 子进程发送多条消息,其中最后一条是"stop\n"。
- 父进程读取每一条消息并打印,当读取到"stop\n"时,父进程杀死子进程。
步骤详细:
1. 创建管道:int pipefd[2]; pipe(pipefd);
2. fork():
- 子进程:
close(pipefd[0]); // 关闭读端
// 向管道写端写数据
write(pipefd[1], "message1\n", 10);
sleep(1);
write(pipefd[1], "message2\n", 10);
sleep(1);
write(pipefd[1], "stop\n", 5);
// 然后子进程继续运行,直到被父进程杀死
while(1) {
printf("Child is alive...\n");
sleep(1);
}
// 注意:在发送stop后,子进程进入死循环,父进程会杀死它
- 父进程:
close(pipefd[1]); // 关闭写端
char buf[1024];
ssize_t n;
while (1) {
// 按行读取:这里我们简化,使用read逐字节读取直到换行
int i=0;
char c;
while ((n = read(pipefd[0], &c, 1)) > 0) {
if (c == '\n') {
buf[i] = '\0';
break;
}
buf[i++] = c;
}
if (n <= 0) break; // 出错或管道关闭
printf("Parent received: %s\n", buf);
// 检查是否收到停止消息
if (strcmp(buf, "stop") == 0) {
printf("Parent will kill child.\n");
kill(pid, SIGKILL); // 发送SIGKILL信号
break;
}
}
// 回收子进程
waitpid(pid, NULL, 0);
但是,上述逐字节读取效率低,我们可以使用缓冲区和strchr来查找换行符,但为了简单,这里我们使用逐字节。
注意:子进程在发送stop后进入死循环,父进程在读取到stop后立即杀死子进程。
另外,为了防止父进程在读取过程中被子进程的其他消息阻塞,我们假设消息不会太长(小于缓冲区大小),并且每条消息以换行符结束。
但是,如果父进程在读取到完整一行之前就阻塞了怎么办?我们使用逐字节读取,每次读取一个字符,直到换行。这样确保每次只读取一行。
然而,更高效的方法是使用stdio的fopen和fgets,将管道读端转换为文件流。
修改父进程读取方式:
FILE *fp = fdopen(pipefd[0], "r");
char buf[1024];
while (fgets(buf, sizeof(buf), fp) != NULL) {
// 去掉换行符
buf[strcspn(buf, "\n")] = '\0';
printf("Parent received: %s\n", buf);
if (strcmp(buf, "stop") == 0) {
printf("Parent will kill child.\n");
kill(pid, SIGKILL);
break;
}
}
// 然后回收子进程
这样更简洁。
完整代码框架:
注意:在父进程中,关闭管道的写端后,将读端文件描述符转换为文件流。
步骤:
1. 创建管道
2. 创建子进程
3. 子进程:关闭读端,写消息,最后写"stop\n",然后进入循环(模拟继续工作)
4. 父进程:关闭写端,将读端文件描述符转换为FILE*,使用fgets按行读取,当读取到"stop"时,杀死子进程,然后回收子进程。
注意:子进程在写管道时,如果父进程关闭了读端(比如父进程提前退出),子进程会收到SIGPIPE信号。但我们的逻辑是父进程一直读取直到收到stop,所以不会提前关闭。
另外,在父进程杀死子进程后,子进程终止,父进程需要回收。
代码实现:
注意错误处理。
我们写一个完整的程序。</think>以下是一个使用匿名管道实现父子进程通信的C语言程序,父进程根据子进程发送的"stop"消息终止子进程:
```c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <signal.h>
#define BUFFER_SIZE 128
int main() {
int pipefd[2]; // 管道文件描述符数组
pid_t pid; // 子进程PID
// 1. 创建匿名管道
if (pipe(pipefd) == -1) {
perror("pipe创建失败");
exit(EXIT_FAILURE);
}
// 2. 创建子进程
pid = fork();
if (pid < 0) {
perror("fork失败");
exit(EXIT_FAILURE);
}
if (pid == 0) { // 子进程
close(pipefd[0]); // 关闭读端
// 向管道发送消息
const char *messages[] = {"Hello", "World", "stop", "Ignored"};
for (int i = 0; i < 4; i++) {
write(pipefd[1], messages[i], strlen(messages[i]) + 1);
printf("[子进程] 发送: %s\n", messages[i]);
sleep(1); // 模拟处理延迟
}
close(pipefd[1]); // 关闭写端
printf("[子进程] 继续运行...\n");
while(1) sleep(1); // 模拟持续工作
exit(EXIT_SUCCESS);
} else { // 父进程
close(pipefd[1]); // 关闭写端
char buffer[BUFFER_SIZE];
while (1) {
// 清空缓冲区并读取数据
memset(buffer, 0, BUFFER_SIZE);
ssize_t count = read(pipefd[0], buffer, BUFFER_SIZE - 1);
if (count <= 0) break; // 管道关闭或错误
printf("[父进程] 收到: %s\n", buffer);
// 3. 检测终止指令
if (strcmp(buffer, "stop") == 0) {
printf("[父进程] 收到终止指令\n");
// 4. 终止子进程
if (kill(pid, SIGKILL) == 0) {
printf("[父进程] 已终止子进程(PID=%d)\n", pid);
} else {
perror("kill失败");
}
break;
}
}
close(pipefd[0]); // 关闭读端
waitpid(pid, NULL, 0); // 回收子进程
printf("[父进程] 程序结束\n");
}
return 0;
}
```
### 程序说明:
1. **管道创建**:
- 通过`pipe(pipefd)`创建匿名管道,`pipefd[0]`为读端,`pipefd[1]`为写端[^4]
2. **进程分工**:
- **子进程**:向管道发送4条消息(包括"stop"),之后进入死循环模拟持续工作
- **父进程**:监控管道内容,收到"stop"后发送`SIGKILL`终止子进程[^1]
3. **通信机制**:
- 子进程使用`write()`向管道写入数据
- 父进程使用`read()`从管道读取数据
- 消息以`\0`结尾的字符串形式传输
4. **进程终止**:
- 父进程通过`kill(pid, SIGKILL)`强制终止子进程[^2]
- `waitpid()`回收子进程资源,防止僵尸进程
### 执行流程:
1. 子进程依次发送:`Hello` → `World` → `stop` → `Ignored`
2. 父进程收到`stop`时立即终止子进程
3. `Ignored`消息不会被父进程处理(因管道已关闭)
### 注意事项:
1. 管道读写端需及时关闭,避免资源泄漏
2. `SIGKILL`(信号9)会立即终止目标进程[^2]
3. 实际应用中应考虑消息边界问题(本程序使用`\0`分隔)
阅读全文
相关推荐




















