4.1 通用的 I/O 模型

本章重点:

  1. 文件描述符
  2. 通用I/O模型的系统调用

1. 文件描述符

概念:

  • 一个非负整数,指代打开的文件
  • 用以表示所有类型的已打开文件,包括管道,FIFO,socket,终端,设备和普通文件
  • 针对每个进程,文件描述符都自成一套

标准输入输出

  • shell 代表程序打开 3 个文件描述符
  • 程序继承了 shell 文件描述符的副本
文件描述符 用途 POSIX名称 stdio流
0 标准输入 STDIN_FILENO stdin
1 标准输出 STDOUT_FILENO stdout
2 标准错误 STDERR_FILENO stderr

2. 通用 I/O 模型

UNIX I/O 模型特点

  • 四个基本的系统调用可以对所有类型的文件执行 I/O 操作, 包括终端之类的设备
  • 程序需要访问文件系统或设备的专有功能时,可以使用 ioctl()
  • ioctl() 为通用 I/O 模型之外的专有特性提供了访问接口

3. I/O 操作的系统调用

3.1 open

fd = open(pathname, flags, mode):

  • 作用:打开一个已存在文件,或创建并打开一个新文件
  • 返回:
    • 成功打开文件,返回文件描述符(未用文件描述符中数值最小者)
    • 发生错误返回 -1,并将 error 设置为相应的错误标识
  • 参数:
    • pathname:文件路径,如果 pathname 是符号连接,将对其解引用
    • flags: 指定文件的打开方式
    • mode: 指定 open() 调用创建文件的访问权限,如果未创建文件,可以忽略 mode 参数

mode

附注:

  • open() 未指定 O_CREATE 标识,可以省略 mode 参数
  • 新建文件的权限,不仅仅依赖于参数 mode,而且受到进程的 umask 值和父目录的默认访问权限控制列表的影响

flags

分类:

  1. 文件访问模式标识:
    • 包括:O_RDONLY, O_WRONLY, O_RDWR
    • 特性:不能同时使用,只能指定其一
    • 检索:调用 fcntl() 的 F_GETFL 操作可以检索文件的访问模式
  2. 文件创建标识:
    • 特性:这些标识不能检索,也无法修改
  3. 已打开文件的状态标识
    • 特性:调用 fcntl() 的 F_GETFL 和 F_SETFL 可以检索和修改此类标识
标识 用途
O_RDONLY 以只读方式打开文件
O_WRONLY 以只写方式打开文件
O_RDWR 以读写方式打开文件
O_ASYNC 当I/O操作可行时,产生信号通知进程
O_NONBLOCK 以非阻塞方式打开
O_SYNC 以同步方式写入文件
O_DSYNC 提供同步的I/O数据完整性
O_DIRECT 无缓冲的输入/输出
O_APPEND 总在文件尾部追加数据
O_TRUNC 截断已有文件,使其长度为 0
O_CREAT 若文件不存在则创建
O_LARGEFILE 在 32 位系统中,使用此标志打开大文件
O_EXCL 结合 O_CREAT,专门用于创建文件
O_CLOEXEC 设置 close-no-exec 标识
O_NOATIME 调用 read()时,不修改文件最近访问时间
O_NOCTTY 不要让 pathname 成为控制终端
O_NOFOLLOW 不对符号链接解引用
O_DIRECTORY 如果 pathname 不是目录,则失败

同步异步,阻塞非阻塞

O_ASYNC:

  • 章节:63.3
  • 作用:信号驱动 I/O
  • 特性:仅对特定类型的文件有效,包括 终端,FIFOS,socket
  • 附注:Linux 中指定 O_ASYNC 没有任何效果,要启动信号驱动 I/O 特性,必须调用 fcntl() 的 F_SETFL 操作来设置 O_ASYNC

O_NONBLOCK:

  • 章节:5.9
  • 作用:以非阻塞方式打开文件

O_SYNC:

  • 章节:13.3
  • 作用:以同步 I/O 方式打开文件

O_DSYNC:

  • 章节:13.3
  • 作用:根据同步 I/O 数据完整性的完成要求来执行文件写操作

O_DIRECT:

  • 章节:13.6
  • 作用:无系统缓冲的文件 I/O 操作
  • 特性:为使此标志的常量定义在 中有效,必需定义 _GNU_SOURCE 功能测试宏

文件创建

O_APPEND

  • 章节:5.1

O_TRUNC:

  • 作用:如果文件已经存在且为普通文件,那么将清空文件内容,将其长度置为 0
  • 特性:
    • Linux 下使用此标志,无论以读写方式打开文件,都可清空文件内容 (两种情况下,都必须拥有对文件的写权限)
    • SUSv3 O_RDONLY 与 O_TRUNC 标志的组合未作规定,但多数其他 UNIX 实现与 Linux 的相同

O_CREAT

  • 作用:如果文件不存在,将创建新的空文件
  • 特性:即使文件以只读方式打开,此标志依然有效
  • 附注:open 调用还需要提供 mode 参数,否则会将新文件的权限设置为栈中的某个随机值

O_LAGEFILE:

  • 章节:5.10
  • 作用:32 位操作系统中使用此标志,支持大文件操作
  • 特性:此标志在 64 位 Linux 中是无效的

原子操作

O_CLOEXEC:

  • 章节:27.4
  • 作用:
    • 为新创建的文件描述符启用 close-on-flag 标志(FD_CLOEXEC)
    • 免去程序执行 fcntl() 的 F_GETFL 和 F_SETFL 操作来设置 close-on-exec 的额外工作
    • 在多线程中,上述两个操作可能导致竞争状态,O_CLOEXEC 标识能避免这一点

O_EXCL:

  • 作用:
    • 与 O_CREAT 结合使用,表明如果文件已经存在,不会打开文件
    • open() 调用失败,并返回错误,error 为 EEXIST
    • 即此标志确保了调用者 (open() 的调用进程)就是创建文件的进程
    • 检查文件存在与否和创建文件这两步属于同一原子操作
  • 特性:
    • 同时指定 O_EXCL,O_CREAT,且 pathname 是符号链接,open 调用失败(error 为 EEXIST)
    • 之所以这样规定,是要求有特权的应用程序在已知目录下创建文件,消除安全隐患, 使用符号链接打开文件会导致另一位置创建文件

文件打开限制

O_DIRECTORY:

  • 章节:18.8
  • 作用:
    • 如果 pathname 非目录,将返回错误(error 为 ENOTDIR)
    • 是转为 实现 opendir() 函数而设计的扩展标识(18.8)
  • 特性:为使此标志的常量定义在 中有效,必需定义 _GNU_SOURCE 功能测试宏

O_NOFOLLOW:

  • 作用:
    • 通常 如果 pathname 是符号链接,open 会对其解引用
    • 指定此标志,且 pathname 是符号链接时,open 函数将返回失败,error 为 ELOOP
  • 特性:
    • 此标志在特权程序中极其有用,确保 open 不对符号链接解引用
    • 为使此标志的常量定义在 中有效,必需定义 _GNU_SOURCE 功能测试宏

O_NOATIME:

  • 作用:
    • 读文件时,不更新文件的最近访问时间(15.1 节描述的 st_atime 属性)
    • 不满足下列要求,open 调用失败,并返回错误,error 为 EPERM
  • 要求:
    • 要么调用进程的有效用户 ID 必需与文件的拥有者相匹配
    • 要么进程需要拥有特权(CAP_FOWNER)
    • 9.5 节所述,对于非特权程序,与文件用户 ID 必需匹配的是进程的文件系统用户 ID, 而非进程的有效用户 ID
  • 特性:
    • 比标志是 Linux 特有的非标准扩展
    • 为使此标志的常量定义在 中有效,必需定义 _GNU_SOURCE 功能测试宏
  • 原理:
    • O_NOATIME 旨在为索引和备份程序服务
    • 该标志的使用能够显著减少磁盘的活动量,省却了既要读取文件内容,又要更新文件 i-node 结构中最近访问时间的繁琐,进而省却了磁头在磁盘上的反复寻道时间(14.4节)
    • mount() 函数中 MS_NOATIME标志(14.8.1) 和 FS_NOATIME_FL标志(15.5) 与 O_NOATIME 功能相似

O_NOCTTY:

  • 作用:
    • 如果正在打开的文件属于终端设备,此标志防止其成为控制终端
    • 如果正在打开的文件不是终端设备,此标志无效

3.2 read

numread = read(fd, buffer, count)

  • 作用:调用从 fd 所指代的打开文件中读取至多 count 字节的数据,存储到 buffer 中
  • 返回:实际读取到的字节数,如果无字节可读,则返回 0

3.3 write

numwritten = write(fd, buffer, count)

  • 作用:从buffer 中读取多达 count 字节的数据写入由 fd 所指代的已打开文件
  • 返回:实际写入文件中的字节数,有可能小于 count

3.4 close

status = close(fd):

  • 作用:释放文件描述符,以及与之相关的资源

3.5 lseek

3.6 ioctl

results matching ""

    No results matching ""