unix环境高级编程-文件IO

文件IO:

大多数文件IO只需要用到5个函数:open,read,write,lseek和close

这里描述的函数经常被称为不带缓冲的IO(unbuffered IO),不带缓冲的意思是每个read和write都调用内核中的一个系统调用,这些不带缓冲的IO函数不是ISO C的组成部分,但是却是POSIX的组成。

只要涉及在多个进程之间共享资源,那么原子操作的概念就变得非常重要。

文件描述符:

对于内核而言,所有打开的文件都通过文件描述符引用,文件描述符是一个非负整数,当打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。当读写一个文件时,用open或creat返回的文件描述符标识该文件,将其作为参数传递给read或write。

一般而言,Unix将文件描述符0余进程的标准输入相关联,文件描述符1与标准输出关联,文件描述符2与标准错误相关联。这三个描述符通常替换成符号常量STDIN_FILENO、STDOUT_FILENO和STDERR_FILENO以提高可读性。这些常量在头文件unistd.h定义。

文件描述符的变化范围是0-OPEN_MAX-1,也即是每个进程最多允许打开文件数-1。


open函数:调用该函数可以创建或打开一个文件

#include

int open(const char *pathname, int oflag, ......);

pathname 是要打开或者创建文件的名字,oflag可用来说明函数的多个选项,用下列一个或多个常量进行或运算构成oflag参数,具体oflag参数有如下:

O_RDONLY   只读 0

O_WRONLY  只写 1

O_RDWR       读写 2

O_EXEC         只执行

O_SEAECH  只搜索(应用于目录)

以上5个参数必须选择一个

下列常量是可选择的:

unix环境高级编程-文件IO_第1张图片

由open返回的文件描述符一定是最小的未用描述符数值,可以被某些应用程序用来标准输入、输出上打开新的文件


creat函数:可调用creat函数创建一个新文件:

#include

int creat(const char *pathname , mode_t mode);    返回值:如果成功返回只写打开的文件描述符,若出错则返回-1

此函数等效于:open(pathname, O_WRONLY | O_CREAT | O_TRUNC, mode);

creat函数一个不足之处是它以只写方式打开所创建的文件,如果要创建一个文件,必须先写,然后又读, 也就是先调用creat,close,然后调用open。现在可以用以下方式:

open(pathname, O_RDWR | O_CREAT | O_TRUNC , MODE);


close 函数:关闭一个打开文件

当一个进程终止时,内核自动关闭打开的所有文件,很多程序都利用这一功能而不显示的调用close关闭打开文件。

lseek函数:为一个打开的函数设置偏移量

每个打开文件都有一个与其想关联的当前文件偏移量,用于度量从文件开始处计算的字节数,通常读写操作都是从当前文件偏移量开始。按照系统默认的情况,当打开一个文件时,除非指定了O_APPEND选项,否则偏移量设置为0。

调用lseek显性为一个打开文件设置偏移量


参数offset和参数whence有关:

若whence是seek_SET,则将该文件的偏移量设置为文件开始处offset字节。

若whence是seek_CUR,则将该文件的偏移量设置为当前值+offset字节,offset可正可负。

若whence是seek_END,则将该文件的偏移量设置为文件长度加offset字节,offset可正可负。


文件偏移量是可以大于文件的当前长度,这种情况下,对该文件的下一次写将加长该文件,并在文件中构成一个空洞,位于文件中单没有写过的字节都被读为0。

文件的空洞并不要求在磁盘上占有存储区。


read函数:从打开文件中读数据



write函数:向打开文件写数据


文件共享:不同进程间共享打开文件

下图显示了一个进程对应三张表之间的关系,该进程有两个不同的打开文件:一个文件从标准输入打开(文件描述符0),另一个从标准输出打开(文件描述符1)

unix环境高级编程-文件IO_第2张图片

如果两个独立进程各自打开同一个文件,则有图中所示关系

unix环境高级编程-文件IO_第3张图片

对于多个进程读取同一文件都能正确工作,每个进程有它自己的文件表项,其中也有它自己的当前文件偏移量,但是多个进程写同一文件时,可能产生预想不到的结果。


原子操作:

考虑一个进程,它要将数据追加到一个文件尾端,程序是这样写的


对单个进程而言是可以运行的,但是对于多个进程,同时使用这种方法,会产生问题。

比如两个进程AB对同一文件进行写操作,每个进程都打开该文件,每个进程有自己的文件表项,但是共享一个v节点表项,当两个进程写操作时,会导致信息不一致而出错。

问题出于逻辑操作:先定位到文件尾端,然后写,它使用了两个分开的函数调用,解决方法是使者两个操作对于其他进程而言成为一个原子操作,任何要求多于一个函数调用的操作都不是原子操作,因为在两个函数调用之间,内核有可能临时挂起进程。

Unix系统为这样的操作提供了一种原子操作的办法,在打开文件时设置O_APPEND标志,这样使得内核在每次写操作之前,都将进程的当前偏移量设置到该文件的尾端处。

比如函数pread和pwrite。

一般而言,原子操作指的是由多步组成的一个操作,如果该操作原子执行,则要么执行完所有操作,要么一步也不执行,不可能只执行所有步骤的一个子集。



转载于:https://www.cnblogs.com/sichenzhao/p/9320372.html

你可能感兴趣的:(unix环境高级编程-文件IO)