首页
最新活动
服务器租用
香港服务器租用
台湾服务器租用
美国服务器租用
日本服务器租用
新加坡服务器租用
高防服务器
香港高防服务器
台湾高防服务器
美国高防服务器
裸金属
香港裸金属服务器
台湾裸金属服务器
美国裸金属服务器
日本裸金属服务器
新加坡裸金属服务器
云服务器
香港云服务器
台湾云服务器
美国云服务器
日本云服务器
CDN
CDN节点
CDN带宽
CDN防御
CDN定制
行业新闻
官方公告
香港服务器资讯
帮助文档
wp博客
zb博客
服务器资讯
联系我们
关于我们
机房介绍
机房托管
登入
注册
帮助文档
专业提供香港服务器、香港云服务器、香港高防服务器租用、香港云主机、台湾服务器、美国服务器、美国云服务器vps租用、韩国高防服务器租用、新加坡服务器、日本服务器租用 一站式全球网络解决方案提供商!专业运营维护IDC数据中心,提供高质量的服务器托管,服务器机房租用,服务器机柜租用,IDC机房机柜租用等服务,稳定、安全、高性能的云端计算服务,实时满足您的多样性业务需求。 香港大带宽稳定可靠,高级工程师提供基于服务器硬件、操作系统、网络、应用环境、安全的免费技术支持。
联系客服
服务器资讯
/
香港服务器租用
/
香港VPS租用
/
香港云服务器
/
美国服务器租用
/
台湾服务器租用
/
日本服务器租用
/
官方公告
/
帮助文档
【Linux】基础IO —— 缓冲区深度剖析
发布时间:2024-02-27 20:43:47 分类:帮助文档
【Linux】基础IO —— 缓冲区深度剖析 🌈欢迎来到Linux专栏~~基础IO (꒪ꇴ꒪(꒪ꇴ꒪ )🐣,我是Scort目前状态:大三非科班啃C++中🌍博客主页:张小姐的猫~江湖背景快上车🚘,握好方向盘跟我有一起打天下嘞!送给自己的一句鸡汤🤔:🔥真正的大师永远怀着一颗学徒的心作者水平很有限,如果发现错误,可在评论区指正,感谢🙏🎉🎉欢迎持续关注! 基础IO 🌈欢迎来到Linux专栏~~基础IO一. 缓冲区🌈缓冲区是什么🌈为什么要引入缓冲器🌈缓冲区的初步认识🌈解疑答惑🥑缓冲区是谁提供的🥑用户级缓冲区在哪里? 🌏设计用户层缓冲区的代码 ~ 实战💢`struct file`的设计💢主函数💢接口实现💢附源码 📢写在最后 一. 缓冲区 🌈缓冲区是什么 💦缓冲区 (buffer),它是内存空间的一部分。 也就是说,在内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或输出的数据,这部分预留的空间就叫做缓冲区,显然缓冲区是具有一定大小的 🌈为什么要引入缓冲器 高速设备与低速设备的不匹配(cpu运算是纳秒,内存是微秒,磁盘是毫秒甚至是秒相差1000倍),势必会让高速设备花时间等待低速设备,我们可以在这两者之间设立一个缓冲区 💥举个例子:(顺丰就是缓冲区) 可以解除两者的制约关系,数据可以直接送往缓冲区,高速设备不用再等待低速设备,提高了计算机的效率可以减少数据的读写次数,如果每次数据只传输一点数据,就需要传送很多次,这样会浪费很多时间,因为开始读写与终止读写所需要的时间很长,如果将数据送往缓冲区,待缓冲区满后再进行传送会大大减少读写次数,这样就可以节省很多时间。例如:我们想将数据写入到磁盘中,不是立马将数据写到磁盘中,而是先输入缓冲区中,当缓冲区满了以后,再将数据写入到磁盘中,这样就可以减少磁盘的读写次数,不然磁盘很容易坏掉 总的来说: 🌈缓冲区的初步认识 ⚡缓冲区刷新策略!(一般+特殊) 立即刷新行刷新(行缓冲) \n满刷新(全缓冲)特殊情况:用户强制刷新(fflush)、进程退出(必须刷新) 一般而言 ,行缓冲的设备文件 —— 显示器 全缓冲的设备文件 —— 磁盘文件 💦所以的设备,永远都倾向于全缓冲!(倾向于,但不绝对) —— 缓冲区满了,才刷新 —— 需要更少次的IO操作 —— 也就是更少次的外设访问(1次IO vs 10次IO)—— 也就可以提高效率 🌈其他刷新策略是结合具体情况做的妥协! 显示器:直接给用户看的,一方面要照顾效率,一方面要照顾用户的体验( 极端情况,可以自定义规则的)磁盘文件:用户不需要立马看见文件的内容,可以把缓冲区写满再输出,更加注重效率的考量 我们可能有疑问:1000个字节,刷一次是1000个字节,刷十次整体也是1000个字节,哪里效率高呢❓ 👍和外设进行沟通IO的时候,数据量的大小不是主要矛盾,和外设预备IO的过程才是最耗费时间的 好比:别人找你借钱,每一次都来找你唠嗑大半天,分开十次,沟通的时间花的很久,而转账的时间就几秒钟,一次沟通直接把钱全转过去了,才是效率最高的 🌈解疑答惑 同样的一个程序,向显示器打印输出4行文本,向普通文件(磁盘上)打印的时候,变成了7行,说明上面测试,并不影响系统接口 C的IO接口是打印了2次的系统接口,只打印了一次 我们最后调用fork,上面的函数已经被执行完了,但不代表数据已经被刷新了 🥑缓冲区是谁提供的 🔥曾经“我们所谈的缓冲区”,绝对不是由OS提供的,如果是OS同一提供,那么我们上面的代码,表现应该是一样的,而不是C的IO接口打印两次,所以是C标准库提供并且维护的用户级缓冲区 fputs把不是直接把数据直接放进操作系统,而是加载进C标准库的缓冲区中,加载完后自己可以直接返回;如果直接调用的是write接口,则是直接写给OS,不经过缓冲区 C语言提供的接口都是向显示器打印的,刷新策略都是行刷新,那么最后执行fork的时候 —— 一定是函数执行完了 && 数据已经被刷新了(因为都带\n),所以fork执行无意义如你对应的程序进行了重定向 ——> 要向磁盘文件打印 ——> 隐形的刷新策略变成了全缓冲!—— > \n便没有意义了 ——> 函数一定执行完了,数据还没有刷新!! 在当前进程对应的C标准库中的缓冲区中!! 这缓冲区的部分数据是父进程的数据吗? 是的 fork之后,父子分流,父进程的数据发生写时拷贝给子进程,所以C标准库会打印两次 总结: 重定向到文件导致:刷新策略改变(变成全缓冲)写时拷贝:父子进程各自刷新一次 🥑用户级缓冲区在哪里? 当我们用fflush强制刷新的时候 #include
#include
#include
int main() { //C语言提供的 printf("hello printf\n"); fprintf(stdout, "hello fprintf\n"); const char *s = "hello fputs\n"; fputs(s, stdout); //OS提供的 const char *ss = "hello write\n"; write(1, ss, strlen(ss)); //fork之前,强制刷新 fflush(stdout); //最后调用fork的时候,上面的函数已经被执行完了 fork();//创建子进程 return 0; } 结果如下: 数据在fork之前,已经被fflush刷新了,缓冲区里没有数据了,也就不存在写时拷贝。 这里更夸张的是,fflush(stdout)只告诉了stdout就能知道缓冲区在哪里? FILE *fopen(const char *path, const char *mode); C语言中,open打开文件,返回的是FILE * ,struct FILE结构体 — 内部封装了fd,还包含了该文件fd对应的语言层的缓冲区结构!(远在天边,近在眼前) 我们可以看看FILE结构体: //在/usr/include/libio.h struct _IO_FILE { int _flags; /* High-order word is _IO_MAGIC; rest is flags. */ #define _IO_file_flags _flags //缓冲区相关 /* The following pointers correspond to the C++ streambuf protocol. */ /* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */ char* _IO_read_ptr; /* Current read pointer */ char* _IO_read_end; /* End of get area. */ char* _IO_read_base; /* Start of putback+get area. */ char* _IO_write_base; /* Start of put area. */ char* _IO_write_ptr; /* Current put pointer. */ char* _IO_write_end; /* End of put area. */ char* _IO_buf_base; /* Start of reserve area. */ char* _IO_buf_end; /* End of reserve area. */ /* The following fields are used to support backing up and undo. */ char *_IO_save_base; /* Pointer to start of non-current get area. */ char *_IO_backup_base; /* Pointer to first valid character of backup area */ char *_IO_save_end; /* Pointer to end of non-current get area. */ struct _IO_marker *_markers; struct _IO_FILE *_chain; int _fileno; //封装的文件描述符 #if 0 int _blksize; #else int _flags2; #endif _IO_off_t _old_offset; /* This used to be _offset but it's too small. */ #define __HAVE_COLUMN /* temporary */ /* 1+column number of pbase(); 0 is unknown. */ unsigned short _cur_column; signed char _vtable_offset; char _shortbuf[1]; /* char* _save_gptr; char* _save_egptr; */ _IO_lock_t *_lock; #ifdef _IO_USE_OLD_IO_FILE }; 所以在C语言上,进行写入的时候放进缓冲区,定期刷新 C语言打开的FILE是文件流。C++中的cout 是类;里面必定包含了 fd、buffer(缓冲区) 🌏设计用户层缓冲区的代码 ~ 实战 💢struct file的设计 struct MyFILE_{ int fd; //文件描述符 char buffer[1024]; //缓冲区 int end; //当前缓冲区的结尾 }; 💢主函数 open文件 —— fputs输入 —— fclose关闭,接口函数都要我们逐一实现 int main() { MyFILE *fp = fopen_("./log.txt", "r"); if(fp = NULL) { printf("open file error"); return 0; } fputs_("hello world error", fp); fclose_(fp); } 我们发现:C语言的接口一旦打开成功,全部都要带上FILE*结构,原因很简单,因为什么数据都在这个FILE结构体中 FILE *fopen(const char *path, const char *mode); //以下全是要带FILE* int fputc(int c, FILE *stream); int fclose(FILE *fp); size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); 💢接口实现 💦fputs //此处刷新策略还没定 全部放进缓冲区 void fputs_(const char *message, MyFILE *fp) { assert(message); assert(fp); strcpy(fp->buffer + fp->end, message);//abcde\0 fp->end += strlen(message); } 运行结果: 上面覆盖了\0,strcpy会在结尾时候自动添加\0 若要往显示器上打印:变成行刷新 if(fp->fd == 0) { //标准输入 } else if(fp->fd == 1) { //标准输出 if(fp->buffer[fp->end-1] =='\n' ) { //fprintf(stderr, "fflush: %s", fp->buffer); //2 write(fp->fd, fp->buffer, fp->end); fp->end = 0; } } else if(fp->fd == 2) { //标准错误 } else { //其他文件 } } 测试用例: fputs_("one:hello world error", fp); fputs_("two:hello world error\n", fp); fputs_("three:hello world error", fp); fputs_("four:hello world error\n", fp); 结果:当遇到\n,才刷新 💦fflush刷新 当end!=0 ,就刷新进内核 内核刷新进外设,这就要用一个函数syncfs #include
//将缓冲区缓存提交到磁盘 int syncfs(int fd); 具体实现: void fflush(MyFILE *fp) { assert(fp); if(fp->end != 0) { //暂且认为刷新了 ——其实是把数据写到 内核 write(fp->fd, fp->buffer, fp->end); syncfs(fp->fd); //将数据写入到磁盘 fp->end = 0; } } 💦fclose 关闭之前要先刷新 void fclose(MyFILE *fp) { assert(fp); fflush(fp); close(fp->fd); free(fp); } 💢附源码 #include
#include
#include
#include
#include
#include
#include
#include
#define NUM 1024 struct MyFILE_{ int fd; //文件描述符 char buffer[1024]; // 缓冲区 int end; //当前缓冲区的结尾 }; typedef struct MyFILE_ MyFILE;//类型重命名 MyFILE *fopen_(const char *pathname, const char *mode) { assert(pathname); assert(mode); MyFILE *fp = NULL;//什么也没做,最后返回NULL if(strcmp(mode, "r") == 0) { } else if(strcmp(mode, "r+") == 0) { } else if(strcmp(mode, "w") == 0) { int fd = open(pathname, O_WRONLY | O_TRUNC | O_CREAT, 0666); if(fd >= 0) { fp = (MyFILE*)malloc(sizeof(MyFILE)); memset(fp, 0, sizeof(MyFILE)); fp->fd = fd; } } else if(strcmp(mode, "w+") == 0) { } else if(strcmp(mode, "a") == 0) { } else if(strcmp(mode, "a+") == 0) { } else{ //什么都不做 } return fp; } //是不是应该是C标准库中的实现! void fputs_(const char *message, MyFILE *fp) { assert(message); assert(fp); strcpy(fp->buffer+fp->end, message); //abcde\0 fp->end += strlen(message); //for debug printf("%s\n", fp->buffer); //暂时没有刷新, 刷新策略是谁来执行的呢?用户通过执行C标准库中的代码逻辑,来完成刷新动作 //这里效率提高,体现在哪里呢??因为C提供了缓冲区,那么我们就通过策略,减少了IO的执行次数(不是数据量) if(fp->fd == 0) { //标准输入 } else if(fp->fd == 1) { //标准输出 if(fp->buffer[fp->end-1] =='\n' ) { //fprintf(stderr, "fflush: %s", fp->buffer); //2 write(fp->fd, fp->buffer, fp->end); fp->end = 0; } } else if(fp->fd == 2) { //标准错误 } else { //其他文件 } } void fflush_(MyFILE *fp) { assert(fp); if(fp->end != 0) { //暂且认为刷新了--其实是把数据写到了内核 write(fp->fd, fp->buffer, fp->end); syncfs(fp->fd); //将数据写入到磁盘 fp->end = 0; } } void fclose_(MyFILE *fp) { assert(fp); fflush_(fp); close(fp->fd); free(fp); } int main() { close(1); MyFILE *fp = fopen_("./log.txt", "w"); if(fp == NULL) { printf("open file error"); return 1; } fputs_("one:hello world error", fp); fputs_("two:hello world error", fp); fputs_("three:hello world error", fp); fputs_("four:hello world error", fp); fclose(fp); } 📢写在最后 但行好事,莫问前程
上一篇
Linux 高并发服务器
下一篇
租用的服务器数据在怎么用
相关文章
隐藏服务器IP的五种方法
nnU-Netv2在服务器上使用全流程(小白边踩坑边学习的记录)
百度全网热卖怎么入驻
防火墙服务器名怎么更改
传奇服务器是怎么租法
Windows后台运行并启动Frpc客户端界面
【问题解决】VSCode1.86.0版+拓展Remote-SSHv0.108 无法连接到VSCode服务器(VSCode无法远程连接到Linux)
一键部署幻兽帕鲁服务器免费一年方案
为openlab搭建网站并架设一台NFS服务器
香港云服务器租用推荐
服务器租用资讯
·租用美国服务器配置
·怎样使用美国服务器(新的服务器怎样使用)
·怎么联系美国服务器(本服务器在美国受到法律)
·云服务器美国电影(美国高防云服务器)
·源服务器在美国(美国服务器ip)
·邮箱搭建美国服务器(群晖搭建邮箱服务器)
·微信美国服务器(微信小程序要服务器吗)
·受美国服务器保护(此服务器受美国保护)
·手机vpn美国服务器
服务器租用推荐
·美国服务器租用
·台湾服务器租用
·香港云服务器租用
·香港裸金属服务器
·香港高防服务器租用
·香港服务器租用特价
7*24H在线售后
高可用资源,安全稳定
1v1专属客服对接
无忧退款试用保障
德讯电讯股份有限公司
电话:00886-982-263-666
台湾总部:台北市中山区建国北路一段29号3楼
香港分公司:九龙弥敦道625号雅兰商业二期906室
服务器租用
香港服务器
日本服务器
台湾服务器
美国服务器
高防服务器购买
香港高防服务器出租
台湾高防服务器租赁
美国高防服务器DDos
云服务器
香港云服务器
台湾云服务器
美国云服务器
日本云服务器
行业新闻
香港服务器租用
服务器资讯
香港云服务器
台湾服务器租用
zblog博客
香港VPS
关于我们
机房介绍
联系我们
Copyright © 1997-2024 www.hkstack.com All rights reserved.