Linux——文件描述符(fd)与重定向、dup/dup2
目录
一.文件描述符
(一).含义
文件描述符(file descriptor)简称为fd,其本质就是一个数组下标
当创建一个进程时,操作系统不仅会创建task_struct结构体(PCB进程控制块),还会创建一个files_struct结构体用于表示该进程打开的相关文件。进程控制块中有一个指针会指向该files_struct。
在files_struct结构体内部,有一个结构体指针数组fd_array。fd_array是一个数组,里面存放的是指针,每一个指针都指向一个file结构体。
文件描述符fd就是fd_array的数组下标。
(二).使用
以c语言fwrite函数为例,fwrite内部通过FILE结构体找到文件描述符fd,同时调用系统接口write。
操作系统根据调用的write的进程,通过task_struct结构体内部指针,找到files_struct,再通过files_struct内的fd_array指针数组,依照write传入的具体fd值(下标),找到对应的file结构体,就找到了相关文件。
图例如下:
当进程打开一个文件时,系统就会在fd_array内部从0下标开始依次寻找尚未指向具体file的指针(空指针),将该文件的file结构体给这个指针。
关闭一个文件时,fd_array中相关指针会指向空,当再其他文件打开时,按照从0开始寻找空指针的方式,找到该空指针,让其指向新的文件。
(三).标准输入/输出/错误
名称 | 硬件 | FILE* | fd |
---|---|---|---|
标准输入 | 键盘 | stdin | 0 |
标准输出 | 显示器 | stdout | 1 |
标准错误 | 显示器 | stderr | 2 |
以C语言为例,<stdio.h>头文件内部已经提前打开了三个文件(两个硬件:键盘、显示器)。
即标准输入、标准输出、标准错误。依照fd_array对文件描述符的分配规则,从0下标开始依次指向具体的file。这也就是为什么标准输入、标准输出、标准错误的fd默认为0、1、2。
所以,下面代码也就很好理解了:
int main()
{
const char* str = "hello world\n";
fwrite(str, strlen(str), 1, stdout);
return 0;
}
stdout是<stdio.h>头文件定义的表示显示器的FILE指针,指针指向的FILE结构体内部fd值为1,因此fwrite直接通过stdout这个指针获得相关fd,进而使用显示器。
二.重定向与dup/dup2
有了对于文件描述符的理解, 重定向的概念就很好理解了。
(一).dup/dup2
这种替换可以通过系统接口dup2完成。
不管是dup还是dup2因为是系统接口,其参数均是文件描述符fd。
dup可以返回一个新的fd来表示传入的oldfd所代表的文件,即赋予该文件另一个fd。
dup2可以将oldfd所代表的文件给newfd,如果两个fd所代表的文件本就相等,那将不做任何改动。
dup2在使用时常常容易搞错复制方向,可以这样理解:dup2(copy from, copy to)
如果调用失败,均返回-1。
(二).重定向
重定向本质是使fd_array中指向原文件的指针指向目标文件。
以标准输出重定向为例:
[cdl@VM-16-9-centos ~]$ echo "hello world" > file.txt
本质就是将fd_array中原本指向显示器file的指针指向file.txt的file结构体,即:
FILE* file = fopen("./file.txt", ...);
dup2(file._fileno, 1);
echo还是将数据给fd为1的指针指向的文件,但因为fd=1的指针指向了file.txt(的file结构体),因此echo把数据给了file.txt。
图示如下:
追加重定向就是在打开file.txt文件时就赋予文件追加属性,相当于这样:
//系统open接口,O_TRUNC:按追加方式打开文件
int fd = open("./file.txt", O_CREAT | O_RDWR | O_TRUNC);
愚者困惑,智者提问——Benjamin Disraeli
如有错误,敬请斧正