网站建设费包括什么,安徽做网站哪家好,网站开发流程介绍,怎么把网站列入黑名单文章目录 一、先看现象二、用户缓冲区的引入三、用户缓冲区的刷新策略四、为什么要有用户缓冲区五、现象解释六、结语 一、先看现象
#include stdio.h
#include string.h
#include unistd.hint main()
{const char* fstr Hello fwrite\n… 文章目录 一、先看现象二、用户缓冲区的引入三、用户缓冲区的刷新策略四、为什么要有用户缓冲区五、现象解释六、结语 一、先看现象
#include stdio.h
#include string.h
#include unistd.hint main()
{const char* fstr Hello fwrite\n;const char* str Hello write\n;printf(Hello printf\n);fprintf(stdout, Hello fprintf\n);fwrite(fstr, strlen(fstr), 1, stdout); // 返回值是写入成功的快数write(1, str, strlen(str)); // 返回值是写入成功的字节数// fork();return 0;
}结构分析带 fork 的输出重定向最终把有一些内容向 log.txt 文件中写入了多次并且打印顺序也有所不同。
int main()
{const char* fstr Hello fwrite;const char* str Hello write;printf(Hello printf);fprintf(stdout, Hello fprintf);fwrite(fstr, strlen(fstr), 1, stdout); // 返回值是写入成功的快数close(1);// write(1, str, strlen(str)); // 返回值是写入成功的字节数// fork();return 0;
}结果分析代码中只使用了库函数向显示器中进行写入并且在字符串的结尾没有加 \n在最后面将标准输出对应的文件描述符进行了关闭最终显示器上什么也没有。上一段代码在字符串的结尾加上了 \n 最终字符串被成功的打印到了屏幕上。
int main()
{const char* str Hello write;write(1, str, strlen(str)); // 返回值是写入成功的字节数close(1);return 0;
}结果分析字符串的结尾依然不加 \n但是这一次采用系统调用接口最后仍然将标准输出对应的文件描述符进行关闭这一次字符串被成功的打印了出来。
二、用户缓冲区的引入
write 为什么能将不带 \n 的字符串写入到显示器文件中。首先我们需要明确一点进程打开的每一个文件都有一个属于自己的操作系统级别的文件缓冲区该缓冲区的存在可以减少对外设的读写操作以提高计算机的效率。举个栗子在一个进程中向磁盘里的同一个文件进多次行写入文件缓冲区的存在可以将每次写入的内容先存储在文件缓冲区中最后在程序退出或者调用 close 的时候一次性将文件缓冲区中的所有内容刷新到磁盘。如果没有该文件缓冲区那在进程里对文件进行 n 次写操做就要对应 n 次向磁盘的写操作CPU 和外设之间是存在非常大的速度差的这样效率会非常低。
write 作为系统调用接口它就是直接向文件缓冲区中写入最后在调用 close 接口或者程序退出的时候会将文件缓冲区的内容刷新到对应的外设中。
printf、fprintf、fwrite 底层一定是封装了 write 系统调用接口那为什么使用 write 系统调用接口就可以将字符串写入到显示器使用 C 库函数没能把字符串写入到显示器文件原因在进度条的那篇文章中讲过我们使用的这些 C 库函数是把字符串写入到了缓冲区中这个缓冲区和上面的文件缓冲区有所不同这里说的缓冲区是 C 语言给我们提供的语言层面的缓冲区也叫做用户级缓冲区\n 具有刷新用户级缓冲区的作用因此不加 \n 并且在程序结束前将显示器对应的文件描述符进行了关闭最终就导致字符串在用户级缓冲区中没有被刷新到文件缓冲区所以屏幕上就什么也没有。这里我们可以肯定在这些 C 库函数中并不是立即调用 write 接口而是在遇到 \n 后才去调用 write 接口将用户缓冲区的内容刷新到文件缓冲区中。 总结使用 C 系统调用接口向文件中写入写入的内容先被存储在用户缓冲区中在合适的时候遇到 \n才会进行刷新这里刷新的本质是调用 write 将数据从用户缓冲区写入内核。
之前说的 exit 会刷新缓冲区其实就是刷新用户缓冲区因为 exit 作为 C 库函数可以看见用户缓冲区而 _exit 作为系统调用接口无法看到语言层面的用户缓冲区因此也就无法刷新用户缓冲区。
三、用户缓冲区的刷新策略 无缓冲直接刷新数据不在用户缓冲区中停留。 行缓冲不刷新直到碰到 \n。 全缓冲缓冲区满了才刷新。
所谓刷新就是调用 write 接口将数据写入操作系统中的文件缓冲区。显示器文件对应采用的就是行缓冲向磁盘文件中写入采用的是全缓冲。进程在退出的时候也会刷新用户缓冲区还可以调用 fflush 进行刷新。
四、为什么要有用户缓冲区 解决效率问题缓冲区就像菜鸟驿站不需要我们自己坐火车坐飞机去送东西而是直接交给菜鸟驿站然后就可以干自己的事情了菜鸟驿站可以选择攒上一大批快递然后统一寄送出去。用户缓冲区的存在本质上提高了 C 语言的效率也就是提高了用户的效率因为 C 语言是程序员在使用在使用 C 库函数进行文件写入时大部分情况只需要把数据交给缓冲区然后就可以快速的返回不需要每一次都亲力亲为的去和操作系统打交道。 配合格式化有些和文件写入相关的 C 库函数是格式化输出函数在我们看来它可以写入整形、符点型但是最终都是以字符串的形式进行写入。格式化就是将类型全都转化成字符串先写入到用户缓冲区用户缓冲区中存的一定都是字符串。
用户缓冲区有进也有出将数据写入到用户缓冲区中就就叫做进将用户缓冲区中的数据刷新到内核中的文件缓冲区中被刷新的数据就可以从用户缓冲区中删掉这就叫做出。用户缓冲就像就像水流一样源源不断流的概念就是因此而来。
小TipsFILE 里面就有对应打开文件的缓冲区字段和维护信息。每个被进程打开文件都有自己对应的文件缓冲区。FILE 对象属于用户用户缓冲区可以看作是在堆上申请的一块空间。
五、现象解释
这下再来解释上面代码中有 fork 然后重定向写入了多次的原因。首先重定向后将本来向显示器文件写入的内容写到了磁盘文件显示器文件的缓冲区采用行缓冲即遇到 \n 就会刷新而磁盘文件采用的是全缓冲当缓冲区满了才刷新。因此在重定向后会把三条 C 库函数写入的内容全部保存到缓冲区中然后调用 fork 创建子进程此时父子进程代码共享数据写时拷贝在程序退出的时候回去刷新用户缓冲区上面说过刷新就是将用户缓冲区中的数据写入到内核然后将用户缓冲区中的内容清空上面还说过缓冲区就是在堆上申请的一段空间可以看作数据部分因为要删除数据所以就会进行写时拷贝此时之前父进程用户缓冲区中的内容就会给子进程拷贝一份然后父子进程都执行刷新动作各自刷新自己的缓冲区数据这就是为什么最终出现多份的原因。没有重定向只向显示器打印四条消息是因为显示器采用的是行刷新策略在调用 fork 前对应的字符串就已经被刷新出去了。在 fork 的时候父进程的用户缓冲区中是空的什么也没有。
磁盘文件全缓冲验证
int main()
{const char* fstr Hello fwrite\n;const char* str Hello write\n;printf(Hello printf\n);sleep(2);fprintf(stdout, Hello fprintf\n);sleep(2);fwrite(fstr, strlen(fstr), 1, stdout); // 返回值是写入成功的快数sleep(2);write(1, str, strlen(str)); // 返回值是写入成功的字节数sleep(5);fork();return 0;
}分析最先将 write 内容写入到文件中因为它是直接写入到文件缓冲区而剩下的 C 库函数对应的内容是统一一次全部刷新到内核即使每个字符串后面都有 \n但最后还是统一全部刷新这就证明了磁盘文件采用的是全刷新策略。
六、结语
今天的分享到这里就结束啦如果觉得文章还不错的话可以三连支持一下春人的主页还有很多有趣的文章欢迎小伙伴们前去点评您的支持就是春人前进的动力