感谢你的阅读,是对我最大的鼓励!!!!
目录
fd理解
文件操作重定向
让我们回顾C语言文件操作
首选我们要知道2个知识点:
额外知识点
如何理解一切皆文件呢?
当父进程fork创建子进程是否创建文件。
fd理解
在操作系统内核中,进程PCB还管理着一个结构体files_struct。这个结构体管理着,该进程打开的文件,一般情况下:进程:文件=1:n的比例,既然如此我们就需要将这多个文件管理起来。
文件加载到内存中,会形成一个叫做file的结构体。
同样的一个进程可以打开多个文件,这些打开的文件以某种数据结构,关联在一起。
而我们的进程中有个叫做files_struct 的结构体,他的里面有一个文件指针数组成员变量,每一个元素保存的都是对应文件的地址。
而我们的fd其实就是files_struct中数组下标数字。
文件操作就是,拿着fd数组下标,经过PCB中files_struct指针找到files_struct结构体,然后根据就是根据fd下标索引到数组中找到相应操作的文件地址,然后通过文件地址找到需要操作的file结构体。
文件操作重定向
让我们回顾C语言文件操作
//......
FILE*fp=fopen("./test.txt","w");
//......
fclose(fp);
看似平平无奇的一句话,现在来看,我们理解FILE*是指针,但是什么是FILE吗?为什么fp就可以操作文件了呢?提前close会怎么样呢?
记住,所有的语言其实都是在操作系统,系统语言上的接口,所以fopen、fclose其实只是在系统接口open、close上的封装。而FILE是一个结构体,那么理解fd的话,我们就立刻知道,FILE这个结构体中一定要包含一份数字,这个数字一定是为了在files_struct结构体的数组中寻找相关的文件下标。
FILE是C语言文件操作所需要的结构体,该结构体中一定有,一份数字为了操作在底层files_struct结构体中指针数组成员寻找相应的file结构体。
那么重定向是什么意思呢?
比如以下字符串
//C
FILE*fp=fopen("./test.txt","w");
printf("holle world\n");
该字符串原本经过printf函数要打印在显示屏上,经过了流重定向打印到了test.txt文件中。
这是个什么原理呢?
首选我们要知道2个知识点:
1、程序会默认打开标准输入、标准输出、标准错误的三个流、对应在C语言的就是stdin stdout stderr,这仨其实就是FILE*指针。
2、files_struct中的数组会顺序存储打开的文件,意思就是原本fd=0(标准输入)、1(标准输..出)、2(标准错误)、3(文件1)、4(文件2)...如果前一个文件关闭会清空files_struct 数组的数据,但是数组不会数据前移,再打开一个文件就会加载到该位置。
让我们来画图理解重定向的原理
下面是我们files_struct结构体中数组与file结构体的链接。
现在我们关闭file2(标准输出),将关闭键盘输入操作。没有打开新文件我们就会使下标1的内存就会被置空,不连接任何文件。
这个时候将一个文件载入到进程,在文件file结构体之间也会有着链接关系,新打开的文件会增到这个数据结构中。
操作系统会让打开文件的进程的内核数据结构files_struct的数组存储打开的文件存储在内存的地址,而我们存储的方式是按顺序低到高寻找是否有未被使用的低下标元素,这时候发现1下标空间未被使用,这时候就会存储该文件的地址,完成进程与打开文件的联系。
操作验证(关闭标准输出,然后打开文件,然后向stdout流写入文件)
关闭fd=1(标准输出)使用3个函数向标准输出打印字符串。
--------
首先查看log.txt无任何字符串
---------
运行程序,没有在屏幕打印数据
---------
字符串流向了log.txt中
这就是我们所谓的重定向。是不是很简单(简单个p)
额外知识点
如何理解一切皆文件呢?
其实这个概念是在打开的文件构成一个数据结构层面的概念。
我们的一切皆文件就是站在vfs层理解这句话的,为什么呢,由于每个硬件的读写操作都是不一样的,但是我们都要同一的管理起来,这个时候就见他们的操作方式的地址加载到file结构体中,每个结构体都有着不同硬件,软件,文件的读写方式,我们在vfs一视同仁的认为他们都在vfs层都是叫做文件。这就是我们一切皆文件的概念。
当父进程fork创建子进程是否创建文件。
不会的。子进程会有着独立的进程空间files_struct是父进程继承,但是文件是多个进程共享的,不会因为子进程的创建,而复制一份文件。
有一点子进程会继承父进程打开的文件地址,所以子进程也可以访问到和父进程相同的文件。如果父进程重定向了标准输入,标准输出等等映射关系,我们的子进程也会重定向。
因为子进程是父进程的拷贝,这属于一种深拷贝中的浅拷贝(可以浏览我另一篇文章:vector【实现】:迭代器失效以及非法的间接寻址、深拷贝中的浅拷贝。_云的小站的博客-CSDN博客)
感谢你的阅读,是对我最大的鼓励!!!!
fd的本质是内核当中进程和打开文件对应关系的数组的下标
如何理解一切皆文件呢?:文件在系统层面有一个vfs的虚拟文件系统当中会包含每一个被打开文件的结构体struct file ,这个结构体有一批函数指针,这批函数指针直接帮我们指向底层方法。所以在上层我们可以以同一的视角struct file的方式看待所有文件 所以一切皆文件是在vfs层的看待文件,而不是在硬件看待。
linux task_struct->files_struct->(vfs层)file(struct结构体)->驱动层
echo ”holle world“ > log.txt 就是echo的的fd:1 关闭然后再打开log.txt文件,完成了输出重定向
fd
当fork创建子进程时候进程数据会拷贝,但是文件数据不拷贝,所以子父进程的files_struct 都是指向相同的file结构体
如果父进程曾经改变了
file结构体中有一个 int cnt 引用计数 :当有一个进程指向该文件cnt++ 一个进程close该文件cnt-- 当cnt==0 文件退出内存释放空间