概述

  • 所有执行I/O操作的系统调用都以文件描述符,一个非负整数的,来指代打开的文件。
  • 文件描述符用以表示所有类型的已打开文件,包括管道、FIFO、socket、终端、设备和普通文件。
  • 针对每个进程,文件描述符都自成一套。
  • 文件描述符012表示标准输入标准输出标准错误,这三个文件描述符在进程中始终是打开的
  • 下面介绍执行文件I/O操作的4个主要系统调用:

    • fd = open(pathname, flags, mode)函数打开pathname所标识的文件,并返回文件描述符,用以在后续函数调用中指代打开的文件
    • numread = read(fd, buffer, count)调用从fd所指代的打开文件中读取至多count字节的数据,并存储到buffer
    • numwritten = write(fd, buffer, count)调用从buffer中读取多达count字节的数据写入由fd所指代的已打开文件中
    • status = close(fd)在所有输入/输出操作完成后,调用close(),释放文件描述符fd以及与之相关的内核资源

通用I/O

UNIX I/O模型的显著特点之一是其输入/输出的通用性概念。这意味着使用上述4个同样的系统调用可以对所有类型的文件执行I/O操作。
一旦应用程序需要访问文件系统或设备的专有功能时,可以选择瑞士军刀般的ioctl()系统调用。

打开一个文件:open()

open()调用既能打开一个已存在的文件,也能创建并打开一个新文件。

#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags, .../* mode_t mode */);
                       // Returns file descriptor on success, or -1 on error
参数
  • pathname:所要打开的文件路径,如果是符号链接会对其进行解引用
  • flags:位掩码,用于指定文件的访问模式,可能的值如下:
访问模式描述
O_RDONLY以只读方式打开文件
O_WRONLY以只写方式打开文件
O_RDWR以读写方式打开文件
O_CLOEXEC设置close-on-exec标志
O_CREAT若文件不存在则创建之
O_DIRECT无缓冲的输入/输出
O_DIRECTORYpathname不是目录,则失败
O_EXCL结合O_CREAT参数使用,专门用于创建文件
O_LARGEFILE在32位系统中使用此标志打开大文件
O_NOATIME调用read()时不更新文件最近访问时间
O_NOCTTY不要让pathname(所指向的是终端设备)成为控制终端
O_NOFOLLOW对符号链接不予解引用
O_TRUNC截断已有文件,使其长度为0
O_APPEND总在文件尾部追加数据
O_ASYNC当I/O操作可行时,产生信号通知进程
O_DSYNC提供同步的I/O数据完整性
O_NONBLOCK以非阻塞方式打开
O_SYNC以同步方式打开

使用的时候多个值作|(按位或)运算即可

  • mode:文件权限,只有当flags指定为O_CREAT(创建)访问模式时,才有必要设置这个参数,换句话说,这个参数是为了新建文件时指定其权限用的,其可能的值如下:
权限标识描述
S_IRUSR所属主可读
S_IWUSR所属主可写
S_IXUSR所属主可执行
S_IRGRP所属组可读
S_IWGRP所属组可写
S_IXGRP所属组可执行
S_IROTH其他人可读
S_IWOTH其他人可写
S_IXOTH其他人可执行

使用的时候多个值作|(按位或)运算即可

返回值
  • 成功:返回文件描述符。SUSv3规定,必须保证其返回值为进程未用文件描述符中数值最小者
  • 失败:返回-1,并将errno置为相应的错误标志

读取文件内容:read()

read()系统调用从文件描述符fd所指代的打开文件中读取数据。

#include <unistd.h>
ssize_t read(int fd, void *buffer, size_t count);
                       // Returns number of bytes read, 0 on EOF, or -1 on error
参数
  • fd:文件描述符
  • buffer:提供用来存放输入数据的内存缓冲区地址,缓冲区至少应有count个字节
  • count:指定最多能读取的字节数
返回值
  • 成功:返回实际读取的字节数;该返回值可能小于count参数值,如遇到文件结束(EOF
  • 失败:返回-1并将errno置为相应的错误标志;如遇到文件结束(EOF)则返回0

数据写入文件:write()

write()系统调用从文件描述符fd所指代的打开文件中读取数据。

#include <unistd.h>
ssize_t write(int fd, void *buffer, size_t count);
                       // Returns number of bytes written, or -1 on error
参数
  • fd:文件描述符
  • buffer:提供要写入文件中数据的内存地址
  • count:指定最多能写入的字节数
返回值
  • 成功:返回实际写入的字节数;该返回值可能小于count参数值,这被称为“部分写”
  • 失败:返回-1并将errno置为相应的错误标志

关闭文件:close()

close()系统调用关闭一个打开的文件描述符,并将其释放回调用进程,供该进程继续使用。

#include <unistd.h>
int close(int fd);
                       // Returns 0 on success, or -1 on error

显式关闭文件往往是良好的编程习惯

参数
  • fd:文件描述符
返回值
  • 成功:返回0
  • 失败:返回-1并将errno置为相应的错误标志

改变文件偏移量:lseek()

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

lseek()系统调用依照offsetwhence参数值调整该文件的偏移量

#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
                       // Returns new file offset if successful, or -1 on error
参数
  • fd:文件描述符
  • offset:指定了一个以字节为单位的数值
  • whence:表明应参照哪个基点来解释offset参数,应为下列其中之一:

    • SEEK_SET:将文件偏移量设置为从文件头部起始点开始的offset个字节
    • SEEK_CUR:相对于当前文件偏移量,将文件偏移量调整offset个字节
    • SEEK_END:将文件偏移量设置为起始于文件尾部的offset个字节
返回值
  • 成功:返回实际写入的字节数;该返回值可能小于count参数值,这被称为“部分写”
  • 失败:返回-1并将errno置为相应的错误标志

文件空洞

如果程序的文件偏移量跨越了文件结尾,然后执行I/O操作,read()调用将返回0,表示文件结尾;但write()调用可以在文件结尾后任意位置写入数据。

从文件结尾后到新写入数据间的这段空间被称为文件空洞。文件空洞存在字节(空字节,读取将返回0)但不占用任何磁盘空间。

通用IO模型以外的操作:ioctl()

ioctl()系统调用为执行文件和设备操作提供了一种多用途机制。

#include <sys/ioctl.h>
int ioctl(int fd, int request, ... /* argp */);
                       // Value returned on success depends on request, or -1 on error
参数
  • fd:文件描述符
  • request:指定了将在fd上执行的控制操作。具体设备的头文件定义了可传递给request参数的常量
  • ...:可以是任意数据类型,根据request的参数值来确定 argp 所期望的类型。通常情况下是指向整数或结构的指针
返回值
  • 成功:返回0
  • 失败:返回-1,并将errno置为相应的错误标志

总结

本章介绍了通用I/O模型,这是UNIX环境下非常重要非常基础的一个概念

标签: Linux, C/C++, TLPI

添加新评论