UNIX环境高级编程 - 文件I/O - 打开/创建/关闭文件

函数open和openat

#include <fcntl.h>

int open(const char* path,int oflag,.../*mode_t mode*/);

int openat(int fd,const char*path,int oflag,.../*mode_t mode */);

可以使用如下命令查看参数:

man 2 open
  • 参数:

    • path:要打开或者创建文件的名字

    • oflag:用于指定函数的操作行为:

      必选的标志:(标志/常量定义于 fcntl.h 中)

      • O_RDONLY常量:文件只读打开
      • O_WRONLY常量:文件只写打开
      • O_RDWR常量:文件读、写打开
      • O_EXEC常量:只执行打开
      • O_SEARCH常量:只搜索打开(应用于目录)。本文涉及的操作系统都没有支持该常

      下面的常量是可选的(进行或运行)(不一定所有的操作系统支持,不同的系统得看实际情况):

      • O_APPEND:每次写时都追加到文件的尾端
      • O_CLOEXEC:将FD_CLOEXEC常量设置为文件描述符标志
      • O_CREAT:若此文件不存在则创建它。在使用此选项时,需要同时说明参数mode(指定该文件的访问权限)
      • O_DIRECTORY:若path引用的不是目录,则出错
      • O_EXCL:若同时指定了O_CREAT时,且文件已存在则出错。根据此可以测试一个文件是否存在。若不存在则创建此文件。这使得测试和创建两者成为一个原子操作
      • O_NOCTTY:若path引用的是终端设备,则不将该设备分配作为此进程的控制终端
      • O_NOFOLLOW:若path引用的是一个符号链接,则出错
      • O_NONBLOCK:如果path引用的是一个FIFO、一个块特殊文件或者一个字符特殊文件,则文件本次打开操作和后续的 I/O 操作设为非阻塞模式。
      • O_SYNC:每次 write 等待物理 I/O 完成,包括由 write 操作引起的文件属性更新所需的 I/O
      • O_TRUNC: 如果此文件存在,且为O_WRONLY或者O_RDWR成功打开,则将其长度截断为0
      • O_RSYNC:使每一个read操作等待,直到所有对文件同一部分挂起的写操作都完成。
      • O_DSYNC:每次 write 等待物理 I/O 完成,但不包括由 write 操作引起的文件属性更新所需的 I/O
    • mode:文件访问权限。文件访问权限常量在 <sys/stat.h> 中定义,有以下九个:

      • S_IRUSR:用户读
      • S_IWUSR:用户写
      • S_IXUSR:用户执行
      • S_IRGRP:组读
      • S_IWGRP:组写
      • S_IXGRP:组执行
      • S_IROTH:其他读
      • S_IWOTH:其他写
      • S_IXOTH:其他执行
  • 返回值:

    • 成功:返回文件描述符。
    • 失败:返回 -1

open/openat 返回的文件描述符一定是最小的未使用的描述符数字。

对于openat函数,被打开的文件名由fdpath共同决定:

  • 如果path指定的是绝对路径,此时fd被忽略。openat等价于open

  • 如果path指定的是相对路径名,则fd是一个打开目录的文件描述符。被打开的文件的绝对路径由该fd描述符对应的目录加上path组合而成

  • 如果path是一个相对路径名,而fd是特殊值AT_FDCWD,则path相对于当前工作目录。被打开文件在当前工作目录中查找。

    • fdAT_FDCWD 说明是当前目录

    image-20211104234109277

openat 函数是 POSIX.1中新增的函数,希望解決两个问题。

  1. 让线程可以使用相对路径名打开目录中的文件,而不再只能打开当前工作目录。

    • 同一进程中的所有线程共享相同的当前工作目录,因此很难让同一进程的多个不同线程在同时间工作在不同的目录中。
  2. 可以避免 time-of-check-to-time-of-use (TOCTTOU)错误。

    • TOCTTOU 错误的基本思想是:如果有两个基于文件的函数调用,其中第二个调用依赖于第一个调用的结果,那么程序是脆弱的。
    • 因为两个调用并不是原子操作,在两个函数调用之间文件可能改变了,这样也就造成了第一个调用的结果就不再有效,使得程序最终的结果是错误的。

函数creat

也可以使用creat 创建一个新文件。

#include <fcntl.h>

int creat(const char*path,mode_t mode);

此函数等效于:

open(path, O_WRONLY | O_CREAT | O_TRUNC, mode)

  • 参数:
    • path:要创建文件的名字
    • mode:指定该文件的访问权限文件访问权限常量在 <sys/stat.h> 中定义,有下列九个:
      • S_IRUSR:用户读
      • S_IWUSR:用户写
      • S_IXUSR:用户执行
      • S_IRGRP:组读
      • S_IWGRP:组写
      • S_IXGRP:组执行
      • S_IROTH:其他读
      • S_IWOTH:其他写
      • S_IXOTH:其他执行
  • 返回值:
    • 成功: 返回O_WRONLY打开的文件描述符
    • 失败: 返回 -1

creat的不足:

  • 它以只写方式打开。若要先写再读,则需调用creatclose,然后再调用open进行读取。
  • 若文件已存在则将文件截断为0。

现在可以使用新的open:

open(path, O_RDWR | O_CREAT | O_TRUNC, mode)

为什么会有open了还需要creat?

历史原因,老版本的open没有O_CREAT标志。

函数close

可以使用close 关闭一个打开文件。

#include <unistd.h>

int close(int fd);
  • 参数:
    • fd:待关闭文件的文件描述符
  • 返回值:
    • 成功:返回 0
    • 失败:返回 -1

注意:

  • 进程关闭一个文件会释放它加在该文件上的所有记录锁。
  • 当一个进程终止时,内核会自动关闭它所有的打开的文件
posted @ 2021-11-05 00:11  zju_cxl  阅读(156)  评论(0编辑  收藏  举报