1.内核缓冲区页缓存在操作系统内部文件加载到内存过程中不是直接加载到用户程序内存中的而是先加载到内核缓冲区页缓存再拷贝至用户内存中这样做可以实现权限隔离防止用户程序破坏内核数据。当操作系统需要读取数据时它通过文件描述符找到对应的struct file结构体从中获取当前读写位置的偏移量f_pos并根据f_pos计算出要访问的文件页索引。然后操作系统以inode 页索引为键值在页缓存中进行查找以此判断所需数据是否已经在内存中。如果查找到了说明数据已在内存直接复制到用户空间即可如果未查找到则需要从磁盘读取到页缓存中再复制给用户程序。struct file { struct path f_path; // 文件路径 struct inode *f_inode; // 指向文件元数据大小、权限等 loff_t f_pos; // 当前读写位置偏移量 unsigned int f_flags; // 打开标志O_RDWR, O_NONBLOCK等 atomic_t f_count; // 引用计数多少个 fd 指向它 const struct file_operations *f_op; // 函数指针read/write等 // ... };也就是说read/write本质实际是拷贝函数因为从数据流动的角度来看read 和write的核心工作就是把数据从一处搬运到另一处而且数据本身没有发生任何变化只是复制了一份。内核缓冲区存在的意义分散的IO请求数据聚合在一起通过少量的IO操作进行设备写入提高整机效率缓冲区的刷新策略常规立即刷新行刷新行缓冲一般用与显示器全缓冲写满比如OS普通文件特殊强制刷新进程退出的强制刷新2. 语言缓冲区先看一个现象从中分析不难发现由于向显示器打印是行刷新所以C的库函数打印内容都刷新到了内核缓冲区进程结束后就打印到了显示器上而如果是写入到文件的话由于是全缓冲输出内容都还在语言缓冲区里面没有被写入内核缓冲区fork之后创建的子进程将这部分继承了下来等进程结束后强制刷新导致C相关的库函数打印了两次。实际上printf/fprintf/fputs的本质都是拷贝函数当他们将内容都输出到语言缓冲区后就认为自己的任务完成了而不需要再等到OS做的一些列操作提高了应答效率。所以缓冲区的核心作用是提高IO的应答效率解决的是使用缓冲区的“人”的效率问题。3.手写stdio简易版//my_stdio.h #pragma once typedef struct { int fd; int flags; int mode; // 刷新策略 char outbuffer[1024]; int cap; int size; //char inbuffer[1024]; }My_FILE; #define NONE_CACHE 1 #define LINE_CACHE 2 #define FULL_CACHE 4 My_FILE *Myfopen(const char *pathname, const char *mode); // r w a int Myfwrite(const char *message, int size, int num, My_FILE *fp); void Myfflush(My_FILE *fp); void Myfclose(My_FILE *fp); //my_stdio.c #include my_stdio.h #include string.h #include sys/stat.h #include sys/types.h #include fcntl.h #include stdlib.h #include unistd.h static mode_t gmode 0666; // My_FILE *fp Myopen(log.txt, a); My_FILE* Myfopen(const char* pathname, const char* mode) // r w a { if (pathname NULL || mode NULL) return NULL; umask(0); int fd 0; int flags 0; if (strcmp(mode, w) 0) { flags O_CREAT | O_WRONLY | O_TRUNC; fd open(pathname, flags, gmode); (void)fd; } if (strcmp(mode, r) 0) { flags O_RDONLY; fd open(pathname, flags); (void)fd; } if (strcmp(mode, a) 0) { flags O_CREAT | O_WRONLY | O_APPEND; fd open(pathname, flags, gmode); (void)fd; } else {} if (fd 0) return NULL; //创建my_FILE对象 My_FILE* fp (My_FILE*)malloc(sizeof(My_FILE)); if (!fp) return NULL; fp-fd fd; fp-flags flags; fp-mode LINE_CACHE; fp-cap 1024; fp-size 0; //fp-outbuffer[0] \0; memset(fp-outbuffer, 0, sizeof(fp-outbuffer)); return fp; } int Myfwrite(const char* message, int size, int num, My_FILE* fp) { if (message NULL || fp NULL) return -1; // 向C文件里面写本质是向缓冲区写 int total size * num; if (total fp-size fp-cap - 1) return -1; // 写入 memcpy(fp-outbuffer fp-size, message, total); fp-size total; fp-outbuffer[fp-size] 0; if (fp-outbuffer[fp-size - 1] \n (fp-mode LINE_CACHE)) Myfflush(fp); return num; } void Myfflush(My_FILE* fp) { if (!fp) return; // 判断是否刷新, 这不就是刷新吗 if (fp-size 0) { // 系统调用 // 从用户缓冲区拷贝到内核, WB, Write Back write(fp-fd, fp-outbuffer, fp-size); fp-size 0; // WT, Write Through // 不仅仅要写入到内核缓冲区必须给我写到对应的硬件上 fsync(fp-fd); } } void Myfclose(My_FILE* fp) { if (!fp) return; Myfflush(fp); close(fp-fd); } //main.c #include my_stdio.h #include string.h #include unistd.h int main() { My_FILE *fp Myfopen(log.txt, a); if(!fp) return 1; int cnt 10; const char* msg hello world ; while(cnt--) { Myfwrite(msg, 1, strlen(msg), fp); Myfflush(fp); sleep(2); } Myfclose(fp); return 0; }