《Linux/Unix系统编程手册》读书笔记8 (文件I/O缓冲)

《Linux/Unix系统编程手册》读书笔记
目录

第13章

这章主要将了关于文件I/O的缓冲。

系统I/O调用(即内核)和C语言标准库I/O函数(即stdio函数)在对磁盘进行操作的时候都会发生缓冲。通过缓冲可以在一定程度上将用户空间与实际的物理设备分离,还可以减少内核访问磁盘的次数。

先来看看关于内核缓冲区高速缓冲:read和write调用在对磁盘文件进行操作的时候不会直接访问磁盘,如下图所示。

例如:write(fd, "abc", 3)
write调用会将"abc"从用户空间缓冲区传递内核缓冲区中并随即返回。在之后的某个时刻(缓冲区满了或者需要刷新),内核会将其缓冲区的数据写入到磁盘上,因此write系统调用与访问磁盘的操作不是同步执行的。同理,对于read(fd,
buffer,
3):内核会从磁盘读取数据并存在内核的缓冲区上,read调用再从内核缓冲区读取3个字节的数据到用户缓冲区。当缓冲区的全部数据被读取完,内核才会再从磁盘文件读取下一段数据。

这样的设计可以使得read和write系统调用不用等待磁盘操作从而加快操作的速度,还减少了内核访问磁盘的次数。

PS:Linux内核对缓冲区(内核缓冲区)的大小没有固定的上限,但是可用物理内存总量和其他进程对物理内存的需求会影响缓冲区的大小。

内核访问磁盘的字节数是固定的,所以尽量使得每次read(write)传输的字节数达到合适的数目可以减少系统调用所消耗的时间。

下表为书中的一个关于复制100MB大小的文件花费的时间,BUF_SIZE为传输的字节数, Elapsed为总共的用时,Total
CPU为CPU的总共用时,User CPU为用户CPU的用时,System CPU为系统CPU用时。测试的文件系统为块大小为4096字节的ext2。

当BUF_SIZE为4096字节的时候,达到最优性能,再继续增大BUF_SIZE不会对性能有太大的影响,是因为系统调用(read和write)花费的时间与在用户空间和内核空间之间传输数据以及实际磁盘操作所花费的时间对比已经微不足道。

再来看看第二个表,是关于写入一个100MB大小的文件所需的时间。

其实再进行write调用后并没有这么快执行磁盘I/O,因为实际计算机的RAM是很大(测试环境是4G),所以结合前表可以知道复制文件耗时绝大部分是用在磁盘的读取。

接着来看stdio库的缓冲。

stdio库的一些函数(fprintf, fscanf, fgets, fputs, fgets, fputc,
fgetc)会帮我们自动采取大块数据缓冲以减少系统调用。

通过setvbuf设置stdio库函数的缓冲方式

1 #include <stdio.h>
2
3 int setvbuf(FILE *stream, char *buf, int mode, size_t size);

成功调用返回0,失败返回非0值。

其中stream为文件流(PS:先打开文件流再调用setvbuf),buf为使用的缓冲区,size为缓冲区的大小。当buf不为NULL,就指向size大小的内存块作为stream的缓冲区;当buf为NULL,stdio库会为stream自动分配一个缓冲区。mode为缓冲的类型。

mode的取值:












_IONBF 不对I/O进行缓冲,即立即调用write和read
_IOLBF 采用行缓冲I/O(终端设备的流的默认采用)
_IOFBF  单次读写数据的大小与缓冲区相同(磁盘的流默认采用)

除了setvbuf还有setbuf和setbuffer


1 #define _BSD_SOURCE   //获取setbuffer的声明
2 #include <stdio.h>
3
4 void setbuf(FILE *stream, char *buf);
5
6 void setbuffer(FILE *stream, char *buf, size_t size);
7

还有通过fflush刷新stdio缓冲区

1 #include <stdio.h>
2
3 int fflush(FILE *stream);

成功调用返回0,失败返回EOF。

如果stream为NULL,fflush会刷新所有的缓冲区。

如果将fflush用在输入流,可以将已缓冲的输入数据全部丢弃。

在C函数库的实现中,如果stdin和stdout指向同一个终端,那么从stdin读取输入时都会隐含调用fflush(stdout)。

----------------------暂时省略同步I/O。。。。这部分翻译的很坑,看不懂。。。。

----------------------还有直接I/O。。。。。。。。。。。。。。。。。。。。。。

下图为I/O缓冲小结:

练习:

13-5. tail [ -n num ] file
命令打印名为file文件的最后路面行(默认为10行)。使用I/O系统调用(lseek()、read()、write()等)来实现该命令。


  1 /*
2 * =====================================================================================
3 *
4 * Filename: 13.5.c
5 *
6 * Description: 简单tail实现,可能存在bug,但是没有找到!!!
7 *
8 * Version: 1.0
9 * Created: 2014年05月02日 18时58分15秒
10 * Revision: none
11 * Compiler: gcc
12 *
13 * Author: alan (), [email protected]
14 * Organization:
15 *
16 * =====================================================================================
17 */
18
19 #include <sys/stat.h>
20 #include <fcntl.h>
21 #include <ctype.h>
22 #include "tlpi_hdr.h"
23
24 #define BUF_SIZE 4096
25
26 int main(int argc, char *argv[]){
27 off_t seek, off, offset = 0;
28 int whence, fd, num, numRead, type = 0, i, off_cnt = 1, n_cnt = 0;
29 Boolean flag = FALSE;
30 struct stat statbuf;
31 char *file;
32 char buf[BUF_SIZE+1];
33
34 if(strcmp(argv[1], "--help") == 0)
35 usageErr("%s [ -n num ] file", argv[0]);
36
37 //获取文件名
38 file = argv[1];
39
40 if(argc == 4 && strcmp(argv[1], "-n") == 0){
41 flag = TRUE;
42 file = argv[3];
43 }
44
45 //获取最后的行数
46 num = (flag == TRUE) ? getInt(argv[2], GN_GT_0, "num") : 10;
47
48 //打开文件对应的文件描述符
49 fd = open(file, O_RDONLY);
50 if(fd == -1)
51 errExit("open");
52
53 if(fstat(fd, &statbuf) == -1)
54 errExit("fstat");
55
56 //判断文件的大小是否超过4096字节
57 if(statbuf.st_size <= BUF_SIZE){
58 off = 0;
59 whence = SEEK_CUR;
60 type = 1;
61 }
62 else{
63 off = -1 * BUF_SIZE;
64 whence = SEEK_END;
65 }
66
67 //根据换行符判断行数
68 while((seek = lseek(fd, off_cnt * off, whence)) != -1){
69 numRead = read(fd, buf, BUF_SIZE);
70 if(numRead == -1)
71 errExit("read");
72 if(numRead > 0){
73 for(i = numRead-1; i >=0; --i){
74 if(buf[i] == ‘\n‘)
75 n_cnt++;
76 if(n_cnt == num+1)
77 break;
78 }
79
80 if(n_cnt == num+1){
81 offset += (numRead-1 - i);
82 break;
83 }
84 else
85 offset += numRead;
86 if(type)
87 break; //如果行数小于要求的,当文件的偏移量回到文件的开始位置,即buf从数组结尾回到数组的头部时,跳出循环。
88 }
89 off_cnt++;
90 memset(buf, 0, BUF_SIZE+1);
91 }
92 if(seek == -1)
93 errExit("lseek");
94
95 if(lseek(fd, (0 - offset), SEEK_END) == -1)
96 errExit("lseek");
97
98 while((numRead = read(fd, buf, BUF_SIZE)) > 0){
99 buf[numRead] = ‘\0‘;
100 printf("%s", buf);
101 memset(buf, 0, BUF_SIZE+1);
102 }
103 if(numRead == -1)
104 errExit("read");
105
106 exit(EXIT_SUCCESS);
107 }

测试结果:

一、


[email protected]:~/Code/tlpi$ ./13.5 13.5.c
while((numRead = read(fd, buf, BUF_SIZE)) > 0){
buf[numRead] = ‘\0‘;
printf("%s", buf);
memset(buf, 0, BUF_SIZE+1);
}
if(numRead == -1)
errExit("read");

exit(EXIT_SUCCESS);
}
[email protected]:~/Code/tlpi$ tail 13.5.c
while((numRead = read(fd, buf, BUF_SIZE)) > 0){
buf[numRead] = ‘\0‘;
printf("%s", buf);
memset(buf, 0, BUF_SIZE+1);
}
if(numRead == -1)
errExit("read");

exit(EXIT_SUCCESS);
}

二、


[email protected]:~/Code/tlpi$ tail -n 15 13.5.c
errExit("lseek");

if(lseek(fd, (0 - offset), SEEK_END) == -1)
errExit("lseek");

while((numRead = read(fd, buf, BUF_SIZE)) > 0){
buf[numRead] = ‘\0‘;
printf("%s", buf);
memset(buf, 0, BUF_SIZE+1);
}
if(numRead == -1)
errExit("read");

exit(EXIT_SUCCESS);
}
[email protected]:~/Code/tlpi$ ./13.5 -n 15 13.5.c
errExit("lseek");

if(lseek(fd, (0 - offset), SEEK_END) == -1)
errExit("lseek");

while((numRead = read(fd, buf, BUF_SIZE)) > 0){
buf[numRead] = ‘\0‘;
printf("%s", buf);
memset(buf, 0, BUF_SIZE+1);
}
if(numRead == -1)
errExit("read");

exit(EXIT_SUCCESS);
}

---------------多点使用系统调用和库函数才会熟练,只要熟练就编程才不会觉得很难上手啊。。。。。。

---------------另外那篇关于动态规划的算法导论学习记录感觉写不下去了。。。。。。。。。。。。。。

---------------继续努力!!!!!!

《Linux/Unix系统编程手册》读书笔记8 (文件I/O缓冲),布布扣,bubuko.com

时间: 05-03

《Linux/Unix系统编程手册》读书笔记8 (文件I/O缓冲)的相关文章

《Linux/Unix系统编程手册》读书笔记7 (/proc文件的简介和运用)

<Linux/Unix系统编程手册>读书笔记 目录 第11章 这章主要讲了关于Linux和UNIX的系统资源的限制. 关于限制都存在一个最小值,这些最小值为<limits.h>文件中的常量. 通过cat 命令查看: [email protected]:~/Code/tlpi$ cat /usr/include/limits.h /* Copyright (C) 1991, 1992, 1996, 1997, 1998, 1999, 2000, 2005 Free Software

《Linux/Unix系统编程手册》读书笔记9(文件属性)

<Linux/Unix系统编程手册>读书笔记 目录 在Linux里,万物皆文件.所以文件系统在Linux系统占有重要的地位.本文主要介绍的是文件的属性,只是稍微提及一下文件系统,日后如果有更深入的研究一定会写出来. 下图为磁盘分区与文件系统的关系 文件系统中的文件(目录)在i-node表上都有唯一的记录(i-node).i-node通过数据块指针指向数据块,这些数据块就是该i-node对应的文件的数据. i-node与数据块的关系如下: 因为Linux支持很多类型的文件系统,但是每种文件系统的

Linux/Unix系统编程手册--SOCKET章节读书笔记

SOCKET章节读书笔记 强烈推荐Linux/Unix系统编程手册,号称超越APUE的神书. backlog含义 #include <sys/socket.h> int listen(int socketfd, int backlog) backlog参数限制未决连接(未accept)的数量,在这个数量之内,connect会立刻成功. Linux上上限为128,定义在 udp已连接socket udp socket也是可以调用connect()的,这种叫已连接socket,内核会记录这个soc

Linux/UNIX系统编程手册 PDF下载

网盘下载地址:Linux/UNIX系统编程手册 PDF下载 – 易分享电子书PDF资源网 作者: Michael Kerrisk 出版社: 人民邮电出版社 原作名: The Linux Programming Interface: A Linux and UNIX System Programming Handbook 译者: 孙剑 许从年 董健 / 孙余强 郭光伟 陈舸 出版年: 2014-1 页数: 1176 定价: 158 装帧: 平装 内容简介 · · · · · · <linux/un

TLPI(liunx/unix系统编程手册)笔记(三) 文件IO:通用的IO模型

读下来总的就是介绍了四个IO的API--open,read,write,close. 大家都是知道的,everything is file,在linux系统里面一切都是看作文件来操作的,学习linux就得先学好文件IO,也可以看见TLPI这本书的介绍完一些概念之后就开始介绍文件IO了. IO,大概的分为磁盘文件IO,buffering(缓冲)IO.貌似缓冲的水很深,之后会写博客. ------------------------------------ (1)文件描述符. 在进行IO操作之前,总

TLPI(liunx/unix系统编程手册)笔记(四) 深入探究文件I/O

本章的重点我想就是原子操作,避免在几个进程在打开同一文件的时候造成的错误,了解一下时间片的概念会对本章有所帮助. (1)独占方式打开文件.(open     <-O_CREAT) 知道,open,可以创建文件并返回fd.当我们的进程运行到open这个函数时间片到了,另一个进程也对这个路径的文件open,那么时间片结束后两个进程都会认为自己是这个文件的拥有者.并未是独占创建打开的.在open 函数的第二个参数中有 O_EXCL 这种打开方式,可以解决独占的问题.另外可以在多进程对一个文件写的时候,

Linux/UNIX系统编程手册 练习3.8

闲来无聊看了看  3-1. 使用Linux专有的reboot系统调用重启系统时,必须将第二个参数magic2定义为一组magic号(例如:LINNUX_REBOOT_MAGIC2).这些号有何意义?(将magic号转换为十六进制数,对解题会有所帮助.) 刚看的时候 有点晕就去查了查 转换成16进制了 . 结果发现 是彩蛋.... 原文地址:https://www.cnblogs.com/jingchu/p/10262241.html

linux/unix系统编程手册源码编译

我的操作,我在官网下载 书本源代码 ,在windows 解压后 ftp工具 传到 linux虚拟机 然后 make 发现 报错 如: No rule to make target `../libtlpi.a', needed by 郁闷好久! 方案: 把源代码压缩包 ftp到虚拟机 在解压缩,make 就成功了! 注意,习惯: 永远在linux解压缩

Linux Unix shell 编程指南学习笔记(第一部分)

第一章:文件安全与权限: 1.文件和目录的权限 创建文件时系统保存了文件所有相关的信息,包括 文件的位置 . 文件类型 . 文件长度 . 哪位用户拥有该文件,哪些用户可以访问该文件 . i 节点 . 文件的修改时间 . 文件的权限位 . 文件类型: d: 目录 l : 符号链接(指向另一个文件) s: 套接字文件 b: 块设备文件 c: 字符设备文件 p: 命名管道文件 -: 不属于上述类型的文件 文件权限 XXX       XXX        XXX 最左边 XXX : 文件属主 权限位