《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库函数的缓冲方式

 #include <stdio.h>

 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

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

还有通过fflush刷新stdio缓冲区

 #include <stdio.h>

 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()等)来实现该命令。

 /*
* =====================================================================================
*
* Filename: 13.5.c
*
* Description: 简单tail实现,可能存在bug,但是没有找到!!!
*
* Version: 1.0
* Created: 2014年05月02日 18时58分15秒
* Revision: none
* Compiler: gcc
*
* Author: alan (), alan19920626@gmail.com
* Organization:
*
* =====================================================================================
*/ #include <sys/stat.h>
#include <fcntl.h>
#include <ctype.h>
#include "tlpi_hdr.h" #define BUF_SIZE 4096 int main(int argc, char *argv[]){
off_t seek, off, offset = ;
int whence, fd, num, numRead, type = , i, off_cnt = , n_cnt = ;
Boolean flag = FALSE;
struct stat statbuf;
char *file;
char buf[BUF_SIZE+]; if(strcmp(argv[], "--help") == )
usageErr("%s [ -n num ] file", argv[]); //获取文件名
file = argv[]; if(argc == && strcmp(argv[], "-n") == ){
flag = TRUE;
file = argv[];
} //获取最后的行数
num = (flag == TRUE) ? getInt(argv[], GN_GT_0, "num") : ; //打开文件对应的文件描述符
fd = open(file, O_RDONLY);
if(fd == -)
errExit("open"); if(fstat(fd, &statbuf) == -)
errExit("fstat"); //判断文件的大小是否超过4096字节
if(statbuf.st_size <= BUF_SIZE){
off = ;
whence = SEEK_CUR;
type = ;
}
else{
off = - * BUF_SIZE;
whence = SEEK_END;
} //根据换行符判断行数
while((seek = lseek(fd, off_cnt * off, whence)) != -){
numRead = read(fd, buf, BUF_SIZE);
if(numRead == -)
errExit("read");
if(numRead > ){
for(i = numRead-; i >=; --i){
if(buf[i] == '\n')
n_cnt++;
if(n_cnt == num+)
break;
} if(n_cnt == num+){
offset += (numRead- - i);
break;
}
else
offset += numRead;
if(type)
break; //如果行数小于要求的,当文件的偏移量回到文件的开始位置,即buf从数组结尾回到数组的头部时,跳出循环。
}
off_cnt++;
memset(buf, , BUF_SIZE+);
}
if(seek == -)
errExit("lseek"); if(lseek(fd, ( - offset), SEEK_END) == -)
errExit("lseek"); while((numRead = read(fd, buf, BUF_SIZE)) > ){
buf[numRead] = '\0';
printf("%s", buf);
memset(buf, , BUF_SIZE+);
}
if(numRead == -)
errExit("read"); exit(EXIT_SUCCESS);
}

测试结果:

一、

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

二、

lancelot@debian:~/Code/tlpi$ tail -n  13.5.c
errExit("lseek"); if(lseek(fd, ( - offset), SEEK_END) == -)
errExit("lseek"); while((numRead = read(fd, buf, BUF_SIZE)) > ){
buf[numRead] = '\0';
printf("%s", buf);
memset(buf, , BUF_SIZE+);
}
if(numRead == -)
errExit("read"); exit(EXIT_SUCCESS);
}
lancelot@debian:~/Code/tlpi$ ./13.5 -n 13.5.c
errExit("lseek"); if(lseek(fd, ( - offset), SEEK_END) == -)
errExit("lseek"); while((numRead = read(fd, buf, BUF_SIZE)) > ){
buf[numRead] = '\0';
printf("%s", buf);
memset(buf, , BUF_SIZE+);
}
if(numRead == -)
errExit("read"); exit(EXIT_SUCCESS);
}

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

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

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

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

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

    <Linux/Unix系统编程手册>读书笔记1  (创建于4月3日,最后更新4月7日) <Linux/Unix系统编程手册>读书笔记2  (创建于4月9日,最后更新4月10日) ...

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

    <Linux/Unix系统编程手册>读书笔记 目录 在Linux里,万物皆文件.所以文件系统在Linux系统占有重要的地位.本文主要介绍的是文件的属性,只是稍微提及一下文件系统,日后如果有 ...

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

    <Linux/Unix系统编程手册>读书笔记 目录 第11章 这章主要讲了关于Linux和UNIX的系统资源的限制. 关于限制都存在一个最小值,这些最小值为<limits.h> ...

  4. 《Linux/Unix系统编程手册》读书笔记6

    <Linux/Unix系统编程手册>读书笔记 目录 第9章 这章主要讲了一堆关于进程的ID.实际用户(组)ID.有效用户(组)ID.保存设置用户(组)ID.文件系统用户(组)ID.和辅助组 ...

  5. 《Linux/Unix系统编程手册》读书笔记5

    <Linux/Unix系统编程手册>读书笔记 目录 第8章 本章讲了用户和组,还有记录用户的密码文件/etc/passwd,shadow密码文件/etc/shadow还有组文件/etc/g ...

  6. 《Linux/Unix系统编程手册》读书笔记4

    <Linux/Unix系统编程手册>读书笔记 目录 第7章: 内存分配 通过增加堆的大小分配内存,通过提升program break位置的高度来分配内存. 基本学过C语言的都用过mallo ...

  7. 《Linux/Unix系统编程手册》读书笔记3

    <Linux/Unix系统编程手册>读书笔记 目录 第6章 这章讲进程.虚拟内存和环境变量等. 进程是一个可执行程序的实例.一个程序可以创建很多进程. 进程是由内核定义的抽象实体,内核为此 ...

  8. 《Linux/Unix系统编程手册》读书笔记1

    <Linux/Unix系统编程手册>读书笔记 目录 最近这一个月在看<Linux/Unix系统编程手册>,在学习关于Linux的系统编程.之前学习Linux的时候就打算写关于L ...

  9. 《Linux/Unix系统编程手册》读书笔记2

    <Linux/Unix系统编程手册>读书笔记 目录 第5章: 主要介绍了文件I/O更深入的一些内容. 原子操作,将一个系统调用所要完成的所有动作作为一个不可中断的操作,一次性执行:这样可以 ...

随机推荐

  1. Microsoft SQL Server 存储过程举例

    -- if SP is existed, drop it. if (object_id('InvHoldToDPS', 'P') is not null) drop proc InvHoldToDPS ...

  2. How to find and fix Bash Shell-shock vulnerability CVE-2014-6271 in unix like system

    type command - env x='() { :;}; echo vulnerable' bash -c 'echo hello' in your terminal.   if your sy ...

  3. css3动画实例

    <!doctype html> <html> <head> <meta charset="utf-8"> <title> ...

  4. JS 学习笔记--12---面向对象

    练习中使用的浏览器为IE10,如果各位朋友有不同意见或者本文有什么错误地方,望指正 ECMASCript有两种开发模式:函数式(面向过程)和面向对象.面向对象有一个很明显的标志,那就是类,我们可以通过 ...

  5. GameMap地图初始化

    init_map(res_path) .初始化mapbase的基本信息 pos2d screen_area = {, }; //普通屏幕大小 m_spBase->init(screen_area ...

  6. html之cellspacing && cellpadding讲解

    单元格间距(表格间距)(cellspacing) -- 代表表格边框与单元格补白的距离,也是单元格补白之间的距离 单元格边距(表格填充)(cellpadding) -- 代表单元格外面的一个距离,用于 ...

  7. 十个实用但IE不支持的CSS属性

    对IE浏览器尤其是IE6的抱怨基本已进入麻痹状态,偶尔甚至产生非常消极的想法:这个世界只有一个浏览器就好了,哪怕这唯一的浏览器就是IE6.当然,这样的想法是非常病态的,马上打消.本文里面,介绍了10个 ...

  8. 全栈式JavaScript

    如今,在创建一个Web应用的过程中,你需要做出许多架构方面的决策.当然,你会希望做的每一个决定都是正确的:你想要使用能够快速开发的技术,支持持续的迭代,最高的工作效率,迅速,健壮性强.你想要精益求精并 ...

  9. Java对象初始化详解

    在Java中,一个对象在可以被使用之前必须要被正确地初始化,这一点是Java规范规定的.本文试图对Java如何执行对象的初始化做一个详细深入地介绍(与对象初始化相同,类在被加载之后也是需要初始化的,本 ...

  10. HDU3487 Play With Chains(Splay)

    很裸的Splay,抄一下CLJ的模板当作复习,debug了一个下午,收获是终于搞懂了以前看这个模板里不懂的内容.以前用这个模板的时候没有看懂为什么get函数返回的前缀要加个引用,经过一下午的debug ...