使用匿名管道时出现程序一直运行问题

父进程创建两个子进程,父子进程之间利用管道进行通信。要求能显示父进程、子进程各自的信息,体现通信效果。(源程序pipe_1.c)

一开始,我忘了初始化pipe,很傻*的直接把fd当管道使,出现了儿子喊爸爸"i am your father."的笑话。

后面我意识到,原来是我pipe忘了初始化了。

然后改成了上面的代码,结果发现程序一直卡在这一步

后面用AI才知道,原来是pipe写的位置错了,应该写在fork的前面,否则会导致父子进程各有一个匿名管道,从而导致进程卡死。

回顾一下匿名管道的特性:

1.读写分开。

2.无名,只能在同一根进程间可以使用。

3.不保证数据写入的原子性。即当父子进程同时向管道写入数据时,有可能会导致写入的数据被破坏。比如同时传入hello,有可能读取到的会是hehellollo。因此无名管道只能进行1v1通信
[写者:有权限可以对管道进行写入操作;读者:有权限可以对管道进行读取操作]
由于管道本身可以初始化自带数据,所以不一定需要写者,但一定需要有读者,否则会报错pipe borken 

这个是修改后正确的代码

#include<stdlib.h>
#include<stdbool.h>
#include<unistd.h>
#include<strings.h>
#include<stdio.h>
#include<sys/wait.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<errno.h>
#include<fcntl.h>
int main()
{
        int fd[2],pid;
        pipe(fd);
        while((pid=fork())==-1);        //若程序没有生成子进程,则重复执行

        if(pid>0)                       //父进程
        {
                printf("i am parent.\n");
                char f_r[50];
                bzero (f_r ,50);                //清空字符串
                read(fd[0],f_r,50);
                printf("chiled said:  %s\n",f_r);
                char f_said[50]="i am your father.";
                write(fd[1],f_said,strlen(f_said));
        }

        else  if(pid==0)
        {
                printf("i am child.\n");
                char c_said[50]="hi,dad.";
                write(fd[1],c_said,strlen(c_said));
                sleep(3);
                char c_r[50];
                bzero(c_r,50);
                read(fd[0],c_r,50);
                printf("dad said:  %s\n",c_r);

        }




        return 0;
}

                

 

这是运行结果

可能有人要问了,为什么最后一句的前面有shell会话命令头呢?

这是因为会话只于程序的父进程绑定,当父进程结束时,shell会以为进程已经执行结束,所以会弹出命令头。

在子进程中,若使用了exec函数,则会将原函数代码全部替换为exec参数文件代码,并从头执行,所以原来exec下面的代码无法执行。
若执行了原来exec下面的代码,则说明exec没有正确执行。

<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`分隔)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值