APUE学习笔记——文件IO
文件描述符
所有打开的文件都通过文件描述符(一个非负整数)引用。
维基百科上对于文件描述符的解释是这样的:
文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统。
当读、写一个文件时,使用open或creat返回的文件描述符标识该文件,将其作为参数传给read或write。
在POSIX.1的应用程序中,文件描述符0与进程的标准输入关联(STDIN_FILENO)、文件描述符1与进程的标准输出关联(STDOUT_FILENO)、文件描述符2与进程的标准错误关联(STDERR_FILENO),且已经确定化。
文件描述符的变化范围是0~OPEN_MAX-1。
函数open和openat
1 |
|
| 打开方式 | 描述 |
|---|---|
O_RDONLY |
只读打开. |
O_WRONLY |
只写打开. |
O_RDWR |
读、写打开. |
O_EXEC |
只执行打开. |
O_SEARCH |
只搜索打开 (应用于目录). |
O_APPEND |
每次写时都追加到文件的尾端. |
O_CLOEXEC |
设置FD_CLOEXEC文件描述符. |
O_CREAT |
若此文件不存在则创建它. |
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 |
如果此文件存在,而且为只写或读-写成功打开,则将其长度截断为0. |
O_TTY_INIT |
如果打开一个还未打开的终端设备,设置非标准termios参数值,使其符合Single UNIX Specification. |
O_DSYNC |
使每次write要等待物理I/O完成,但是如果该写操作并不影响读取刚写入的数据,则不需等待文件属性被更新. |
O_RSYNC |
使每一个一文件描述符作为参数进行的read操作等待,直至所有对文件同一部份挂起的写操作都完成. |
fd参数吧open和openat函数区分开,共有3种可能性。
path参数指定的是绝对路径名,在这种情况下,fd参数被忽略,openat函数就相当于open函数。path参数指定的是相对路径名,fd参数指出了相对路径名在文件系统中的开始地址。fd参数是通过打开相对路径名所在的目录来获取。path参数指定了相对路径名,fd参数具有特殊值AT_FDCWD。在这种情况下,路径名在当前工作目录中获取,openat函数在操作上与open函数类似。
函数creat
调用函数creat创建一个新的文件
1 |
|
等效于:
1 | open(path, O_WRONLY|O_CREAT|O_TRUNC, mode); |
函数close
调用close函数关闭一个打开文件
1 |
|
函数lseek
当前文件偏移量(current file offset):它通常是一个非负整数,用以度量从文件开始处计算的字节数。
调用lseek显式的为一个打开文件设置偏移量。
1 |
|
对参数offset的解释与参数whence的值有关。
- 若
whence是SEEK_SET,则经该文件的偏移量设置为距文件开始处offset个字节。 - 若
whence是SEEK_CUR,则经该文件的偏移量设置为其当前量增加offset,offset可为正或负。 - 若
whence是SEEK_END,则经该文件的偏移量设置为文件长度加offset,offset可正可负。
函数read
调用read函数从打开文件中读数据。
1 |
|
有多种情况可使实际读到的字节数少于要求读的字节数:
- 读到普通文件时,在读到要求字计数之前已到达了文件微端。
- 当从终端设备读时,通常一次最多读一行。
- 当从网络读时,网络中的缓冲机制可能造成返回值小于所要求的字节数。
- 当从管道或
FIFO读时,如若管道包含的字节少于所需的数量,那么read将只返回实际可用的字节数。 - 当从某些面向记录的设备读时,一次最多返回一个记录。
- 当一信号造成中断,而已经读到部分数据量时。
函数write
调用write函数向打开文件写数据。
1 |
|
函数pread和pwrite
1 |
|
调用pread相当于调用lseek后调用read,但是pread又与这种顺序调用有下列重要区别。
- 调用
pread时,无法中断其定位和都读操作。 - 不更新当前文件偏移量。
调用pwrite相当于调用lseek后调用write,但也与它们有类似的区别。
原子操作(atomic operation);指的是由多步组成一个一个操作。如果该操作原子地执行,则要么执行所有步骤,要么一步也不执行,不可能只执行所有步骤的一个子集。
函数dup和dup2
复制一个现有的文件描述符
1 |
|
函数sync、fsync和fdatasync
内核通常先将数据复制到缓冲区中,然后排入队列,晚些时候写入磁盘。这种方法被称为
延迟写(delayed write).
1 |
|
sync只是将所有修改过的块缓冲区排入写队列,然后就返回,它并不等待实际写磁盘操作结束。fsync函数只对由文件描述符fd指定的一个文件起作用,并且等待写磁盘操作结束才返回。fdatasync函数类似于fsync,但它只影响文件的数据部分。
函数fcntl
改变已经打开文件的属性。
1 |
|
fcntl函数有以下5种功能。
- 复制一个已有的描述符 (
cmd=F_DUPFD或F_DUPFD_CLOEXEC)。 - 获取/设置文件描述符标志 (
cmd=F_GETFD或F_SETFD)。 - 获取/设置文件状态标志 (
cmd=F_GETFL或F_SETFL)。 - 获取/设置异步I/O所有权 (
cmd=F_GETOWN或F_SETOWN)。 - 获取/设置记录锁 (
cmd=F_GETLK或F_SETLKW)。
|cmd|功能
|
|——|:—————————————————-|
|F_DUPFD|复制文件描述符fd。|
|F_DUPFD_CLOEXEC|复制文件描述符,设置与新描述符关联的FD_CLOEXEC文件描述符标志的值,返回新文件描述符。|
|F_GETFD|对应于fd的文件描述符标志作为函数值返回。|
|F_SETFD|对于fd设置文件描述符标志。|
|F_GETFL|对应于fd的文件状态标志作为函数值返回。|
|F_SETFL|将文件状态标志设置为第3个参数的值。可以更改的几个标志是:O_APPEND、O_NONBLOCK、O_SYNC、O_DSYNC、O_RSYNC、O_FSYNC、和O_ASYNC。|
|F_GETOWN|获取当前接收SIGIO和SIGURG信号进程ID或进程组ID。|
|F_SETOWN|设置接收SIGIO和SIGURG信号的进程ID或进程组ID。|
函数ioctl
1 |
|
/dev/fd
较新的系统都提供名为/dev/fd的目录,其目录项是名为0、1、2等的文件。打开文件/dev/fdn等效于复制描述符n(假设描述符n是打开的).