在unix中可用的文件I/O函数包含打开文件,读文件,写文件等。

Unix系统中的大多数文件I/O须要用到5个函数:open,read,write,lseek,close.

这里要说明的是read,write的文件I/O都是不带缓冲的,所谓的不带缓冲意思是它们都是走的内核中的一个系统调用。

对于内核而言,全部打开的文件都是通过文件描写叙述符进行引用,文件描写叙述符是一个非负整数。当打开一个现有文件或创建一个新文件时,内核向进程返回一个文件描写叙述符。当读或写一个文件时。使用open,create返回文件描写叙述符标识该文件。然后把返回的文件描写叙述符作为參数传递给read,write。

普通情况下unix系统的shell使用文件描写叙述符0作为进程的标准输入,1为标准输出,2为标准错误输出,不同的shell版本号可能有所不同,与unix内核无关。

文件描写叙述符的范围是0~open_max 早期的unix系统规定每个进程最多使用20个文件描写叙述符。

open函数

由open返回的文件描写叙述符是一个最小的未被使用的描写叙述符数值。这一点被有些程序是如今在标准输入,输出,报错上打开新文件。

比方。一个程序先关闭标准输出,然后再打开还有一个文件。事实上我们能够通过dup2函数来实现控制在制定的文件描写叙述符上打开文件。

Creat函数

Creat的一个不足之处是它以仅仅读方式打开所创建的文件。

在open的老版本号中,假设要创建一个暂时文件,并要先写该文件。然后又要读该文件,则必须先调用creat,close,然后调用open.只是如今的新版本号open已经攻克了这些不便利。

Close函数

当关闭一个文件时还会释放该进程加在该文件上的全部记录锁(有关记录锁有关内容,能够查看博客关于记录锁的文章)。

当一个进程终止时,内核会自己主动关闭它打开的全部文件。

非常多程序利用了这一功能而不显示的调用close关闭打开的文件。

Lseek函数

每个打开的文件都有一个与其相关联的“当前文件偏移量”它一般是一个非负整数,用以度量从文件開始处计算的字节数。通常,读,写操作都从当前文件偏移量处開始,并使偏移量添加所读写的自己数,当打开一个文件的最初时候文件的偏移量为0.

一般,文件的当前偏移量应当是一个非负整数,可是。某些设备也可能同意负的偏移量。但对于普通文件。则其偏移量必须是非负值。由于偏移量可能是负值,所以在比較lseek的返回值时不能检測是否小于0而是測试它是否等于-1.

Lseek仅将当前的文件偏移量记录在内核中,它并不引起不论什么I/O操作。然后,该偏移量用与下一个读或是写操作。

文件偏移量能够大于文件的当前长度,在这样的情况下,对该文件的下一次写将加长该文件并在文件里造成一个空洞,这一定是同意的。

位于文件里但没有写过的字节都被读为0.

文件里的空洞并不要求在磁盘上占用存储区。详细处理方式与文件系统的实现有关,当定位到超出文件尾端之后写时,对于新写的数据须要分配磁盘块。可是对于源文件尾端和新開始写位置之间的部分则不须要分配磁盘块。

Read函数

如read成功,则返回读到的字节数。

如已到达文件结尾,则返回0.

有多重情况可使实际读到的字节数少于要求读的字节数:

·读普通文件时。在读到要求字节数之前已经到达了文件尾端。比如,若在到达文件尾端之前还有30个字节,而要求读100个字节。则read返回30,下一次再调用read时,它将返回0

·  当从终端设备读时,通常一次最多读一行

·  当从网络读时。网络中的缓冲机构可能造成返回值小于所要求读的字节数。

·  当从管道或FIFO读时,如若管道包括的字节少于所须要的数量。那么read将仅仅返回实际可用的字节数

·  当从某些面向记录的设备(比如磁带)读时。一次最多返回一个记录。

·  当某一信号造成中断,而已经读了部分数据量时,读操作从文件的当前偏移量处開始,在成功返回之前,该偏移量将添加实际读到的字节数。

Write函数

对于普通文件。写操作从文件的当前偏移量处開始。假设在打开该文件时,制定了append选项,则在每次写操作之前,将文件偏移量设置在文件的当前结尾处。在一次成功写之后。该文件偏移量添加实际写的字节数

unix中文件I/O_记录锁 

文件共享

unix系统支持在不同进程间共享打开的文件。

内核使用三种数据结构表示打开的文件,它们之间的关系决定了在文件共享方面一个进程对还有一个进程可能产生的影响。

(1)每一个进程在进程表中都有一个记录项,记录项中包括有一张打开文件描写叙述符表,可将其视为一个矢量。每一个描写叙述符占用一项。与每一个文件描写叙述符相关联的是:

(a)文件描写叙述符标志

(b)指向一个文件表项的指针。

(2)内核为全部打开文件维持一张文件表。每一个文件表项包括:

           (a)文件状态标志(读,写。加入,同步和非堵塞等)。

           (b)当前文件偏移量

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

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

对于大多数文件,v节点还包括了该文件的i节点(索引节点)。这些信息是在打开文件时从磁盘上读入内存的,所以全部关于文件的信息都是高速可供使用的。比如。i节点包括了文件的全部这。文件长度。文件所在的设备。指向文件实际数据块在磁盘上所在位置的指针(linux没有使用v节点。而是使用了通用i节点结构。

尽管两种实现有所不同。但在概念上,v节点与i节点是一样的。

两者都指向文件系统特有的i节点结构)。创建v节点结构的目的是对在一个计算机系统上的多文件系统类型提供支持。

假设两个独立进程各自打开了同一个文件。我们假定第一个进程在文件描写叙述符3上打开该文件。而还有一个进程则在文件描写叙述符4上打开该文件。打开该文件的每一个进程都得到一个文件表项,但对一个给定的文件仅仅有一个v节点表项。每一个进程都有自己的文件表项的一个理由是:这样的安排使每一个进程都有它自己的对该文件的当前偏移量。

给出了这些数据结构后。如今对前面所述的操作做进一步说明

1.在完毕每一个write后。在文件表项中的当前文件偏移量即添加所写的字节数。假设这使当前文件偏移量超过了当前文件长度,则在i节点表项中的当前文件长度被设置为当前文件偏移量(也就是该文件加长了)。

2.假设已append标志打开了一个文件。则对应标志也被设置到文件表项的文件状态标志中。每次对这样的具有填写标志的文件运行写操作时,在文件表中的当前文件偏移量首先被设置为i节点表项中的文件长度。

这使得每次写的数据都加入到文件的当前尾端处。

3.若一个文件用lseek定位到文件当前的尾端。则文件表项中的当前文件偏移量被设置为i节点表项中的当前文件长度。

4.lseek函数仅仅改动文件表项中的当前文件偏移量,没有进行不论什么I/O操作

unix中文件I/O_字节数_02

可能有多个文件描写叙述符项指向同一个文件表项。我们就能看到这一点。在fork后也会发生相同的情况,次时父,子进程对于每个打开文件描写叙述符共享同一个文件表项。

注意,文件描写叙述符标志和文件状态标志在作用域方面的差别,前者仅仅用于一个进程的一个描写叙述符,而后者则适用于指向该给定文件表项的不论什么进程中的全部描写叙述符。

对于多个进程读同一个文件都能正确工作。

每一个进程都有它自己的文件表项。当中也有它自己的当前文件偏移量。可是,当多个进程同一时候写同一个文件时,则可能产生预期不到的结果。

我们会在单独的文章中,描写叙述多个个进程写的情况。