C语言pipe/pthread/fork组合程序输出结果思路辨析
C语言多线程fork场景下管道关闭输出问题
问题描述
给定如下C语言代码,要求写出程序的运行输出:
cont = 0; int p[2]; void* f1() { while(!cont){} printf("%d ", close(p[0])); return NULL; } int main() { pthread_t t1; pipe(p); pthread_create(&t1,NULL,f1,NULL); fork(); printf("%d ", close(p[0])); cont = 1; pthread_join(t1,NULL); return 0; }
题目给出的参考答案为两种可能:输出0 -1 0或者0 0 -1。我个人推导的结果是0 -1 0 -1,推导逻辑为:fork()会复制进程的全部资源,父进程调用close(p[0])时打印0,随后父进程内的线程再调用close(p[0])会打印-1,子进程也会执行相同流程,因此总共有4个输出值,想知道推导思路哪里存在错误。
错误点分析
你的推导核心错误是对fork()在多线程进程中的行为理解有误,具体拆解如下:
- fork不会复制父进程的所有线程
调用fork生成的子进程,只会复制发起fork调用的那一个执行线程的上下文,父进程中已经创建的其他线程(本题中就是t1线程)完全不会在子进程中存在,子进程里不会运行f1函数的逻辑,自然不会执行f1里的close(p[0])操作。 - fork后父子进程资源完全独立
fork完成后,父子进程拥有独立的地址空间、独立的文件描述符副本:父进程对自己持有的p[0]做close操作,不会影响子进程持有的p[0]状态,反过来也一样。全局变量cont也是父子进程各存一份,修改其中一个不会影响另一个。
统计执行流数量:- 父进程包含主线程、t1线程两个执行流,会产生2个输出
- 子进程只有从fork位置继续执行的主线程这一个执行流,只会产生1个输出
总输出数固定是3个,不可能出现4个值。
输出逻辑梳理
我们按执行顺序拆解流程:
- 初始父进程先创建管道p,再创建t1线程,t1启动后一直在自旋等待全局变量cont变为1,暂时不会执行后续close逻辑。
- 主线程调用fork生成子进程,此时父子进程的执行顺序由操作系统调度决定,没有固定先后:
- 父进程主线程首先执行自己的close(p[0]):这是父进程第一次关闭p[0],返回0,输出0。
- 子进程如果先被调度,会先执行自己的close(p[0]):这是子进程第一次关闭自己持有的p[0]副本,返回0,输出0。
- 每个进程的主线程执行完close后,会把自己进程地址空间内的cont变量设为1,随后调用pthread_join等待t1:
- 对父进程来说,cont设为1后,自旋的t1会退出循环,执行close(p[0]):此时父进程的p[0]已经被主线程关闭过,重复关闭会返回-1,输出-1。
- 对子进程来说,根本不存在t1这个线程,pthread_join会直接返回,不会产生额外输出。
因此最终只会有三个输出值,根据父子进程调度顺序不同,就会出现参考答案给出的两种结果:
- 如果父进程先执行完自己的所有逻辑,子进程最后输出,结果就是
0 -1 0 - 如果子进程先执行完自己的close逻辑,父进程再输出后续两个值,结果就是
0 0 -1
内容的提问来源于stack exchange,提问作者Cristian Alam
相关产品推荐
相关产品推荐

