整理自《Linux-UNIX 系统编程手册》

  • 所有执行 I/O 操作的系统调用都以文件描述符,一个非负整数(通常是小整数),来指代打开的文件(包括管道(pipe)、FIFO、socket、终端、 设备和普通文件)。每个进程使用一套相互隔离的文件描述符。
  • fd = open(pathname, flags, mode)
    numread = read(fd, buffer, count)
    numwritten = write(fd, buffer, count)
    status = close(fd)
open
int open(const char pathname, int flags, .../* mode */);
Returns file descriptor on success, or -1 on error.
  • 如果 pathname 是一符号链接,会对其进行解引用。
  • flags 参数列表

这些参数分为 3 组:

  1. 文件访问模式标志:只能指定其中一种。调用 fcntl() 的 F_GETFL 操作能够检索文件的访问模式。
  2. 文件创建标志:这些标志不能检索,也无法修改。
  3. 已打开文件的状态标志:使用 fcntl() 的 F_GETFL 和 F_SETFL 操作可以分别检索和修改此类标志。有时将其称之为文件状态标志。
open 函数错误
  • EACCES:error access 缩写,通常是权限错误
  • EISDIR:error is directory 缩写,通常是试图写入一个目录
  • EMFILE:error max? file 缩写,进程被允许打开的文件描述符达到上限(不太确定,欢迎评论)
  • ENFILE:error n? file 缩写,文件描述符数量达到系统上限
  • ENOENT:error not exsit? 缩写,文件不存在,且不是创建模式
  • EROFS:error read only file system 试图写入只读文件系统
  • ETXTBSY:正在运行的可执行文件不允许修改
creat
int creat(const char *pathname, mode_t mode)
Returns file descriptor, or -1 on error

creat 可被 open 替换

read
ssize_t read(int fd, void *buffer, size_t count)
Returns number of bytes read, 0 on EOF, or -1 on error
  • 系统调用不会分配内存缓冲区用以返回信息给调用者。所以,必须预先分配大小合适 的缓冲区并将缓冲区指针传递给系统调用。与此相反,有些库函数却会分配内存缓冲区用 以返回信息给调用者。
  • 由于表示字符串终止的空字符需要一个字节的内存,所以缓冲区的大小至少要比预计读 取的最大字符串长度多出 1 个字节。
write
ssize_t write(int fd, void *buffer, site_t count)
Returns number of bytes written, or -1 on error

write() 调用成功并不能保证数据已经写入磁盘(首先写入到内核缓冲区)。

close
int close(int fd)
Returns 0 on success, or -1 on error

企图关闭一个未打开的文件描述符,或者两次关闭同一文 件描述符,也能捕获特定文件系统在关闭操作中诊断出的错误条件。

lseek()
off_t lseek(int fd, off_t offset, int whence)
Returns new file offset if successful, or -1 on error

对于每个打开的文件,系统内核会记录其文件偏移量,有时也将文件偏移量称为读写偏 移量或指针。文件偏移量是指执行下一个 read() 或 write() 操作的文件起始位置,会以相对于文 件头部起始点的文件当前位置来表示。文件第一个字节的偏移量为 0。

whence 参数则表明应参照哪个基点来解释 offset 参数,应为下列其中之一:

  • SEEK_SET:将文件偏移量设置为从文件头部起始点开始的 offset 个字节。
  • SEEK_CUR:相对于当前文件偏移量,将文件偏移量调整 offset 个字节 1。
  • SEEK_END:将文件偏移量设置为起始于文件尾部的 offset 个字节。也就是说,offset 参数应该从文件最后一个字节之后的下一个字节算起。

lseek() 调用只是调整内核中与文件描述符相关的文件偏移量记录,并没有引起对任何物理 设备的访问。

不允许将 lseek() 应用于管道、FIFO、socket 或者终端。 一旦如此,调用将会失败,并将 errno 置为 ESPIPE。另一方面,只要合情合理,也可以将 lseek() 应用于设备。例如,在磁盘或者磁带上查找一处具体位置。

文件空洞

write() 函数可以在文件结尾后的任意位置写入数据。从文件结尾后到新写入数据间的这段空间被称为文件空洞。

从编程角度看,文件空洞中是存在字节的,读取空洞将返回以 0(空字节)填充的缓冲区。

然而,文件空洞不占用任何磁盘空间。直到后续某个时点,在文件空洞中写入了数据,文件系统才会为之分配磁盘块。文件空洞的主要优势在于,与为实际需要的空字节分配磁盘块相比,稀疏填充的文件会占用较少的磁盘空间。核心转储文件是包含空洞文件的常见例子。

ioctl
int ioctl(int fd, int request, .../* argp */
Value returned on success depends on request, or -1 on error

fd 参数为某个设备或文件已打开的文件描述符,request 参数指定了将在 fd 上执行的控制操作。具体设备的头文件定义了可传递给 request 参数的常量。

ioctl() 调用的第三个参数采用了标准 C 语言的省略符号(...)来表示(称之为 argp),可以是任意数据类型。ioctl() 根据 request 的参数值来确定 argp 所期望的类型。通常情况下,argp 是指向整数或结构的指针,有些情况下,不需要使用 argp。