首页 > 系统 > Unix > 正文

《Unix环境高级编程》读书笔记 第3章-文件I/O

2024-06-28 13:25:00
字体:
来源:转载
供稿:网友
《Unix环境高级编程》读书笔记 第3章-文件I/O1. 引言
  • Unix系统的大多数文件I/O只需用到5个函数:open、read、write、lseek以及close
  • 本章描述的函数经常被称为不带缓冲的I/O。术语不带缓冲指的是在用户的进程中对其不会自动缓冲,每个read和write都调用内核中的一个系统调用。但是,所有磁盘I/O都要经过内核的块缓存区(也称为内核的缓冲区高速缓存)。唯一例外的是对原始磁盘设备的I/O。
2. 文件描述符
  • 对于内核而言,所有打开的文件都通过文件描述符引用。文件描述符是一个非负整数,其变化范围是0~OPEN_MAX-1
  • 惯例:幻数0、1、2分别为符号常量STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO
3. 函数open、openat:创建或打开一个文件
  1. #include <fcntl.h>
  2. int open(const char *path, int oflag, ... /* mode_t mode */ );
  3. int openat(int fd, const char *path, int oflag, ... /* mode_t mode */ );
  4. Both return: file descriptor if OK, −1 on error
  • oflag参数

    1. 以下5个常量必须指定一个且只能指定一个O_RDONLY、O_WRONLY、O_RDWR、O_EXEC(只执行打开)、O_SEARCH(只搜索打开,应用于目录,尚未支持)
    2. 下列常量则可选O_APPEND、O_CLOEXEC、O_CREAT(需要指定第3个参数mode)、O_EXCL、O_DIRECTORY、O_NOFOLLOW、O_NONBLOCK、O_SYNC、O_TRUNC
  • open和openat函数返回的文件描述符一定是最小的未用描述符数值。这一点被某些应用程序用来在标准输入、标准输出或标准错误上打开新的文件。

  • fd参数把open和openat函数区分开,共有3种可能性

    1. path参数指定的是绝对路径名,则fd参数被忽略,openat函数相当于open函数
    2. path参数指定的是相对路径名,则fd参数指出了相对路径名在文件系统中的开始地址。fd参数是通过打开相对路径名所在的目录来获取
    3. path参数指定了相对路径名,而fd参数具有特殊值AT_FDCWD,则路径名在当前工作目录中获取
  • openat函数是POSIX.1最新版本中新增的一类函数之一,希望解决两个问题:

    1. 让线程可以使用相对路径名打开目录中的文件,而不再只能打开当前工作目录
    2. 可以避免time-of-check-to-time-of-use(TOCTTOU)错误
  • TOCTTOU错误的基本思想是:

    指计算机系统的资料与权限等状态的检查与使用之间,因为特定状态在这段时间已发生改变所产生的软件漏洞

  • 文件名和路径名截断

4. 函数creat:创建一个新文件
  1. #include <fcntl.h>
  2. int creat(const char *path, mode_t mode);
  3. Returns: file descriptor opened for write-only if OK, −1 on error
  • 等效于 open( path, O_WRONLY | O_CREAT | O_TRUNC, mode );
  • creat的一个不足之处是它以只写方式打开所创建的文件
5. 函数close
  1. #include <unistd.h>
  2. int close(int fd);
  3. Returns: 0 if OK, −1 on error
  • 关闭一个文件时会释放该进程加在该文件的所有记录锁
  • 当一个进程终止时,内核自动关闭它所有的打开文件
6. 函数lseek:为一个打开文件设置偏移值
  1. #include <unistd.h>
  2. off_t lseek(int fd, off_t offset, int whence);
  3. Returns: new file offset if OK, −1 on error
  • 每个打开文件(通过open)都有一个与其关联的“当前文件偏移量”。它通常是一个非负整数(有可能为负),用于度量从文件开始处计算的字节数。
  • offse参数的解释与whence参数的值有关:SEEK_SET、SEEK_CUR、SEEK_END
  • 若lseek执行成功,返回新的文件偏移量,故可通过 lseek( fd, 0, SEEK_CUR ); 确定当前偏移量
  • 可用来确定所涉及的文件是否可以设置偏移量,管道、FIFO或网络套接字不可以,lseek返回-1,errno被设置为ESPIPE
  • 名字中的l是在引入off_t类型之前,offset参数和返回值都是long
7. 函数read
  1. #include <unistd.h>
  2. ssize_t read(int fd, void *buf, size_t nbytes);
  3. Returns: number of bytes read, 0 if end of file, −1 on error
  • 读操作从文件的当前偏移处开始,在成功返回之前,该偏移量将增加实际读到的字节数
  • 多种情况下使得读到的字节数少于要求读的字节数
    1. 读普通文件时,到达文件尾端
    2. 从终端设备读时,通常一次最多读一行
    3. 从网络读时,网络中的缓冲机制可能造成返回值小于所要求读的字节数
    4. 从管道或FIFO读时,如若管道包含的字节少于所需的数量,那么read将只返回实际可用的字节数
    5. 从某些面向记录的设备(如磁带)读时,一次最多返回一个记录
    6. 当一信号造成中断,而已经读了部分数据量时
8. 函数write
  1. #include <unistd.h>
  2. ssize_t write(int fd, const void *buf, size_t nbytes);
  3. Returns: number of bytes written if OK, −1 on error
  • 其返回值通常与参数nbytes的值相同,否则表示错误。
  • 对于普通文件,写操作从文件的当前偏移处开始。如果在打开文件时,指定了O_APPEND选项,则在每次写操作之前,将文件偏移量设置在文件的当前结尾处
9. I/O的效率
  • 大多数文件系统为改善性能都采用某种预读技术
10. 文件共享
  • 内核使用3种数据结构表示打开文件,它们之间的关系决定了在文件共享方面一个进程对另一个进程可能产生的影响

  • 左边:进程级的文件描述符表

    1. 文件描述符标志,目前只有一个FD_CLOEXEC
    2. 指向一个文件表项的指针
  • 中间:系统级的打开文件表:每次调用open打开一个文件新增一个文件表项(不同进程可打开同一个文件,导致多个文件表项)

    1. 文件状态标志(读、写、添写、同步、非阻塞等,受open时指定的oflag参数影响,也可通过fcntl函数指定FD_SETFL改变)
    2. 当前文件偏移量
    3. 指向该文件v节点表项的指针
  • 右边:文件系统级的i-node表

    1. i节点包含文件的相关信息,如文件的所有者、文件长度、指向文件实际数据块在磁盘上所在位置的指针等。
    2. linux没有v节点,而是采用了一个通用i节点。无论是v节点还是通用i节点,它们都是指向一个与文件系统相关的i节点
  • 其他

    1. 完成write之后,文件表项的当前文件偏移量增加所写入的字节数。如果这导致当前文件偏移量超出了文件长度,则将i节点表项中的当前文件长度设置为当前文件偏移量
    2. 如果通过O_APPEND标志打开一个文件,则相应标志会设置到文件表项的文件状态标志中。每次执行写操作之前,当前文件偏移量会首先被设置为i节点表项中的文件长度
    3. lseek函数只修改文件表项中的当前文件偏移量,不进行任何I/O操作。
    4. 注意:文件描述符标志只作用于一个进程的一个文件描述符,而文件状态标志则作用于指向该文件表项的任何进程中的所有描述符
11. 原子操作
  • 先lseek再write,不等价于,指定了O_APPEND的write
  • 对open函数指定O_CREAT和O_EXCL

  • 函数PRead、pwrite

  1. #include <unistd.h>
  2. ssize_t pread(int fd, void *buf, size_t nbytes, off_t offset);
  3. Returns: number of bytes read, 0 if end of file, −1 on error
  4. ssize_t pwrite(int fd, const void *buf, size_t nbytes, off_t offset);
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表