【Unix】文件IO与标准IO

一、文件IO

大多数UNIX文件IO只需用到5个函数:open、read、write、lseek以及close。

这些函数经常被称为不带缓存的IO(unbuffered IO),不带缓存是指每个read和write都调用内核中的一个系统调用。

这些不带缓存的IO函数不是ANSI C的组成部分,但是是POSIX.1和XPG3的组成部分。

1、对于内核而言,所有打开文件都由文件描述符引用。

2、由open返回的文件描述符一定是最小的未用描述符数字。

3、可用close函数关闭一个文件,关闭一个文件时也释放该进程加在该文件上的所有记录锁。

当一个进程终止时,它所有的打开文件都由内核自动关闭。

4、每个文件都有一个与其相关联的“当前文件位移量”,用以度量从文件开始处计算的字节数。

可以调用lseek显式地定位一个打开文件。

5、用read函数从打开文件中读取数据。如read成功,则返回读到的字节数,如已到达文件的尾端,则返回0。

6、用write函数向打开文件写数据,若成功返回已写的字节数,若出错为-1。

对Unix内核而言,文本文件和二进制代码文件并无区别。

7、IO效率

当Buffersize定义为文件系统的快长时,系统CPU时间最小,继续增加缓存长度对此文件并无影响。

 

二、文件共享

1、Unix支持在不同进程间共享打开文件。内核使用了三种数据结构。

1)每个进程在进程表中都有一个记录项,每个记录项中有一张打开文件描述符表。与每个文件描述符相关联的是:

文件描述符标志

指向一个文件表项的指针

2)内核为所有打开文件维持一张文件表,每个文件表项包含:

文件状态标志(读、写、增写、同步、非阻塞等)

当前文件位移量

指向该文件v节点表项的指针

3)每个打开文件(或设备)都有一个v节点结构。v节点包含了文件类型和对此文件进行各种操作的函数的指针信息。

2、这三张表的关系对于在不同进程之间共享文件的方式非常重要。

3、文件描述符标志和文件状态标志在作用范围方面的区别,前者只用于一个进程的一个描述符,而后者则适用于指向该给定文件表项

的任何进程中的所有描述符。

 

三、原子操作以及文件控制

1、打开文件时设置O_APPEND标志。

2、dup和dup2函数可以用来复制一个现存的文件描述符。

3、fcntl函数可以改变以及打开文件的性质。

4、ioctl函数是IO操作的杂物箱。不能用其他函数表示的IO操作通常都能用ioctl表示。

终端IO是ioctl的最大使用方面。

5、比较新的系统都提供名为/dev/fd的目录,其目录项名为0、1、2等的文件。打开文件/dev/fd/n等效于复制描述符n。

 

四、标准IO库

1、标准IO库,不仅在Unix而且在很多操作系统上都实现此库,它由ANSI C标准说明。

标准IO库处理很多细节,例如缓存分配,以优化长度执行IO等。

2、对于标准IO库,它们的操作是围绕流进行的。当用标准IO库打开或创建一个文件时,我们已使一个流与一个文件相结合。

当打开一个流时,标准IO函数fopen返回一个指向FILE对象的指针。

该对象通常是一个结构,包含了:用于实际IO的文件描述符,指向流缓存的指针,缓存长度,当前缓存字符数,出错标志等。

为了引用一个流,需将FILE指针作为参数传递给每个标准IO函数。

 

五、缓存

1、标准IO提供缓存的目的是尽可能减少使用read和write调用的数量,它也对每个IO流自动地进行缓存管理。

2、标准IO提供了三种类型的缓存:

1)全缓存。在这种情况下,当填满标准IO缓存后才进行实际IO操作。对于驻在磁盘上的文件通常是由标准IO库实施全缓存的。

术语flush说明标准IO缓存的写操作。

2)行缓存。在这种情况下,当在输入和输出中遇到新行符时,标准IO库执行IO操作。

当流涉及一个终端时(例如标准输入和标准输出),典型地使用行缓存。

3)不带缓存。标准IO库不对字符进行缓存。如果用标准IO函数写若干字符到不带缓存的流中,则相当于用write系统调用

函数将这些字符写到相关联的打开文件上。

标准出错流stderr通常是不带缓存的,这就使得出错信息可以尽快显示出来,而不管它们是否含有一个新行字符。

3、ANSI C要求下列缓存特征:

1)当且仅当标准输入和标准输出并不涉及交互作用设备时,它们才是全缓存的。

2)标准出错绝不会是全缓存的。

4、可以通过setbuf和setvbuf函数来更改缓存类型。

5、任何时候,我们都可以使用函数fflush强制刷新一个流。

此函数使该流所有未写的数据都被传递至内核。

 

六、流

1、打开流,有如下三个函数:

fopen、freopen、fdopen

2、调用fclose函数关闭一个打开的流。

3、一旦打开了流,则可在三种不同类型的非格式化IO中进行选择,对其进行读、写操作。

1)每个一个字符的IO。一次读或写一个字符,如果流是带缓存的,则标准IO函数处理所有缓存。

2)每次一行的IO。使用fgets和fputs一次读或写一行。每行都以一个新行符终止。

3)直接IO。fread和fwrite函数支持这种类型的IO。

4、输入函数

以上三个函数可用于一次读一个字符:

getc、fgetc、getchar getchar等同于getc(stdin)

不管是出错还是到达文件尾端,这三个函数都返回同样的值。为了区分这两种不同的情况,必须调用ferror或feof。

在大多数实现的FILE对象中,为每个流保持了两个标志:

出错标准、文件结束标志

调用clearerr则清除这两个标志。

5、从一个流读之后,可以调用ungetc将字符再送回流中。EOF不能回送。

6、输出函数

对应于上面所述的每个输入函数都有一个输出函数:

putc、fputc、putchar putchar(c)等同于putc(c, stdout)

7、每次一行IO

下面两个函数提供每次输入一行的功能:

fgets、gets

下面两个函数提供每次输入一行的功能:

fputs、puts

 

七、标准IO的效率

1、系统调用与普通的函数调用相比是很花费时间的。

2、标准IO库与直接调用read和write函数相比并不慢很多。

 

八、二进制IO

使用下列两个函数执行二进制IO操作:

fread、fwrite

 

九、定位流

1、有两种方法定位标准IO流

1)ftell和fseek。

2)fgetpos和fsetpos。

 

十、格式化IO

1、执行格式化输出处理的是三个printf函数:

printf 将格式化数据写到标准输出

fprintf 写到指定的流

sprintf 将格式化的字符送入数组buf中

2、执行格式化输入处理的是三个scanf函数:

scanf

fscanf

sscanf

 

十一、实现细节

1、在Unix中,标准IO库最终都要调用IO例程。每个IO流都有一个与其关联的文件描述符,可以通过fileno函数获得其描述符。

2、标准IO库提供了两个函数以帮助创建临时文件

tmpnam、tmpfile

 

十二、总结

东西比较多也比较杂,但是保持一个清晰的头脑是最重要的。

多思考一下遇到的问题,以及如何解决这些问题,可以明白现在的实现机制已经是多么好的一种方式了。

 

 

 

 

你可能感兴趣的:(Unix)