汉扬编程 编程大纲 三分钟弄清楚C语言IO函数,为什么要设计缓冲区读写文件?

三分钟弄清楚C语言IO函数,为什么要设计缓冲区读写文件?

通过前面几节的介绍,相信朋友们都能搭建自己的 linux 开发环境了。

三分钟弄清楚C语言IO函数,为什么要设计缓冲区读写文件?

接下来,小编决定介绍一下 linux 中 C语言程序开发中常用的一些函数,并在这一实践过程中进一步了解 linux 内核。这样不至于使学习过程太过枯燥,也能顺便积累一些开发经验。

三分钟弄清楚C语言IO函数,为什么要设计缓冲区读写文件?

三分钟弄清楚C语言IO函数,为什么要设计缓冲区读写文件?

在整理这些函数的过程中,小编发现即使是经常使用的函数,也有一些值得回味的东西。C语言中I/O 函数的缓冲 先来说说 linux 中 C语言程序开发常用的 I/O 函数。大多数程序员常常将 I/O 函数分为“带缓冲”的和“不带缓冲”的两类。

三分钟弄清楚C语言IO函数,为什么要设计缓冲区读写文件?

这里说的“缓冲”其实是指用户空间的一块内存区域。一般来说,“带缓冲”的 I/O 函数写数据时,并不直接写磁盘介质,而是将数据先写到这块内存缓冲中,之后用户空间缓冲中的数据会被传送到系统缓冲中(fflush函数可以加速这一过程),稍后 linux 内核会将系统缓冲中的数据送完磁盘驱动器(fsync函数可以加速这一过程),这之后,数据才真正的被写入磁盘。

三分钟弄清楚C语言IO函数,为什么要设计缓冲区读写文件?

设计缓冲的原因,主要是因为目前内存的读写速度(常 ns 级)往往远大于硬盘的读写速度(常 ms 级)。因此,缓冲区的建立可以尽力避免太过频繁的写磁盘。而且对于硬盘来说,写入一个字节可能跟写入一个扇区没什么两样,程序员每次写入的数据也许就几个字节,所以可以将每次写入的几个字节放入缓冲区,排列组合成一整块数据再写入,也能极大的提升效率。

三分钟弄清楚C语言IO函数,为什么要设计缓冲区读写文件?

“带缓冲”的 I/O 读函数读取数据之前,则会首先判断用户空间的进程缓冲区是否包含数据,如果没有,则继续判断系统缓冲区是否包含数据,如果缓冲区有数据,就直接从内存缓冲区中取数据,避免了较慢的读磁盘操作。

一般来说,“带缓冲”的 I/O 读函数从磁盘读取数据时,并不只读取调用者指定的读取字节数。例如,程序员调用读函数从磁盘读取 10 字节数据,读函数可能一次性读取一个扇区(常常是 512B)保存在缓冲区里,只返回给程序员需要的 10 字节。这么设计,主要是因为相信该程序员使用了这里的 10 字节数据,那么他接着使用接下来的数据可能性是非常大的,因此一次性多读取点放在内存里。

归根结底,缓冲区的设计就是为了减少耗时较多的读写 I/O 操作。

C语言中“不带缓冲的”I/O 函数 本节讨论的是“不带缓冲的”I/O 函数族,“带缓冲的”函数以后再讨论。最常用的“不带缓冲”的 I/O 函数族,包含 open、read、write、lseek 以及 close 五个函数。

其实,按照上面的讨论,“不带缓冲”这个说法并不准确。read、write 函数还是有系统缓冲区的。 Unix 认为“一切皆文件”,无论是磁盘,还是设备,甚至一段内存,都可以当作是文件来操作。linux 作为类 unix 系统,自然也是如此。一般来说,在 C语言中,操作文件的基本流程是:

打开文件 -> 读写文件 -> 关闭文件 使用 open 函数即可打开文件,按照上一节介绍的,在linux 中输入 man 命令即可查询到 open 函数的使用说明:

# man 2 open

从文档中,即可看到 open 函数所需要的头文件,它的函数原型,以及各个参数的意义和返回值等信息。其他几个函数也可以通过 man 命令查询。下面给出一段 C语言读写文件的代码示例,请看:

#include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>#include <string.h>int main(){ char rbuf[128] = {0}; int fd = open(\”test.txt\”, O_RDWR|O_CREAT); write(fd, \”hello embedTime\”, strlen(\”hello embedTime\”)); close(fd); fd = open(\”test.txt\”, O_RDWR|O_CREAT); read(fd, rbuf, sizeof(rbuf)); close(fd); printf(\”read data: %s\\n\”, rbuf); return 0;}

上面的示例中,先打开了 test.txt 文件,然后写入了“hello embedTime”信息,写完后关闭文件。接下来,又打开一次文件,从 test.txt 中读取了数据,并把读取的数据打印了出来,最终编译执行,结果如下:

# gcc t.c# ./a.outread data: hello embedTimeC语言中的 lseek 函数 上面给出的示例,我们写入数据后,为了读出它,又重新打开了一次文件,这不是啰嗦了吗?直接下面这样写行吗?

#include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>#include <string.h>int main(){ char rbuf[128] = {0}; int fd = open(\”test.txt\”, O_RDWR|O_CREAT); write(fd, \”hello embedTime\”, strlen(\”hello embedTime\”)); read(fd, rbuf, sizeof(rbuf)); close(fd); printf(\”read data: %s\\n\”, rbuf); return 0;}

编译并执行,得到如下结果:

# gcc t.c# ./a.outread data: 怎么回事,读出来的数据是空的。这其实是因为系统记录 I/O 函数每次读写的位置指针,无论是 write 函数,还是 read 函数都会改变这一读写指针的位置。这就明白了,当 write 函数写入“hello embedTime”后,读写指针就被移到 “embedTime”的最后了,read 函数读取时,也是从这里开始读的,后面没有数据了,读出来的自然就是空了。

所以,如果想读出数据,只需要把读写文件的位置指针移到文件最前面,就行了。lseek 函数正可以完成这项工作,可以使用 man 命令查看它的手册,下面直接给出修改后的代码了,请看:

…write(fd, \”hello embedTime\”, strlen(\”hello embedTime\”));lseek(fd, 0, SEEK_SET);read(fd, rbuf, sizeof(rbuf));…

再次编译执行,发现一切正常了:

# gcc t.c# ./a.outread data: hello embedTime欢迎在评论区一起讨论,质疑。文章都是手打原创,每天最浅显的介绍C语言、linux等嵌入式开发,喜欢我的文章就关注一波吧,可以看到最新更新和之前的文章哦。

本文来自网络,不代表汉扬编程立场,转载请注明出处:http://www.hyzlch.com/mianfei/6177.html

没事了,搞一下C语言打印大小可变的菱形图案

输入100个学生的计算机成绩

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注

返回顶部