Linux--标准IO库

一、标准IO简介

所谓标准 I/O 库则是标准 C 库中用于文件 I/O 操作(譬如读文件、写文件等)相关的一系列库函数的集合,通常标准 I/O 库函数相关的函数定义都在头文件 <stdio.h> 中,所以我们需要在程序源码中包含 <stdio.h> 头文件。
标准 I/O 库函数是构建于文件 I/O open() read() write() lseek() close() 等)这些系统调用之上的, 譬如标准 I/O 库函数 fopen() 就利用系统调用 open() 来执行打开文件的操作、 fread() 利用系统调用 read() 来执行读文件操作、fwrite() 则利用系统调用 write() 来执行写文件操作等等。
标准 I/O 和文件 I/O 的区别如下:
虽然标准 I/O 和文件 I/O 都是 C 语言函数,但是标准 I/O 是标准 C 库函数,而文件 I/O 则是 Linux系统调用;
标准 I/O 是由文件 I/O 封装而来,标准 I/O 内部实际上是调用文件 I/O 来完成实际操作的;
可移植性:标准 I/O 相比于文件 I/O 具有更好的可移植性,通常对于不同的操作系统,其内核向应用层提供的系统调用往往都是不同,譬如系统调用的定义、功能、参数列表、返回值等往往都是不 一样的;而对于标准 I/O 来说,由于很多操作系统都实现了标准 I/O 库,标准 I/O 库在不同的操作系统之间其接口定义几乎是一样的,所以标准 I/O 在不同操作系统之间相比于文件 I/O 具有更好的 可移植性。
性能、效率:标准 I/O 库在用户空间维护了自己的 stdio 缓冲区,所以标准 I/O 是带有缓存的,而 文件 I/O 在用户空间是不带有缓存的,所以在性能、效率上,标准 I/O 要优于文件 I/O

二、FILE指针

之前所有文件 I/O 函数( open() read() write() lseek() 等)都是围绕文件描述符进行的, 当调用 open() 函数打开一个文件时,即返回一个文件描述符 fd ,然后该文件描述符就用于后续的 I/O 操作。
而对于标准 I/O 库函数来说,它们的操作是围绕 FILE 指针进行的,当使用标准 I/O 库函数打开或创建一个文件时,会返回一个指向 FILE 类型对象的指针(FILE * ,使用该 FILE 指针与被打开或创建的文件相关联,然后该 FILE 指针就用于后续的标准 I/O 操作(使用标准 I/O 库函数进行 I/O 操作),所以由此可知, FILE 指针的作用相当于文件描述符,只不过 FILE 指针用于标准 I/O 库函数中、而文件描述符则用于文件I/O 系统调用中。
FILE 是一个结构体数据类型,它包含了标准 I/O 库函数为管理文件所需要的所有信息,包括用于实际 I/O 的文件描述符、指向文件缓冲区的指针、缓冲区的长度、当前缓冲区中的字节数以及出错标志等。 FILE 数据结构定义在标准 I/O 库函数头文件 stdio.h 中。

三、标准输入、输出、错误

0 1 2 这三个是文件描述符,只能用于文件 I/O read() write() 等),那么在标准 I/O 中,自然是无法使用文件描述符来对文件进行 I/O 操作的,它们需要围绕 FILE 类型指针来进行,在 stdio.h 头文件中有相应的定义,如下:
/* Standard streams. */
extern struct _IO_FILE *stdin; /* Standard input stream. */
extern struct _IO_FILE *stdout; /* Standard output stream. */
extern struct _IO_FILE *stderr; /* Standard error output stream. */
/* C89/C99 say they're macros. Make them happy. */
#define stdin stdin
#define stdout stdout
#define stderr stderr
所以,在标准 I/O 中,可以使用 stdin stdout stderr 来表示标准输入、标准输出和标准错误。

四、fopen() fread()   fwrite()

在标准 I/O 中,我们将使用库函数 fopen()打开或创建文件, fopen() 函数原型如下所示:
#include <stdio.h>
FILE *fopen(const char *path, const char *mode);
函数参数和返回值含义如下:
path 参数 path 指向文件路径,可以是绝对路径、也可以是相对路径。
mode 参数 mode 指定了对该文件的读写权限,是一个字符串,稍后介绍。
返回值: 调用成功返回一个指向 FILE 类型对象的指针( FILE * ),该指针与打开或创建的文件相关联, 后续的标准 I/O 操作将围绕 FILE 指针进行。如果失败则返回 NULL ,并设置 errno 以指示错误原因。

五、fseek 定位

库函数 fseek() 的作用类似于 系统调用 lseek() ,用于设置文件读写位置偏移量, lseek() 用于文件 I/O ,而库函数 fseek() 则用于标准 I/O。

六、检查或复位标志

调用 fread() 读取数据时,如果返回值小于参数 nmemb 所指定的值,表示发生了错误或者已经到了文件末尾(文件结束 end-of-file ),但 fread() 无法具体确定是哪一种情况;在这种情况下,可以通过判断错误标志或 end-of-file 标志来确定具体的情况。
feof() 函数
ferror() 函数
clearerr() 函数

七、格式化IO

在前面编写的测试代码中,会经常使用到库函数 printf() 用于输出程序中的打印信息, printf() 函数可将格式化数据写入到标准输出,所以通常称为格式化输出。除了 printf() 之外,格式化输出还包括: fprintf() 、 dprintf()、 sprintf() snprintf() 4 个库函数。 除了格式化输出之外,自然也有格式化输入,从标准输入中获取格式化数据,格式化输入包括:scanf() 、 fscanf()、 sscanf() 这三个库函数。
格式化输出:
C 库函数提供了 5 个格式化输出函数,包括: printf() fprintf() dprintf() sprintf() snprintf() ,其函数定义如下所示:
#include <stdio.h>
int printf(const char *format, ...);
int fprintf(FILE *stream, const char *format, ...);
int dprintf(int fd, const char *format, ...);
int sprintf(char *buf, const char *format, ...);
int snprintf(char *buf, size_t size, const char *format, ...);
可以看到,这 5 个函数都是可变参函数,它们都有一个共同的参数 format ,这是一个字符串,称为格式控制字符串,用于指定后续的参数如何进行格式转换,所以才把这些函数称为格式化输出,因为它们可以以调用者指定的格式进行转换输出。

格式控制字符串 format​​​​​​​

把这个参数称为格式控制字符串,顾名思义,首先它是一个字符串的形式,其次它能够控制后续变参的格式转换。
格式控制字符串由两部分组成:普通字符(非 % 字符)和转换说明。普通字符会进行原样输出,每个转换说明都会对应后续的一个参数,通常有几个转换说明就需要提供几个参数(除固定参数之外的参数),使 之一一对应,用于控制对应的参数如何进行转换。
转换说明的描述信息需要和与之相对应的参数对应的数据类型要进行匹配,如果不匹配通常会编译报错或者警告! ​​​​​​​
格式化输入:
C 库函数提供了 3 个格式化输入函数,包括: scanf() fscanf() sscanf() ,其函数定义如下所示:
#include <stdio.h>
int scanf(const char *format, ...);
int fscanf(FILE *stream, const char *format, ...);
int sscanf(const char *str, const char *format, ...);

八、I/O缓冲

出于速度和效率的考虑,系统 I/O 调用(即文件 I/O open read write 等)和标准 C 语言库 I/O 函数 (即标准 I/O 函数)在操作磁盘文件时会对数据进行缓冲。

1、文件 I/O 的内核缓冲

read() write() 系统调用在进行文件读写操作的时候并不会直接访问磁盘设备,而是仅仅在用户空间缓冲区和内核缓冲区(kernel buffer cache )之间复制数据。
调用 write()后仅仅只是将 字节数据拷贝到了内核空间的缓冲区中,拷贝完成之后函数就返回了, 在后面的某个时刻,内核会将其缓冲区中的数据写入(刷新)到磁盘设备中,所以由此可知,系统调用 write() 与磁盘操作并不是同步的,write()函数并不会等待数据真正写入到磁盘之后再返回。此外当调用 close() 或显式同步操作(如 fsync()fdatasync()sync())时,操作系统也会将相应文件的缓冲区数据写入磁盘。
如果在此期间,其它进 程调用 read() 函数读取该文件的这几个字节数据,那么内核将自动从缓冲区中读取这几个字节数据返回给应用程序。
我们把这个内核缓冲区就称为文件 I/O 的内核缓冲。这样的设计,目的是为了提高文件 I/O 的速度和效 率,使得系统调用 read()、write()的操作更为快速,不需要等待磁盘操作(将数据写入到磁盘或从磁盘读取 出数据),磁盘操作通常是比较缓慢的。同时这一设计也更为高效,减少了内核操作磁盘的次数。

刷新文件 I/O 的内核缓冲区

强制将文件 I/O 内核缓冲区中缓存的数据写入(刷新)到磁盘设备中,对于某些应用程序来说,可能是 很有必要的,例如,应用程序在进行某操作之前,必须要确保前面步骤调用 write() 写入到文件的数据已经真 正写入到了磁盘中,诸如一些数据库的日志进程。
联系到一个实际的使用场景,当我们在 Ubuntu 系统下拷贝文件到 U 盘时,文件拷贝完成之后,通常在拔掉 U 盘之前,需要执行 sync 命令进行同步操作,这个同步操作其实就是将文件 I/O 内核缓冲区中的数据更新到 U 盘硬件设备,所以如果在没有执行 sync 命令时拔掉 U 盘,很可能就会导致拷贝到 U 盘中的文件遭到破坏!
㈠、 fsync() 函数
㈡、 fdatasync() 函数
㈢、 sync() 函数
对性能的影响
在程序中频繁调用 fsync() fdatasync() sync() (或者调用 open 时指定 O_DSYNC O_SYNC 标志) 对性能的影响极大,大部分的应用程序是没有这种需求的,所以在大部分应用程序当中基本不会使用到。

直接 I/O:绕过内核缓冲

Linux 内核 2.4 版本开始, Linux 允许应用程序在执行文件 I/O 操作时绕过内核缓冲区,从用户空间 直接将数据传递到文件或磁盘设备,把这种操作也称为直接 I/O direct I/O )或裸 I/O raw I/O)。直接 I/O 只在一些特定的需求场合,譬如磁盘速率测试工具、数据库系统等。

​​​​​​​​​​​​​​

2、标准IO的stdio 缓冲

标准 I/Ofopenfreadfwritefclosefseek 等)是 C 语言标准库函数,而文件 I/Oopenreadwrite、 close、lseek 等)是系统调用,虽然标准 I/O 是在文件 I/O 基础上进行封装而实现(譬如 fopen 内部实际上调 用了 openfread 内部调用了 read 等),但在效率、性能上标准 I/O 要优于文件 I/O,其原因在于标准 I/O 实 现维护了自己的缓冲区,我们把这个缓冲区称为 stdio 缓冲区。

通过这样的优化操作,当操作磁盘文件时,在用户空间缓存大块数据以减少调用系统调用的次数,使得 效率、性能得到优化。使用标准 I/O 可以使编程者免于自行处理对数据的缓冲,无论是调用 write() 写入数据、还是调用 read() 读取数据。
stdio 缓冲进行设置
C 语言提供了一些库函数可用于对标准 I/O stdio 缓冲区进行相关的一些设置,包括 setbuf() setbuffer() 以及 setvbuf()

刷新 stdio 缓冲区

㈠、关闭文件时刷新 stdio 缓冲区

 fclose(stdout); //关闭标准输出
㈡、程序退出时刷新 stdio 缓冲区

总结:

从图中自上而下,
1)首先应用程序调用标准 I/O 库函数将用户数据写入到 stdio 缓冲区中, stdio 缓冲区是 由 stdio 库所维护的用户空间缓冲区。
2)针对不同的缓冲模式,当满足条件时, stdio 库会调用文件 I/O (系统 调用 I/O )将 stdio 缓冲区中缓存的数据写入到内核缓冲区中,内核缓冲区位于内核空间。
3)最终由内核向磁 盘设备发起读写操作,将内核缓冲区中的数据写入到磁盘(或者从磁盘设备读取数据到内核缓冲区)。
应用程序调用库函数可以对 stdio 缓冲区进行相应的设置,设置缓冲区缓冲模式、缓冲区大小以及由调 用者指定一块空间作为 stdio 缓冲区,并且可以强制调用 fflush() 函数刷新缓冲区;而对于内核缓冲区来说, 应用程序可以调用相关系统调用对内核缓冲区进行控制,譬如调用 fsync() fdatasync() sync() 来刷新内核 缓冲区(或通过 open 指定 O_SYNC O_DSYNC 标志),或者使用直接 I/O 绕过内核缓冲区( open 函数 指定 O_DIRECT 标志)。

九、文件描述符与 FILE 指针互转

在应用程序中,在同一个文件上执行 I/O 操作时,还可以将文件 I/O (系统调用 I/O )与标准 I/O 混合使 用,这个时候我们就需要将文件描述符和 FILE 指针对象之间进行转换,此时可以借助于库函数 fdopen() 、 fileno()来完成。
库函数 fileno() 可以将标准 I/O 中使用的 FILE 指针转换为文件 I/O 中所使用的文件描述符,而 fdopen() 则进行着相反的操作。
当混合使用文件 I/O 和标准 I/O 时,需要特别注意缓冲的问题,文件 I/O 会直接将数据写入到内核缓冲 区进行高速缓存,而标准 I/O 则会将数据写入到 stdio 缓冲区,之后再调用 write() stdio 缓冲区中的数据写入到内核缓冲区。譬如下面这段代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
printf("print");
write(STDOUT_FILENO, "write\n", 6);
exit(0);
}
执行结果你会发现,先输出了 "write" 字符串信息,接着再输出了 "print" 字符串信息,产生这个问题的原因很简单:
printf函数是标准IO函数,且是行缓冲的,他会将数据先输入到stdio缓冲区中,遇到换行符、程序退出或者主动刷新时才会将数据刷新进内核缓冲区,在终端显示。
write函数是系统调用函数,会直接将数据写入内核缓冲区中。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/24692.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

图片和PDF展示预览、并支持下载

需求 展示图片和PDF类型&#xff0c;并且点击图片或者PDF可以预览 第一步&#xff1a;遍历所有的图片和PDF列表 <div v-for"(data,index) in parerFont(item.fileInfo)" :key"index" class"data-list-item"><downloadCard :file-inf…

Java学习54-关键字this的使用

this是什么 this的作用&#xff1a; 它在方法(准确的说是实例方法或非static的方法)内部使用&#xff0c;表示调用该方法的对象 它在构造器内部使用&#xff0c;表示该构造器正在初始化的对象 this可以调用的结构&#xff1a;成员变量、方法和构造器 什么时候使用this 实…

83页 | 2024数据安全典型场景案例集(免费下载)

以上是资料简介和目录&#xff0c;如需下载&#xff0c;请前往星球获取&#xff1a;

深度学习——卷积神经网络(CNN)

深度学习 深度学习就是通过多层神经网络上运用各种机器学习算法学习样本数据的内在规律和表示层次&#xff0c;从而实现各种任务的算法集合。各种任务都是啥&#xff0c;有&#xff1a;数据挖掘&#xff0c;计算机视觉&#xff0c;语音识别&#xff0c;自然语言处理等。‘ 深…

【ARM Cache 与 MMU 系列文章 7.6 -- ARMv8 MMU 配置 寄存器使用介绍】

请阅读【ARM Cache 及 MMU/MPU 系列文章专栏导读】 及【嵌入式开发学习必备专栏】 文章目录 MMU 转换控制寄存器 TCR_ELxTCR_ELx 概览TCR_ELx 寄存器字段详解TCR 使用示例Normal MemoryCacheableShareability MMU 内存属性寄存器 MAIR_ELxMAIR_ELx 寄存器结构内存属性字段Devic…

TiDB-从0到1-配置篇

TiDB从0到1系列 TiDB-从0到1-体系结构TiDB-从0到1-分布式存储TiDB-从0到1-分布式事务TiDB-从0到1-MVCCTiDB-从0到1-部署篇TiDB-从0到1-配置篇 一、系统配置 TiDB的配置分为系统配置和集群配置两种。 其中系统配置对应TiDB Server&#xff08;不包含TiKV和PD的参数&#xff0…

利用GPT和PlantUML快速生成UML图用于设计

在软件开发中&#xff0c;设计阶段可是关键的一步。UML&#xff08;统一建模语言&#xff09;图能帮我们更清晰地理解和规划系统结构&#xff0c;但手动画UML图有时会很费时费力。好消息是&#xff0c;通过结合使用ChatGPT和PlantUML&#xff0c;我们可以高效地生成UML图&#…

STM32_HAL库_外部中断

一、设置分组 stm32f1xx_hal_cortex.c 查看分组 五个形参&#xff0c;分组0~4 stm32f1xx_hal.c 设置了分组为2&#xff0c; 此工程就不需要再设置了 再回到stm32f1xx_hal_cortex.c 查看NVIC_SetPriorityGrouping的定义&#xff0c;若无法跳转&#xff0c;先编译一下&…

山东大学软件学院项目实训-创新实训-基于大模型的旅游平台(二十七)- 微服务(7)

11.1 : 同步调用的问题 11.2 异步通讯的优缺点 11.3 MQ MQ就是事件驱动架构中的Broker 安装MQ docker run \-e RABBITMQ_DEFAULT_USERxxxx \-e RABBITMQ_DEFAULT_PASSxxxxx \--name mq \--hostname mq1 \-p 15672:15672 \-p 5672:5672 \-d \rabbitmq:3-management 浏览器访问1…

在网上赚钱,可以自由掌控时间,灵活的兼职副业选择

朋友们看着周围的人在网上赚钱&#xff0c;自己也会为之心动&#xff0c;随着电子设备的普及&#xff0c;带动了很多的工作、创业以及兼职副业选择的机会&#xff0c;作为普通人的我们&#xff0c;如果厌倦了世俗的朝九晚五&#xff0c;想着改变一下自己的生活&#xff0c;可以…

uc/OS移植到stm32实现三个任务

文章目录 一、使用CubeMX创建工程二、uc/OS移植三、添加代码四、修改代码五、实践结果六、参考文章七、总结 实践内容 学习嵌入式实时操作系统&#xff08;RTOS&#xff09;,以uc/OS为例&#xff0c;将其移植到stm32F103上&#xff0c;构建至少3个任务&#xff08;task&#xf…

就业班 第四阶段(k8s) 2401--6.4 day2 Dashboard+国产kuboard(好用)+简单命令

可视化部署Dashboard 昨天做一主两从飞高可用&#xff0c;出现浏览器那一行&#xff0c;是为啥 thisisunsafe kubectl get 获取资源 pod node svc -A 所有名称空间 -n 指定名称空间 -w 动态显示 kubectl edit 资源 pod node svc 官方的&#xff0c;毛坯房 国产 在哪找的…

数字证书和CA

CA&#xff08;Certificate Authority&#xff09;证书颁发机构 验证数字证书是否可信需要使用CA的公钥 操作系统或者软件本身携带一些CA的公钥&#xff0c;同时也可以向提供商申请公钥 数字证书的内容 数字证书通常包含以下几个主要部分&#xff1a; 主体信息&#xff08…

uc/OS-III多任务程序

文章目录 一、实验内容二、实验步骤&#xff08;一&#xff09;基于STM32CubeMX建立工程&#xff08;二&#xff09;获取uc/OS-III源码&#xff08;三&#xff09;代码移植 三、修改mai.c文件四、实验现象 一、实验内容 学习嵌入式实时操作系统&#xff08;RTOS&#xff09;,以…

检测五个数是否一样的算法

目录 算法算法的输出与打印效果输出输入1输入2 打印打印1打印2 算法的流程图总结 算法 int main() {int arr[5] { 0 };int i 0;int ia 0;for (i 0; i < 5; i) { scanf("%d", &arr[i]); }for (i 1; i < 5; i) {if (arr[0] ! arr[i]) {ia 1;break;} }…

2024全国大学生数据统计与分析竞赛B题【电信银行卡诈骗的数据分析】思路详解

电信诈骗是指通过电话、网络和短信方式&#xff0c;编造虚假信息&#xff0c;设置骗局&#xff0c;对受害人实施远程、非接触式诈骗&#xff0c;诱使受害人打款或转账的犯罪行为&#xff0c;通常以冒充他人及仿冒、伪造各种合法外衣和形式的方式达到欺骗的目的&#xff0c;如冒…

C# 异步方法async / await 任务超时处理

一、需求 如果调用一个异步方法后&#xff0c;一直不给返回值结果怎么办呢&#xff1f;这就涉及到怎么取消任务了。 二、Task取消任务 static CancellationTokenSource source new CancellationTokenSource();static void Main(string[] args){Task.Run(() >{for (int i …

Responder工具

简介 Responder是一种网络安全工具&#xff0c;用于嗅探和抓取网络流量中的凭证信息&#xff08;如用户名、密码等&#xff09;。它可以在本地网络中创建一个伪造的服务&#xff08;如HTTP、SMB等&#xff09;&#xff0c;并捕获客户端与该服务的通信中的凭证信息。 Responder工…

路由器作为网络扩展器——设置桥接、路由模式

下面提到的路由器都是家用路由器 一、有线桥接(交换模式) 1.连接示意图 (副路由器只看交换模式部分) 副路由器充当交换机的角色 二、无线桥接(与有线类似) &#xff08;副路由器的无线信号 连接 主路由器的无线信号&#xff09; 三、路由模式 1.连接示意图 (副路由器只看…

扩散模型条件生成——Classifier Guidance和Classifier-free Guidance原理解析

1、前言 从讲扩散模型到现在。我们很少讲过条件生成&#xff08;Stable DIffusion曾提到过一点&#xff09;&#xff0c;所以本篇内容。我们就来具体讲一下条件生成。这一部分的内容我就不给原论文了&#xff0c;因为那些论文并不只讲了条件生成&#xff0c;还有一些调参什么的…