目录
两种缓冲区的说明
用户级缓冲区:
数据如何从用户级缓冲区——>文件内核缓冲区?
刷新策略:对于各种文件的
了解了各种知识之后,使用代码来进行更深理解的认识:
Linux🌷
两种缓冲区的说明
先用一张图简单说明下各种缓冲区:
缓冲区分为:用户级缓冲区和文件内核缓冲区两种;
文件是保存在磁盘中的;
如果我们使用C语言/c++等函数调用往文件里写内容时,数据会先被写入至用户级缓冲区;
如果使用wait等系统调用接口往文件中写数据的话,数据直接被写入文件内核缓冲区中;
用户级缓冲区:
printf函数输出到显示器上,是通过stdout进行的,stdout 是文件指针类型,指向一个struct file结构
体,结构体里面封装了一个 fd 和 维护了与C缓冲区相关的内容;
数据如何从用户级缓冲区——>文件内核缓冲区?
在这一步是一定要使用到 fd 的。
1. 进程退出时;
2. fflush()函数;
注意:
close()函数会影响刷新;
刷新策略:对于各种文件的
1. 立即刷新(不缓冲);
2. 行刷新(行缓冲\n),比如:显示器打印;
3. 缓冲区满了才刷新(全缓冲),比如:往磁盘文件中写入;
了解了各种知识之后,使用代码来进行更深理解的认识:
示例一:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
close(1);
int fd = open("./log.txt", O_CREAT|O_WRONLY, 0644);
//在此不做打开失败的判断
printf("fd:%d\n",fd);
fprintf(stdout,"hello linux\n");
fprintf(stdout,"hello linux\n");
fprintf(stdout,"hello linux\n");
close(fd);
return 0;
}
我们发现显示器和文件中都没有内容。
原因:
我们关闭了1号文件标识符,新打开的文件被分配到1号标识符,stdout只认1号标识符;
使用C语言函数 printf 和 fprintf 进行输出,这些是函数调用,输出的内容先保存至用户级缓冲区,因为这些内容是要写入磁盘文件中的,是满了才刷新,这些数据太少达不到刷新的要求,一直保存至用户级缓冲区中,最后将fd关闭,数据刷新不到内核缓冲区中,自然也写不到磁盘文件中。
示例二:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
close(1);
int fd = open("./log.txt", O_CREAT|O_WRONLY, 0644);
//在此不做打开失败的判断
printf("fd:%d\n",fd);
fprintf(stdout,"hello linux\n");
fprintf(stdout,"hello linux\n");
fprintf(stdout,"hello linux\n");
//close(fd);
return 0;
}
与上个代码不同,我们不关闭fd,发现内容输出至磁盘文件中。
原因:
原本数据是存在用户级缓冲区中,return 后进程结束,会将用户级缓冲区的内容刷新至内核缓冲区中,内核缓冲区的数据自动调用一些接口将数据保存在文件中。
我们只需要知道只要能写入内核缓冲区的数据一定会写入磁盘文件中,至于怎么写入我们不必太过关心。
示例三:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main()
{
const char* msg1 = "hello linux\n";
write(1,msg1,strlen(msg1));
printf("hello printf\n");
fprintf(stdout,"hello fprintf\n");
close(1);
return 0;
}
上述write为系统调用接口,直接将数据写入内核缓冲区中;
printf 和 fprintf 是函数调用,将数据先写入用户级缓冲区中;
上述 printf 和 fprintf 函数最后都带了 \n,表示如果是行刷新设备则可刷新至设备中;
我们直接运行程序三个都可输出:
write直接写入内核数据缓冲区;printf 和 fprintf 输出至显示器中遇行刷新至内核缓冲区中;
所以都可以输出;
重定向的话:
三个都是往stdout中写入,按理说都可以写入文件,但出现这种现象;
本质是行刷新——>满刷新;
printf 和 fprintf 输出的内容被存到C语言级缓冲区中,满了才可以刷新,此时数据还是保存在C语言
缓冲区中,将1号文件标识符进行关闭,就算进程退出也不能将数据刷新至内核缓冲区中,因此只
输出一句;
如果不关闭close(1)发现全都可以写入文件:
示例四:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main()
{
const char* msg1 = "hello linux\n";
write(1,msg1,strlen(msg1));
printf("hello printf\n");
fprintf(stdout,"hello fprintf\n");
fork();
return 0;
}
执行 ./test4 出现结果的解释:
write将数据写入到内核缓冲区中,printf 和 fprintf 先写到C语言缓冲区中,因为最终输出到显示器中,遇行则刷新至内核缓冲区中,执行fork,最后进程退出,三个输出都显示到显示器上;
执行 ./test4 > log.txt 出现结果的解释:
write将数据写入到内核缓冲区中,printf 和 fprintf 先写到C语言缓冲区中,进行了重定向,本来输出到显示器的数据,现在要写入磁盘文件中,由行刷新变为满刷新,所以现在最后两个输出数据都存在C语言缓冲区中,执行fork创建了子进程,子进程拥有父进程的所有代码和数据,在子进程退出时将C语言缓冲区中的数据刷新到内核缓冲区中,然后父进程退出,将C语言缓冲区的数据刷新至内核缓冲区中,所以最后两个输出才会在log文件中显示两遍。
坚持打卡!😃