
我们可以看到,这三大数据流是来自于C标准库的,类型又是一个我们从来没有听说过的FILE类型。我们可以预料就是,FILE类型应该是C语言库函数内自己封装的一个函数结构体。


我们看到这个数组的下标模式,也大概对文件的管理形式有了一定的猜测。
就是如下图这种管理模式

其实上一切皆文件,就是封装的结果,利用形式上完全一模一样的结构体对各种硬件进行描述,让其截然不同的操作封装硬件提供的接口上(读写。。操作,每一个硬件都封装了这样的函数)通过,函数指针指向接口,其中的返回值,函数指针名等都一致,只是函数的底层实现不一样罢了。这样底层的不一致就被屏蔽了,在我们使用者看来就是一切皆文件了,也就是一切皆struct file


fd分配规则:最小的没有使用的数组下标会分配给刚刚打开的文件。


看到这里,是不是很奇怪,明明printf()是向显示器写入的为什么会对log.txt写入呢?

这数组的前三个在操作系统打开的时候,加载进来了,而printf()函数,默认就是向该数组中fd为1的文件执行写入操作,我们把fd==1的文件关了,重新加载了一个文件进去,按照分配规则,那么那个文件就该被分配为fd==1.


功能:就是将新的文件描述符变成旧的文件描述符的一份拷贝拷贝。

int main()
79 {
80 int fd=open("log.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);
81
82 dup2(fd,1);
83 printf("hello Linux\n");
84
85 close(fd);
86
87 }
本质是一块内存区域,用于暂时存放数据,以便更高效地处理输入、输出操作。
缓冲区也会为格式化输入、输出操作提高场所。
提高使用者的效率
提高计算机整体的拷贝效率。
一、无缓冲、无刷新
无缓冲:无刷新,意味着数据不会暂存在缓冲区中,而是立即被写入到目标设备中。
适用场景:需要立即看到结果、实时性要求很高的场景,如:实时系统、设备驱动程序。
优点:保证了数据的即时可见性。
缺点:性能下降,频繁的使用系统调用会增加开销。
二、全缓冲、全刷新
全缓冲:全刷新,缓冲区满了或者关闭文件时,缓冲区的数据才会被刷新到目的设备中。
适用场景:文件的读写操作,尤其是大文件。
优点:减少了系统调用的次数,提高了性能。
缺点:可能会丢失数据,如:在缓冲区的数据未被刷新前,发生崩溃,则这部分的数据就会丢失。
三、行缓冲、行刷新
行缓冲:行刷新,意味着遇到换行符\n,缓冲区的数据就会被立即刷新到目的设备中。
适用场景:标准输入输出(显示器)。
注:当调用c语言接口fflush(),进行强制刷新; 进程退出时,或文件关闭时,自动刷新
fwrite等标准库函数,会先将数据拷贝到缓冲区中,然后根据一定的条件,调用系统调用接口进行刷新。
文件操作的系统调用接口,其实是个拷贝函数,它将数据从语言层的缓冲区拷贝到内存的缓冲区。
int main()
80 {
81
87
88 const char * s1="hello write\n";
89
90 write(1,s1,strlen(s1));
91
92 const char * s2="hello fprintf\n";
93 fprintf(stdout,"%s",s2);
94
95 const char * s3="hello fwrite\n";
96 fwrite(s3 ,strlen(s3),1,stdout);
97
98 fork();
99
return 0;
}
现象1解释:write()为系统调用接口,直接将数据写入到内核中;fprintf、fwrite为库函数,先将数据写入到缓冲区中,因为它们都是向显示器进行写入,而写入显示器是行刷新(遇到换行符\n,进行刷新),所以fork创建子进程前缓冲区中的数据全部被刷新到内核中了。
刷新到内核的数据,不属于进程的数据;存放在缓冲区中的数据,属于进程的数据。

现象2解释:重定向到普通文件时,数据刷新缓冲区的方式,由行缓存变为全缓冲,C语言接口自带缓冲区,所以它会将数据写入到缓冲区中,就不会立即刷新。fork创建子进程,父子共享缓冲区的数据,但是进程退出后,统一进行刷新。刷新缓冲区,是清空缓冲区,是修改数据的一种方式,所以父子进程的数据会发生写时拷贝,父子进程分别刷新各自的缓冲区,随即产生两份数据。write是系统调用接口,直接将数据写入到内核中,不存在所谓的缓冲区。