Unix文件I/O
本节描述的函数经常被称为不带缓冲的I/O.
1
#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 */);
int creat(const char *path, mode_t mode);
返回值: 若成功,返回文件描述符; 若出错,返回-1
open一些重要的oflag参数:
- O_RDONLY/O_WRONLY/O_RDWR
- O_APPEND: 追加
- O_CREAT | O_EXCL: 测试并创建文件的原子操作
- O_TRUNC: 截断
- O_DIRECTORY: 若path不是目录,则出错
- O_NONBLOCK: 若path是一个FIFO、一个块特殊文件或一个字符特殊文件,后续I/O操作设置为非阻塞方式.
- O_SYNC: 同步写(使每次write等待物理I/O操作完成)
#include <unistd.h>
int close(int fd); 返回值: 若成功,返回0; 若出错,返回-1
off_t lseek(int fd, off_t offset, int whence);
ssize_t read(int fd, void *buf, size_t nbytes); 返回值: 读到的字节数,若已到文件尾,返回0;若出错,返回-1
ssize_t write(int fd, const void *buf, size_t mbytes); 返回值: 若成功,返回已写的字节数;若出错,返回-1
一个进程终止时,内核自动关闭它所有的打开文件
lseek仅将当前的文件偏移量记录在内核中,它并不引起任何I/O操作.然后,该偏移量用于下一个读或者写操作.
2.文件共享
UNIX系统支持在不同进程间共享打开文件。
内核使用3种数据结构表示打开文件。
(1) 每个进程在进程表中都有一个记录项,记录项包含一张打开文件描述符表,每一项包含:
- 文件描述符标志
- 指向一个文件表项的指针
(2) 内核为所有打开文件维持一张文件表, 每个文件表项包含:
- 文件状态标志(读、写、添写、同步和非阻塞)
- 当前文件偏移量
- 指向该文件v节点表项的指针
(3) 每个打开文件(或设备)都有一个v节点结构。v节点包含文件类型和此文件进行各种操作函数的指针。对于大多数文件,v节点还包含了该文件的i节点。这些信息是在打开文件时从磁盘上读入内存的,所以文件的所有信息都是可以可用的,如i节点包含了文件的所有者、文件长度、指向文件实际数据块在磁盘上所在位置等。
如图3-8,若两个独立进程各自打开了同一个文件,则两个进程都获得各自的一个文件表项,但只有一个v节点表项。两个进程都获得自己的文件表项,是因为这可以使每个进程都有它自己的对该文件的当前偏移量.
3.11 原子操作
- 打开文件时设置O_APPEND
- lseek与读写的原子操作: pread, pwrite
- 测试文件是否存在和创建文件的原子操作: open函数设置O_CREAT和O_EXCL选项
原子操作: 指的是由多步组成的一个操作。如果该操作原子地执行,则要么执行完所有步骤,要么一步也不执行。
3
(1)下面两个函数都可用来复制一个现有的文件描述符
#include <unistd.h>
int dup(int fd);
int dup2(int fd, int fd2);
dup2可用于重定向。
(2) sysc,fsync和fdatasync
延迟写: 大多数磁盘I/O都通过缓冲区进行。向文件写入数据时,内核通常先将数据复制到缓冲区,然后排入队列,晚些时候再写入磁盘。
为保证磁盘实际文件系统与缓冲区中内容的一致性,Unix系统提供了sync,fsync,fdatasync.
(3)fcntl
可以改变已经打开文件的属性,包括:
- 复制一个已有的文件描述符
- 获取/设置文件描述符
- 获取/设置文件状态标志
- 获取/设置异步I/O所有权
- 获取/设置记录锁
(4) ioctl
(5) /dev/fd
/dev/fd目录下是0、1、2等文件。打开文件/dev/fd/n等效于复制描述符n(假定描述符n是打开的)
小结
因为read和write都在内核进行,所以这些函数为不带缓冲的I/O函数。