引言
本文只是记录了一次看上去意义不大的尝试与总结,如果看下去,可能会浪费您宝贵的5分钟时间。
今日正为下周的分享会做准备,本打算讲讲当我们在终端键入./helloworld
程序后都发生了什么事,理到__start
初始化时,突然想起前不久研究的关于splice调用中易出现的一个bug,在当初的测试中,一旦我在子进程中修改了stdout
的mode
后,即使我在terminal中运行没有修改过的程序,也不会出bug了,子进程的文件描述符状态变化会影响到父进程shell吗?(事实证明是我这个菜鸡记性太差,该回头好好复习Linux系统编程了)
文件描述符与打开文件表
《Linux/Unix系统编程手册》的图5-2诠释了这种现象的本质原因,如图:
进程的每一个文件描述符都会指向由内核维护的打开文件表(open file table)
中的一个打开文件句柄(open file handle)
,一个打开文件句柄
存储着与一个打开文件
相关的所有信息(文件偏移量,状态标志等)。
父进程调用fork后,子进程会继承其所有已打开的文件描述符(不包括父进程在fork之后打开的文件描述符),即父进程与子进程的这些文件描述符指向了相同的打开文件句柄
。
上图进程A的fd2
和进程B的fd2
都指向了打开文件句柄73
,这种情况通常都是在父进程fork出子进程后出现的,也有可能是某一进程使用UNIX域套接字将一个文件描述符传递给了另一个进程(第二种情况可参见《Linux/Unix系统编程手册》61.13.3-传递文件描述符,此处不做赘述)。
用O_CLOEXEC进行的一次尝试
关于O_CLOEXEC模式打开的文件描述符,我也不做什么过多解释了,大家只要知道进程调用exec时会关闭O_CLOEXEC模式打开的文件描述符就行了。
接下来,只不过是我在理清了上面的问题后做的一个无聊尝试罢了。
如果我们在父进程中把stdout设为O_CLOEXEC,fork出的子进程exec后还能正常输出吗?
我用这两段不怎么规范的垃圾代码试了一下:
//test1.c
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main(void){
fcntl(STDOUT_FILENO, F_SETFD, FD_CLOEXEC);
printf("test1\n");
int stat;
int pid = fork();
if (pid == 0){
execv("./test2", NULL);
}
else{
printf("father\n");
pid = wait(&stat);
}
return 0;
}
//test2.c
#include <stdio.h>
int main(void){
printf("test2\n");
return 0;
}
事实证明,确实不能正常输出了。
所以这个探究有啥意义吗?好像没有诶,不过为啥一定要有意义才去探究呢?
同样重要的细节补充
对了,顺便一提,假如按我关于文件描述符与打开文件表的说法,test1设置了O_CLOEXEC
状态后,理论上也会影响到test1的父进程shell,后续该终端的子进程应该都没法输出了,但事实上并没有出现预想中的情况,该终端的子进程依旧可以正常输出,其stdout的O_CLOEXEC
状态并没有被设置,为啥?
《Linux/Unix系统编程手册》中做出了提示:O_CLOEXEC
标志为进程和文件描述符所私有,对其修改不会影响到同一进程或不同进程中的其他文件描述符。
就是这样。
Last modified on 2021-02-07