APUE之第5章——标准I/O库

一、知识回顾:文件I/O
  • 文件 I/O 是不带缓冲的 I/O(unbuffered I/O),指每个 read 和 write 都调用内核中的一个系统调用。
  • 对于内核而言,所有打开的文件都通过文件描述符引用,即不带缓冲的 I/O 通过文件描述符的方式来引用一个文件。
  • 大多数文件 I/O 只需用到5个函数:open、read、write、lseek、close。
二、标准I/O
  • 标准 I/O 库由 ISO C 标准说明。
  • 标准 I/O 库的操作围绕流进行,即带缓冲的 I/O 则通过文件流(stream)的方式来引用文件。
  • 引入标准 I/O 库的目的是为了提高 I/O 的效率,尽可能地减少使用 read 和 write 调用的次数。因为系统调用会消耗较多的资源,标准 I/O 库提供了缓冲,可以通过累积一定量的数据后,集中写入到实际的文件中来减少系统调用。这样文件的读写就要经过缓冲区做缓冲,从而提高 I/O 效率。
    假设,每个人家里喝的水都是从自来水厂来的,自来水厂的水又是从水源地来的,这当中流动的水,就是水流,可以理解为IO流,
    在 UNIX 中,UNIX 就是自来水厂,水源地就是源,家就是目的。则从水源到自来水厂的水流,就是IO输入流,从自来水厂到家的水流,就是IO输出流。
    如果没有蓄水池,则每次用水都要运送一次。一万次用水就是运送一万次,这样的效率很低。若在水源地与自来水厂、自来水厂与家之间分别建造蓄水池,
    则自来水厂可以随时从蓄水池中取水、为用户供水,我们需要用水时也能随时有水可用。这里的蓄水池就可以类比成缓冲。
 
三、标准 I/O 与文件 I/O 的区别
 
  标准I/O
文件 I/O
遵循标准
标准 I/O 被称为高级磁盘I/O,遵循 ANSI C 相关标准。只要开发环境中有标准 I/O库,标准 I/O 就可以使用。
文件 I/O 被称为低级磁盘 I/O,遵循 POSIX 相关标准。任何兼容 POSIX 标准的操作系统上都支持文件 I/O。
缓冲机制
标准 I/O 默认采用缓冲机制,可以看成是在文件 I/O 的基础上封装了缓冲机制。先读写缓冲区,必要时再访问实际文件,从而减少了系统调用的次数。
低级 I/O 一般没有采用缓冲,需要自己创建缓冲区。通过文件 I/O 读写文件时,每次操作都会执行相关系统调用。这样处理的好处是直接读写实际文件,坏处是频繁的系统调用会增加系统开销。
操作设备
标准 I/O 中用 FILE(流)表示一个打开的文件,通常用来访问普通文件。针对的是控制台,打印输出到屏幕等。
文件 I/O 中用文件描述符表现一个打开的文件,可以访问不同类型的文件如普通文件、设备文件和管道文件等。主要针对的是文件操作、读写硬盘等。
以下为标准 I/O 函数和文件 I/O 函数的对比
打开
fopen,freopen,fdopen
open
fopen与open
标准 I/O 使用 fopen 函数打开一个指定文件。
pathname 是文件名。type 参数指定对I/O流的读、写方式,主要有"r"(只读打开),"r+"(读写打开),"w"(只写打开),"w+"(读写打开),"a"(追加打开)、"a+"(读和追加打开),可以加上字母 b 用以指定以二进制模式打开文件。
 
若成功,会返回一个 FILE 类型的指针,作为我们打开文件的凭据,后面所有对这个文件的操作都需要使用这个文件指针,使用之后要调用 fclose 函数释放资源。
若失败,会返回一个指向 NULL 的指针,表示文件打开失败了,可以通过 errno 获取到具体失败的原因。
文件 I/O 使用 open 函数打开或创建一个文件。
path 参数是要打开或创建的文件名。aflag 参数可用来说明文件的打开模式,主要有 O_RDONLY(只读打开),O_WRONLY(只写打开),O_RDWR(读、写打开) 等。
 
若成功,返回文件描述符;失败则返回-1。
关闭
fclose
close
fclose与close
标准 I/O 使用 fclose 函数关闭一个打开的流。
如果成功关闭,返回 0,失败则返回 EOF。
文件 I/O 使用 close 函数关闭一个打开的文件。
如果成功关闭,返回 0,失败则返回-1,而不是 EOF。
getc,fgetc,getchar,
fgets,gets,
fread
read
 
标准 I/O 可以用 3 种方式进行文件读取。
  • 每次一个字符的I/O
函数getchar等同于getc(stdin)。前两个函数的区别是,getc可被实现为宏,而fgetc不能实现宏。
若成功,返回下一个字符;若已达到文件尾端或出错,则返回 EOF。
  • 每次一行的I/O
这两个函数都指定了缓冲区的地址,读入的行将送入其中。gets从标准输入读,而fgets从指定的流读。
gets(不建议使用):接受一个参数,不判断目标数组是否能够容纳读入的字符,可能导致存储溢出;
fgets使用三个参数:第一个参数和gets一样,用于存储输入的地址,第二个参数为整数,表示输入字符串的最大长度,最后一个参数就是文件指针,指向要读取的文件。
若成功,返回 buf;若已达到文件尾端或出错,则返回 NULL。
  • 直接I/O(二进制 I/O)
若成功,返回读或写的对象数。如果出错或达到文件尾端,则此数字可以少于 nobj。
文件 I/O 使用 read 函数从打开的文件中读取数据。
其中 fd 就是 open 返回的文件描述符,buf用于存储数据的目的缓冲区,而 nbytes 指定要读取的字节数。
 
如果成功读取,就返回读取的字节数,如已达到文件的尾端,则返回 0。如果失败,则返回-1。
putc,fputc,putchar,
fputs,puts,
fwrite
write
 
标准 I/O 可以用 3 种方式进行文件写入。
  • 每次一个字符的I/O
第一个参数是字符,第二个参数是文件指针。
putchar(c)等同于putc(stdin),putc可被实现为宏,而fputc不能。
若成功,返回c;失败则返回 EOF。
  • 每次一行的I/O
fputs 函数将一个 null 字节终止的字符串写到指定的流,尾端的终止符 null不写出。这并不一定是每次输出一行,因为字符串不需要换行符作为最后一个非null字节。通常,在 null 字节之前是一个换行符
puts 将一个 null 字节终止的字符串写到标准输出,终止符不写出。但是,puts 随后又将一个换行符写到标准输出。
若成功,返回非负值;失败则返回 EOF。
  • 直接I/O(二进制 I/O)
若成功,返回读或写的对象数。如果出错或达到文件尾端,则此数字少于 nobj。
文件 I/O 使用 write 函数向打开的文件写数据。
其中 fd 就是文件描述符,buf是要写入的内存数据,而 nbytes 指定要写的字节数。
 
如果成功写入,就返回已写的字节数,失败则返回-1。
随机存取
ftell,fseek
lseek
 
标准I/O 使用 fseek 和 ftell 完成文件的随机存取。
fseek 函数的第一个参数是文件指针,第二个参数是一个 long 类型的偏移量(offset),表示从起始点开始移动的距离。第三个参数就是用于指定起始点的模式,主要有 SEEK_SET(文件开始处)、SEEK_CUR(当前位置)、SEEK_END(文件结尾处)。
成功,返回0,出错,返回-1。
 
ftell 函数用于返回文件的当前位置,返回类型是一个 long 类型。
成功,返回当前文件位置指示,出错,返回-1L。
文件I/O 使用 lseek 来完成文件的随机存取。
fd 是文件描述符,offset 是偏移量,whence 指定起始点模式,取值与fseek相同:SEEK_SET,SEEK_CUR,SEEK_END,但也可以用整数0,1,2相应代替。
与fseek 不同的是,lseek有返回值,如果成功就返回指针变化前的位置,否则返回-1。
 
四、一图泛读 APUE 之标准 I/O 库
 
五、strace 命令跟踪 PHP 函数的系统调用
strace是一个可用于诊断、调试的Linux用户空间跟踪器,可以用它来监控用户空间进程和内核的交互,比如系统调用、信号传递、进程状态变更等。
创建一个php文件,内容如下:
<?php
    $file = fopen("test.txt", "wr");
    fwrite($file, "test\n");
    fclose($file);
?>

执行如下命令:

strace php open.php

可以看到strace的结果:

lstat 函数返回文件的信息。
open打开文件,第二个参数是文件的打开模式,
    O_WRONLY 只读打开,
    O_CREAT 此文件不存在则创建, 
    O_TRUNC 如果此文件存在,而且为只读或读-写成功打开,则将其长度截断为0。
fstat 函数获得已在文件描述符 fd 打开文件的有关信息。
lseek 函数为一个打开的文件设置偏移量。
write 写入内容
close 关闭文件
由此可以看出,PHP文件函数其实就是文件I/O上的一些封装。
posted @ 2019-07-16 23:31  鹿呦呦  阅读(335)  评论(0编辑  收藏  举报