【Linux系统编程】 浅谈标准I/O缓冲区

简介:

标准I/O库提供缓冲的目的是尽可能地减少使用read和write调用的次数。它也对每个I/O流自动地进行缓冲管理,从而避免了应用程序需要考虑这一点所带来的麻烦。不幸的是,标准I/O库最令人迷惑的也是它的缓冲。


标准I/O提供了三种类型的缓冲:

1、全缓冲:

在填满标准I/O缓冲区后才进行实际I/O操作。常规文件(如普通文本文件)通常是全缓冲的。


2、行缓冲:

当在输入和输出中遇到换行符时,标准I/O库执行I/O操作。这允许我们一次输出一个字符,但只有在写了一行之后才进行实际I/O操作。标准输入和标准输出对应终端设备(如屏幕)时通常是行缓冲的。


3、不带缓冲:

用户程序每次调库函数做写操作都要通过系统调用写回内核(如系统调用函数)。标准错误输出通常是无缓冲的,这样用户程序产生的错误信息可以尽快输出到设备。


下面是各个缓冲区的验证。


全缓冲:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. int main(int argc, char *argv[])  
  2. {  
  3.     FILE *fp = NULL;  
  4.     // 读写方式打开,文件不存在则创建  
  5.     fp = fopen("sunplusedu.txt""w+");  
  6.     if(NULL == fp)  
  7.     {  
  8.         printf("open error\n");  
  9.         return 1;  
  10.     }  
  11.     char *str = "sunplusedu\n";  
  12.     fwrite(str, 1, strlen(str), fp);    // 往文件写内容  
  13.     while(1);   // 程序阻塞在这里  
  14.   
  15.     return 0;  
  16. }  

运行程序发现,sunplusedu.txt并没有内容。因为常规文件通常是全缓冲的,只有缓冲区满了后,才会把内容写到文件中。接下来,我们改一下上面那个例子。


[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include <stdio.h>  
  2. #include <string.h>  
  3.   
  4. int main(int argc, char *argv[])  
  5. {  
  6.     FILE *fp = NULL;  
  7.     // 读写方式打开,文件不存在则创建  
  8.     fp = fopen("sunplusedu.txt""w+");  
  9.     if(NULL == fp)  
  10.     {  
  11.         printf("open error\n");  
  12.         return 1;  
  13.     }  
  14.     char *str = "sunplusedu\n";  
  15.     int i = 0;  
  16.     while(i <= 512){ // 缓冲区大小不确定,i的大小只是一个调试值  
  17.         fwrite(str, 1, strlen(str), fp);    // 往文件写内容  
  18.         i++;  
  19.     }  
  20.     while(1);   // 程序阻塞在这里  
  21.   
  22.     return 0;  
  23. }  


上面的例子是循环给文件写内容,让缓冲区有填满的可能,结果发现,文件是有内容的。实际上要想成功给文件写进内容,除了缓冲区填满,还有别的方法。


1)人为关闭文件,就算缓冲区没有填满,内容也会写进文件

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include <stdio.h>  
  2. #include <string.h>  
  3.   
  4. int main(int argc, char *argv[])  
  5. {  
  6.     FILE *fp = NULL;  
  7.     // 读写方式打开,文件不存在则创建  
  8.     fp = fopen("sunplusedu.txt""w+");  
  9.     if(NULL == fp)  
  10.     {  
  11.         printf("open error\n");  
  12.         return 1;  
  13.     }  
  14.     char *str = "sunplusedu\n";  
  15.     fwrite(str, 1, strlen(str), fp);    // 往文件写内容  
  16.     fclose(fp);     // 人为关闭文件,就算缓冲区没有填满,内容也会写进文件  
  17.       
  18.     while(1);   // 程序阻塞在这里  
  19.   
  20.     return 0;  
  21. }  

2)程序正常结束,就算缓冲区没有填满,没有关闭文件,内容也会写进文件。

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include <stdio.h>  
  2. #include <string.h>  
  3.   
  4. int main(int argc, char *argv[])  
  5. {  
  6.     FILE *fp = NULL;  
  7.     // 读写方式打开,文件不存在则创建  
  8.     fp = fopen("sunplusedu.txt""w+");  
  9.     if(NULL == fp)  
  10.     {  
  11.         printf("open error\n");  
  12.         return 1;  
  13.     }  
  14.     char *str = "sunplusedu\n";  
  15.     fwrite(str, 1, strlen(str), fp);    // 往文件写内容  
  16.       
  17.     return 0;  
  18.     // 程序正常结束,就算缓冲区没有填满,没有关闭文件,内容也会写进文件。  
  19. }  

行缓冲:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include <stdio.h>  
  2.   
  3. int main(int argc, char *argv[])  
  4. {  
  5.     printf("hello sunplusedu");  
  6.     while(1);  
  7.       
  8.     return 0;  
  9. }  

运行这个程序,会发现 hello sunplusedu 并没有打印到屏幕上。因为标准输入和标准输出对应终端设备时通常是行缓冲的,当在输入和输出中遇到换行符时,标准I/O库执行I/O操作。如下:


[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include <stdio.h>  
  2.   
  3. int main(int argc, char *argv[])  
  4. {  
  5.     printf("hello sunplusedu\n");  
  6.     while(1);  
  7.       
  8.     return 0;  
  9. }  



除了遇到换行符,还有别的方法可以执行I/O操作。


1)缓冲区填满

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. int main(int argc, char *argv[])  
  2. {  
  3.     while(1){   // 循环打印,总有缓冲区填满的可能  
  4.         printf("hello sunplusedu");  
  5.     }  
  6.     while(1);  
  7.       
  8.     return 0;  
  9. }  


2)人为刷新缓冲区

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include <stdio.h>  
  2.   
  3. int main(int argc, char *argv[])  
  4. {  
  5.     printf("hello sunplusedu");  
  6.     fflush(stdout); // 人为刷新  
  7.   
  8.     while(1);  
  9.       
  10.     return 0;  
  11. }  


3)程序正常结束

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include <stdio.h>  
  2.   
  3. int main(int argc, char *argv[])  
  4. {  
  5.     printf("hello sunplusedu");  
  6.       
  7.     return 0;  
  8.     // 程序正常结束  
  9. }  


不带缓冲:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include <unistd.h>  
  2. #include <string.h>  
  3.   
  4. int main(int argc, char *argv[])  
  5. {  
  6.     char *str = "hello sunplusedu.com";   
  7.     // 有没有\n,缓冲区有没有填满,都没关系  
  8.     write(1, str, strlen(str)); // 往标准输出写内容  
  9.     while(1);  
  10.       
  11.     return 0;  
  12. }  
相关文章
|
7月前
|
Ubuntu Linux Anolis
Linux系统禁用swap
本文介绍了在新版本Linux系统(如Ubuntu 20.04+、CentOS Stream、openEuler等)中禁用swap的两种方法。传统通过注释/etc/fstab中swap行的方式已失效,现需使用systemd管理swap.target服务或在/etc/fstab中添加noauto参数实现禁用。方法1通过屏蔽swap.target适用于新版系统,方法2通过修改fstab挂载选项更通用,兼容所有系统。
682 3
Linux系统禁用swap
|
7月前
|
Linux
Linux系统修改网卡名为eth0、eth1
在Linux系统中,可通过修改GRUB配置和创建Udev规则或使用systemd链接文件,将网卡名改为`eth0`、`eth1`等传统命名方式,适用于多种发行版并支持多网卡配置。
1233 3
|
Ubuntu Linux 网络安全
Linux系统初始化脚本
一款支持Rocky、CentOS、Ubuntu、Debian、openEuler等主流Linux发行版的系统初始化Shell脚本,涵盖网络配置、主机名设置、镜像源更换、安全加固等多项功能,适配单/双网卡环境,支持UEFI引导,提供多版本下载与持续更新。
766 3
Linux系统初始化脚本
|
8月前
|
运维 Linux 开发者
Linux系统中使用Python的ping3库进行网络连通性测试
以上步骤展示了如何利用 Python 的 `ping3` 库来检测网络连通性,并且提供了基本错误处理方法以确保程序能够优雅地处理各种意外情形。通过简洁明快、易读易懂、实操性强等特点使得该方法非常适合开发者或系统管理员快速集成至自动化工具链之内进行日常运维任务之需求满足。
597 18
|
7月前
|
安全 Linux Shell
Linux系统提权方式全面总结:从基础到高级攻防技术
本文全面总结Linux系统提权技术,涵盖权限体系、配置错误、漏洞利用、密码攻击等方法,帮助安全研究人员掌握攻防技术,提升系统防护能力。
920 1
|
7月前
|
监控 安全 Linux
Linux系统提权之计划任务(Cron Jobs)提权
在Linux系统中,计划任务(Cron Jobs)常用于定时执行脚本或命令。若配置不当,攻击者可利用其提权至root权限。常见漏洞包括可写的Cron脚本、目录、通配符注入及PATH变量劫持。攻击者通过修改脚本、创建恶意任务或注入命令实现提权。系统管理员应遵循最小权限原则、使用绝对路径、避免通配符、设置安全PATH并定期审计,以防范此类攻击。
1284 1
|
8月前
|
缓存 监控 Linux
Linux系统清理缓存(buff/cache)的有效方法。
总结而言,在大多数情形下你不必担心Linux中buffer与cache占用过多内存在影响到其他程序运行;因为当程序请求更多内存在没有足够可用资源时,Linux会自行调整其占有量。只有当你明确知道当前环境与需求并希望立即回收这部分资源给即将运行重负载任务之前才考虑上述方法去主动干预。
2270 10
|
8月前
|
安全 Linux 数据安全/隐私保护
为Linux系统的普通账户授予sudo访问权限的过程
完成上述步骤后,你提升的用户就能够使用 `sudo`命令来执行管理员级别的操作,而无需切换到root用户。这是一种更加安全和便捷的权限管理方式,因为它能够留下完整的权限使用记录,并以最小权限的方式工作。需要注意的是,随意授予sudo权限可能会使系统暴露在风险之中,尤其是在用户不了解其所执行命令可能带来的后果的情况下。所以在配置sudo权限时,必须谨慎行事。
1531 0
|
8月前
|
Ubuntu Linux 开发者
国产 Linux 发行版再添新成员,CutefishOS 系统简单体验
当然,系统生态构建过程并不简单,不过为了帮助国产操作系统优化生态圈,部分企业也开始用国产操作系统替代 Windows,我们相信肯定会有越来越多的精品软件登录 Linux 平台。
720 0
|
8月前
|
Ubuntu 安全 Linux
Linux系统入门指南:从零开始学习Linux
Shell脚本是一种强大的自动化工具,可以帮助您简化重复的任务或创建复杂的脚本程序。了解Shell脚本的基本语法和常用命令,以及编写和运行Shell脚本的步骤,将使您更高效地处理日常任务。
706 0