FILE
因为IO相关函数与系统调用接口对应,并且库函数封装系统调用,所以本质上,访问文件都是通过文件描述符fd访问的。所以C库当中的FILE结构体内部必定封装了文件描述符fd。
缓冲区分析
当我们调用对文件写操作c库函数(printf、fprintf,fwrite)向显示器文件写入内容,首先是将内容写到FILE的c提供的缓冲区里,刷新方式采取2.遇到\n刷新,我们调用的c库函数就会调用它封装的write,write根据FILE里的fd在内核中找到对应的文件结构体对象里指针指向的缓冲区里;当我们调用系统调用write时,write将内容直接写到被写入文件结构体对应的内核缓冲区里,然后由操作系统将内容传送到磁盘。
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main()
{
const char* str ="linux";
const char* str1 = "OS";
//库函数
fprintf(stdout, "hello");
fwrite(str, strlen(str), 1, stdout);
//系统调用
write(1, str1, strlen(str1));
close(1);
return 0;
}
可以看到close了fd=1的文件(即显示器文件),库函数fprintf、fwrite函数和系统调用write里的要向显示器文件输入的字符串末尾都无\n,但是为什么只有系统调用向显示器文件成功写入了字符串呢?这是因为c库对文件操作函数内部提供了缓冲区。另外,我们这里所说的缓冲区, 都是用户级缓冲区。write系统调用将要写入显示器文件的内容直接写到内核级缓冲区,因此可以打印出write写入的内容;c库函数由于要等到进程退出才可以将内容从用户级缓冲区刷新到内核级缓冲区,但在进程退出前,调用了close(1),关闭了显示器文件,因此显示器文件结构体对应的内核级缓冲区被关闭了,无法向里面刷新内容,也就无法将内容写入到磁盘里的显示器文件。如果要写入的字符串末尾都加了\n(刷新方式为)那么就全部会被写入到显示器文件里,并由显示器打印出来。
过程和向显示器文件写入内容一样,刷新方式采取3.全缓冲。
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main()
{
const char* str ="linux";
const char* str1 = "OS";
//库函数
fprintf(stdout, "hello");
fwrite(str, strlen(str), 1, stdout);
//系统调用
write(1, str1, strlen(str1));
fork();
return 0;
}
为什么c库函数写入的内容写入到text.txt了两遍?
调用库函数向文件text.txt写入文件时刷新方式为3.全缓冲,也就是说只有进程退出时才可以刷新,因为fork()创建了子进程,向内核缓冲区刷新内容时,子进程的内核缓冲区内存空间发生写时拷贝。