缓冲区
背景知识认识
缓冲区就是一部分内存,由谁提供? -----(系统,C语言malloc,用户声明的变量)任何都能提供缓冲区
printf(“hello linux”);
sleep(3);
执行完sleep后才会输出printf,这段期间没有打印的信息就在缓冲区
1.为什么要这样做?
提高使用者的效率(丢给缓冲区就认为是完成了)
比如快递员将包裹放在快递站而不是送到家门口
(使用者) (缓冲区)
2.积累一部分再发送,可以提高效率
刷新策略(方式):
1.无缓冲(立即刷新)
2.行缓冲(行刷新)
3.全缓冲(缓冲区满了,再刷新)
特殊情况:
1.强制刷新
2.进程退出时,一般都要刷新
注:一般的显示文件-----行刷新
磁盘文件-----全刷新
样例及理解
请看两段代码和运行结果
#include<stdio.h>
#include<string.h>
#include<unistd.h>
int main()
{
fprintf(stdout, "fprintf:hello linux\n");
printf("printf:hello linux\n");
fputs("fputs:hello linux\n", stdout);
const char* tmp = "write:hello linux\n";
write(1, tmp, strlen(tmp));
//fork();
return 0;
}
结果:
#include<stdio.h>
#include<string.h>
#include<unistd.h>
int main()
{
fprintf(stdout, "fprintf:hello linux\n");
printf("printf:hello linux\n");
fputs("fputs:hello linux\n", stdout);
const char* tmp = "write:hello linux\n";
write(1, tmp, strlen(tmp));
fork();
return 0;
}
结果:
发现执行子进程后,C语言接口的指令被两次重定向到log.txt
而系统指令的显示只有一次
结论:
1.都是显示文件信息,所以都是文件行刷新,都是字符串文件,都有\n,在fork()之前都会刷新,包括systemcall
2.重定向到(磁盘)文件,是写入到文件,显示方式已经变成全刷新
3.全缓冲意味着缓冲区变大,当前数据是不足以让缓冲区写满,fork()调用的时候,数据仍然在缓冲区
4.现在谈的缓冲区和操作系统没有关系,底层调用的都是系统的write,但是现在这个问题却是跟C语言的函数有关
5.c/c++提供的缓冲区,里面一定保存的是用户的数据,属于当前进程运行时自己的数据.
6.如果把数据交给了OS,这个数据就属于OS,不属于用户
7.当进程退出后,都要进行缓冲区刷新,这个刷新也算写入操作,即在调用fork()时,任意一个进程在退出的时候,刷新缓冲区,就要发生写实拷贝
write系统调用,直接写入操作系统,不属于进程,也不发生写实拷贝.
总结:什么叫做刷新?
对于一个字符语句要写入文件时,将数据从语言写入操作系统
将数据通过语言直接写入OS代价高,效率低,引入缓冲区就能提高效率
从C语言至今,了解的缓冲区概念都是语言级别的缓冲区,即用户缓冲区
刷新就是把数据从语言缓冲区拷贝到操作系统的文件缓冲区中
所以:
样例中,C语言的打印信息出现两次是由于父子进程发生写实拷贝,父进程创建子进程时,会将数据拷贝给子进程,这样就会有两次打印的数据,而系统的打印是直接将数据传给文件缓冲
用户缓冲区,VS内核缓冲区
上述的缓冲区是c/c++语言提供的语言缓冲区(用户缓冲区)
怎么验证?这个缓冲区在哪里?
观察所有的C语言提供的文件操作都离不开FILE*这个结构体指针
这个FILE里面包含了fd,它里面也有一段缓冲区,即任何一个用C语言接口操作的文件都会被C语言提供一个语言缓冲区
.h文件中有声明
所以printf就能实现格式化输出scanf 格式化输入
键盘输入其实都是字符 %c %d是将数据格式化到需要的类型变量中
所以键盘,字符串这些叫做字符设备
C库函数的模拟实现
myFILE结构体的模拟实现
#pragma once
#define SIZE 4096
#define FLUSH_NONE 1
#define FLUSH_LINE (1<<1)
#define FLUSH_ALL (1<<2)
typedef struct _myFILE
{
int fileno;
int flag;
char buffer[SIZE];
int end;
}myFILE;
extern myFILE *my_fopen(const char *path, const char *mode);
extern int my_fwrite(const char *s, int num, myFILE *stream);
extern int my_fflush(myFILE *stream);
extern int my_fclose(myFILE*stream);
my_fopen等函数的模拟实现
#include "mystdio.h"
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#define DFL_MODE 0666
myFILE *my_fopen(const char *path, const char *mode)
{
int fd = 0;
int flag = 0;
if(strcmp(mode, "r") == 0)
{
flag |= O_RDONLY;
}
else if(strcmp(mode, "w") == 0)
{
flag |= (O_CREAT | O_TRUNC | O_WRONLY);
}
else if(strcmp(mode, "a") == 0)
{
flag |= (O_CREAT | O_WRONLY | O_APPEND);
}
else{
// Do Nothing
}
if(flag & O_CREAT)
{
fd = open(path, flag, DFL_MODE);
}
else
{
fd = open(path, flag);
}
if(fd < 0)
{
errno = 2;
return NULL;
}
myFILE *fp = (myFILE*)malloc(sizeof(myFILE));
if(!fp)
{
errno = 3;
return NULL;
}
fp->flag = FLUSH_LINE;
fp->end = 0;
fp->fileno = fd;
return fp;
}
int my_fwrite(const char *s, int num, myFILE *stream)
{
// 写入
memcpy(stream->buffer+stream->end, s, num);
stream->end += num;
// 判断是否需要刷新, "abcd\nefgh"
if((stream->flag & FLUSH_LINE) && stream->end > 0 && stream->buffer[stream->end-1] == '\n')
{
my_fflush(stream);
}
return num;
}
int my_fflush(myFILE *stream)
{
if(stream->end > 0)
{
write(stream->fileno, stream->buffer, stream->end);
//fsync(stream->fileno);
stream->end = 0;
}
return 0;
}
int my_fclose(myFILE*stream)
{
my_fflush(stream);
return close(stream->fileno);
}
演示
#include "mystdio.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main()
{
myFILE *fp = my_fopen("./log.txt", "w");
if(fp == NULL)
{
perror("my_fopen");
return 1;
}
int cnt = 20;
const char *msg = "haha, this is my stdio lib";
while(cnt--){
my_fwrite(msg, strlen(msg), fp);
sleep(1);
}
my_fclose(fp);
return 0;
}
欢迎三连,喜欢请留言讨论!!!