哈尔滨网站制作建设多少钱,仿抖音网站开发,全国企业信用公示信息公示网官网,wordpress quizzin我在上篇博客留下了一个问题#xff0c;那个问题就是关于缓冲区的问题#xff0c;我们发现
文件有缓冲区#xff0c;语言有用户级缓冲区#xff0c;那么缓冲区到底是什么#xff1f;#xff0c;或者该怎
么认识缓冲区#xff1f;这篇文章或许会让你有所认识#xff0c;…我在上篇博客留下了一个问题那个问题就是关于缓冲区的问题我们发现
文件有缓冲区语言有用户级缓冲区那么缓冲区到底是什么或者该怎
么认识缓冲区这篇文章或许会让你有所认识并且在此之前我还会介绍文
件的结构体对象中的成员。文章目录 标准错误文件结构体 1. 缓冲区a. 引入b. 缓冲区存在的意义c. 产生的问题d. C语言提供的缓冲区 2. 尝试实现一个用户缓冲区 但是在此之前我要补充两个知识点一个是为什么要有标准错误而且标准错误也是指向屏幕。还有就是介绍内核数据结构文件结构体。
标准错误
很多人都知道一个进程会默认打开三个文件标准输入标准输出标准错误其中标准输入指键盘标准输出和标准错误都是指屏幕。我们对标准输入和标准输出很熟悉基本上每个人都使用过但是标准错误我们可能没有用过那么它是什么以及为什么要有它 它是一个文件其实正如它的名字而言它是记录进程错误信息的文件那么这个文件可以是屏幕那自然也可以是一个文本文件。像我们使用过的perror就是输出到标准错误 只是我们的默认标准错误指向屏幕而已当我们把标准错误的指向修改后 这后面的Sucess是C语言中错误码对应的错误信息当我们修改错误码之后 所以标准错误可以帮助我们在进行大型的工程的时候通过改变标准错误指向的方式将错误信息写入到特定文本文件以等待后续的处理而正常的信息则是正常写到自己的目标文件屏幕里互不干扰。
文件结构体
我们说当一个文件被打开时操作系统会创建一个结构体来描述这个文件那现在我们就来简单认识一下该结构体中的一些成员 其中f_list是用来链接系统中被打开文件的。 f_count是有多少个进程打开了文件 f_flags是文件的打开方式 f_mode是文件的权限 f_fowner是说明文件是谁打开的 f_pos表示文件的读写位置 f_mapping跟文件缓冲区有关 f_op是关于对文件操作的操作集
1. 缓冲区
我们接下来的缓冲区不做特殊说明说的都是用户级别的缓冲区
a. 引入
我们现在再回顾这个问题 我们看到有没有fflush会产生出不同的结果但是为什么第一份代码中为什么aaaaaaaa没有输入到log.txt文件中呢这就是我们今天要说的缓冲区。这是因为write是系统调用它写入内容到一号文件时是直接写入到文件缓冲区中而printf是C标准库提供的函数它会先将内容输入到C语言提供的缓冲区中但是我们知道C语言缓冲区中的内容不做处理的话是直到程序运行结束的时候才会刷新而我们在程序结束之前就将log.txt文件关闭了那缓冲区中的aaaaaaaaa就被释放了。我们也平常会说斜杠n刷新缓冲区但是缓冲区到底是什么呢其实缓冲区就是平台C语言、操作系统提供的一块内存而已。
b. 缓冲区存在的意义
那么缓冲区存在的意义是什么呢我们直接将内容打印到屏幕上不可以吗当然是可以的屏幕也是文件当我们使用系统调用的时候会越过C语言提供的缓冲区直接写到屏幕上。 首先我们要对文件进行写入本质上其实是对硬件的写入那么假如当我们高频的对硬件进行读写的时候那势必会将我们程序的运行速度减慢那么这时侯缓冲区的作用就来了我们可以先将对文件的读写操作先写入到这个缓冲区中然后就不用管它了而这个缓冲区只需要以一种特定的触发方式当触发之后再将内容写入到文件中。这么做的话就会分担我们程序的压力从而提高我们程序的效率。写入文件的动作是必须做的但是看的就是调取硬件的频率。 并且缓冲区中的触发机制是缓冲区中的数据积累到一定量的时候就会触发从而向文件中写入不需要多次向文件中写入那么这也帮我们降低了写入文件的成本从而变相的提高了我们写入文件的效率。 其中的触发机制就是我们的缓冲区的刷新机制而缓冲区的刷新机制一般有 即时缓冲立即刷新、行缓冲行刷新、全缓冲缓冲区满了再刷新。 当然也会有特殊情况 强制刷新fflush、进程退出的时候自动刷新。 而对于硬件而言一般来说 显示器采用的是行刷新磁盘采用的是全刷新
c. 产生的问题
我们会有这样的一份代码 我们看到当程序正常运行的时候结果是符合我们预期的但是当它重定向到文件的时候文件中的内容不符合我们的预期。观察它会发现C标准库的函数打印的内容打印了两份而系统调用只打印了一份且顺序有所变化。 那么接下来我就带大家分析其中的原因 程序正常运行我们就不说了我们只说重定向的问题。 当我们重定向程序打印的内容到文件的时候其实有一个东西偷偷的改变了那就是缓冲区的刷新机制由屏幕缓冲区的刷新到硬盘的刷新机制而这也是行缓冲到全缓冲的变化。全缓冲缓冲区是很大的意味着不强制刷新的情况下这一点内容是只有进程结束的时候才会刷新缓冲区内容要注意我们在程序结束之前可是创建了一个子进程父子进程是共享代码数据的现在就要有一个认识用户级缓冲区也是属于进程的一份数据那么缓冲区中的数据父子共享。所以无论父子进程哪个进程先退出都会将自己缓冲区的内容刷新到文件中那缓冲的内容是共享的啊其中一个刷走之后另一个就没有了不合理所以这里会发生写时拷贝使另一个没有退出的进程仍然享有缓冲区的数据那这时候该进程也退出了也要刷新缓冲区了这时候会再次向文件中写入数据而这就是导致重定向的时候C标准库的函数打印了两次的原因那为什么系统调用没有打印两次呢那是因为系统调用是直接向文件中写入的没有经过C语言的缓冲区也就不是我们程序的数据它已经是操作系统的数据了不触发写时拷贝所以就打印一次。 还有是顺序问题这个很简单 对于直接运行程序由于是输出到屏幕每一个输出的内容又都有斜杠n所以打印的内容都是即时刷新到文件中了。 对于重定向由于是全缓冲系统调用不管直接刷到文件里了而C标准库的函数还在C语言提供的缓冲区里。所以才会导致顺序发生变化。 而上面的向文件中写入也只是先写入到文件缓冲区中然后由操作系统来根据自己的刷新缓冲区的触发机制来刷新缓冲区。 还有一个动词我们要明确什么是刷新 刷新就是将缓冲区的内容写入到目的地的过程比如将C语言提供的缓冲区中的内容写到文件缓冲区中又或者是操作系统将文件缓冲区中的内容写入到磁盘文件中。 而C语言提供的缓冲区这种我们一般就叫做用户缓冲区。 上面说的文件缓冲区是属于操作系统的属于内核缓冲区
d. C语言提供的缓冲区
那么如果上面说的C语言给我们提供了缓冲区的话它在哪里呢 我们观察C标准库的关于文件操作的接口就会发现 这个缓冲区其实就在FILE结构体里
可以看到C标准库中的FILE确实是维护着一段空间。
2. 尝试实现一个用户缓冲区
经过上面的探究我们也试着写出一个属于自己的简单的用户级别的缓冲区用户级别的缓冲区肯定是封装着系统调用。 头文件
#pragma oncetypedef struct myFILE
{int _fileno; //存储fdchar _buffer[1024]; //用户缓冲区int _end;//缓冲区的最后一个元素的后一个元素的下标
}myFILE;extern myFILE* my_fopen(const char* path, const char* mode);
extern int my_fputs(const char* s, myFILE* stream);
extern int my_fflush(myFILE* stream);
extern int my_fclose(myFILE* fp);原文件
#include mylib.h
#include string.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#include unistd.h
#include errno.h
#include stdlib.hmyFILE* my_fopen(const char* path, const char* mode)
{int flags 0;if(strcmp(mode, r) 0){flags | O_RDONLY;}else if(strcmp(mode, w) 0){flags | (O_CREAT|O_WRONLY|O_TRUNC);}else if(strcmp(mode, a) 0){flags | (O_CREAT|O_WRONLY|O_APPEND);}else{}int fd 0;if(flags O_RDONLY)fd open(path, flags);else{fd open(path, flags, 0666);}if(fd 0){errno 2;return NULL;}myFILE* fp (myFILE*)malloc(sizeof(myFILE));if(fp NULL){errno 3;return NULL;}fp-_end 0;fp-_fileno fd;return fp;
}int my_fputs(const char* s, myFILE* stream)
{memcpy(stream-_buffer stream-_end, s, strlen(s));stream-_end strlen(s);if(stream-_buffer[stream-_end - 1] \n){my_fflush(stream);}return strlen(s);
}int my_fflush(myFILE* stream)
{if(stream-_end 0);write(stream-_fileno, stream-_buffer, stream-_end);stream-_end 0;return 1;
}int my_fclose(myFILE* fp)
{my_fflush(fp);close(fp-_fileno);return fp-_fileno;
}
#include unistd.h
#include mylib.hint main()
{myFILE* fp my_fopen(log.txt, w);const char* str hello world\n;int i 0;for(i 0; i 20; i){ my_fputs(str, fp);sleep(1);}my_fclose(fp);return 0;
}