UNIX环境高级编程-第三章文件IO

本章讨论的是不带缓存的IO。

文件描述符

对于内核,打开的文件都由文件描述符引用。

按照惯例,文件描述符0对应标准输入,文件描述符1对应标准输出,文件描述符2对应标准错误。这是UNIX shell 和很对应用程序的惯例。

文件描述符数量有上限范围,在其UNIX上限是19,能打开20个,现在很多系统增加到了63,有的没限制。

open函数

O_APPEND选项,文件表中的偏移量被设置成索引节点表中的文件长度。

open返回的文件描述符一定是最小的未使用的数字。可以用这个特性在标准输入、标准输出或者标准错误上打开一个新的文件。如先关闭标准输出,然后打开一个文件,这个文件一定会在描述符1上打开。

dup2函数可以正好的保证在给定的描述符上打开一个文件。

creat函数

创建新文件

close函数

关闭一个打开文件

关闭一个文件也释放该进程加在这个文件上的所有记录锁。

进程终止时,内核会自动关闭他打开的文件。

lseek函数

设置文件偏移量。只修改文件表项中的文件偏移量,没有进行任何I/O操作。

读写操作都是从文件偏移量开始的,打开一个文件,除非指定O_APPEND,否则偏移量都被设置为0。

1
2
3
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence) ;

对于offset相对哪里偏移,通过whence设置,whence可以设置为SEEK_SET,SEEK_CUR,SEEK_END,分别对应文件开始处,当前位置,文件结尾。

返回值可以用来判断文件是否可以设置偏移量,如果文件描述符引用的是以管道或FIFO,lseek返回-1,errno设置为EPIPE。

文件偏移量可以大于当前文件长度,形成空洞。

read函数

1
2
3
#include <unistd.h>
ssize_t read(int fd, void *buffer, size_tn bytes);
//返回:读到的字节数,若已到文件尾为0,若出错为- 1

实际读到的字节数少于要求的字节数:

  • 到达了文件尾还没读够
  • 从终端设备读,一次之只能读一行
  • 从网络读,网络缓冲机构原因
  • 读面向记录的设备,如磁带,一次返回一个记录

write函数

1
2
3
#include <unistd.h>
ssize_t write(int fd, const void *buff, size_t nbytes) ;
//返回:若成功为已写的字节数,若出错为- 1

写出错常见原因:磁盘满,超过了一个给定进程的文件长度限制。

I/O效率

文件共享

UNIX支持不同的进程共享打开的文件。

image-20210121161901854

每个进程都有自己文件表项:使得每个进程对自己的文件有一个偏移量。

多个进程读同一个文件都能正确的工作,因为有自己的文件表项,其中有自己的文件偏移量,但是多个进程在写一个文件时,就可能发生问题,所以下面介绍原子操作。

原子操作

下面是要实现原子操作的两个例子。

写数据到文件尾部

在早期没有O_APPEND的时候,假设两个进程AB都对同一个文件的尾部进行写入,先调用lseek到文件尾部,然后write,这时候第二个写操作可能会覆盖第一个写操作,从而出现问题。

这种逻辑的问题在于先定位,后写使用了两个分开的函数调用,解决办法是使这两个操作对于其他进程而言成为一个原子操作。也就有了O_APPEND,内核每次对这种文件写之前,都将进程的对该文件的偏移量设置到文件的尾端。

创建文件

考虑open函数的O_CREAT和O_EXCL选项。

O_EXCL选项配合O_CREAT选项使用,若文件存在,则出错。若文件不存在,则创建文件成为一个原子操作。

情形是要打开一个文件,若不存在则创建,如果打开和创建之间,另一个进程创建了文件,而且写入了数据,则本进程再创建,刚写上的数据就会被擦除。因此要合并在一个原子操作。

dup和dup2函数

复制以一个现存的文件描述符。

1
2
3
4
#include <unistd.h>
int dup(int fd) ;//返回的是可用文件描述符的最小值
int dup2(int fd, int fd2) ;//通过fd2指定新的描述符的值。
//两函数的返回:若成功为新的文件描述符,若出错为- 1

dup2中,如果fd2已经打开,则先关闭。fd可以和fd2相等。

返回的新文件描述符与参数fd共享同一个文件表项(进程表—文件表—索引节点表),fd存在进程表项中,指向的是同一个文件表项,所以共享一个文件的状态标志和偏移量。

fcntl函数

改变已经打开文件的性质。

1
2
3
4
5
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, int arg) ;
返回:若成功则依赖于c m d(见下),若出错为- 1

通过cmd设置五种功能:

  • 复制一个现存的描述符(cmd=F_DUPFD)。新的文件描述符取可用的大于等于第三个参数的最小值
  • 获得/设置文件描述符标记(cmd = F_GETFD或F_SETFD)。
  • 获得/设置文件状态标志(cmd = F_GETFL或F_SETFL)。
  • 获得/设置异步I / O有权(cmd = F_GETOWN或F_SETOWN)。
  • 获得/设置记录锁(cmd = F_GETLK , F_SETLK或F_SETLKW)

oictl函数

用于终端IO、磁带IO、套接字IO

/dev/fd

目录里是名为0、1、2等的文件,打开文件/dev/fd/n等效于复制描述符n。

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×