文件描述符
在内核中,文件描述符是非负整数,使用open、creat会返回该文件的描述符,可以将此返回值作为read、write的参数。
按照惯例0,1,2这三个文件描述符对应标准输入(STDIN_FILENO)、标准输出(STDOUT_FILENO)和标准错误(STDERR_FILENO),括号中的宏是为了提升代码可读性,定义在头文件unistd.h中。
早期的文件描述符是0-19,后来上线增加到63,linux3.2.0之后文件描述符增加至无限,受系统配置、资源有关。
open和openat
最后...参数是表示可变参数,只有创建新文件的时候才使用。path是文件名字。oflag是文件选项,以下宏定义在fcntl.h中,是oflag的值
O_RDONLY 只读
O_WRONLY 只写
O_RDWR 读写打开
O_EXEC 只执行打开
O_SEARCH 只搜索打开(用于目录,目前未支持)
以上尝试必须出现且只能指定一个。
O_APPEND 写时追加文件末尾
O_CREAT 文件不存在时创建,使用此选项时需要指定mode
O_DIRECTORY 如果path不是目录则出错
还有其他选项这里未记录,这些选项都是根据需要自己选择。
这两个函数返回的描述符一定是最小未被使用的值,可以通过关闭1号描述符,再打开另一个文件,这样1号文件描述符就被指定到新打开的文件中了。
fd参数是区分两个函数的,有三种可能
- path是绝对路径名,这种情况下fd被忽略
- path是相对文件名,fd参数指定出相对路径名在文件系统中的开始地址,fd参数是打开的相对路径名所在的目录来获取的。
- path指定了相对路径,fd参数具有特殊值AT_FDCWD。这种情况下,路径名在当前工作目录获取。
若整个路径名超过PATH_MAX,或任意文件名超过NAME_MAX,会出现截断情况,在POSIX_NO_TRUNC有效的情况下。此时会出现出错,将error设置为ENAMETOOLONG。之前的系统文件名只支持14长度,现在的系统支持255的长度。
creat
早期的open函数第二个参数只能是0,1,2就是前面提到的只读那几个宏。因此无法打开一个不存在的文件,需要先调用此函数,创建文件,并且creat打开的文件是以只写的方式打开的。所以早期的打开不存在文件的顺序是creat,close,open。
lseek
此函数是用来设置文件的偏移量的,内核在进程的某地方记录了文件下次写入到文件中的位置,这个位置就是偏移量。
offset与whence有关联
- whence是SEEK_SET,则文件偏移量设置为offset。
- whence是SEEK_CUR,则文件偏移量设置为当前值加offset,offset可正可负。
- whence是SEEK_END,则文件偏移量设置为文件长度加offset,offset可正可负。
执行成功返回新的文件偏移量,如果文件描述符是管道,FIFO或网络套接字,则返回-1,error设置为ESPIPE。
在移动文件偏移量到文件长度之外的地方后进行写入,文件会出现一段空洞部分,此空洞部分不会分配磁盘块
read
实际读到的字节数,小于要求读到的字节数:
- 普通文件读到文件末尾
- 终端设备读时,一次只能读取一行
- 从网络读
- 从管道、FIFO读时
- 当一信号造成中断时,只返回读取部分
write
返回值通常与nbytes一致,否则出错,原因可能是磁盘写满了,超过文件长度限制了。写时从当前文件偏移量开始。
文件共享
在内核中,文件使用一个v节点保存文件的属性,已经内容在硬盘的硬盘块。进程中用文件描述符指向文件表项,文件表项有个值指向v节点,
此图是两个进程打开同一个文件,然后各自的文件表项指向同一个v节点。文件表象有文件偏移量,在对文件进行读写时,各各进程用自己的偏移量去操作文件。但是多个进程同时对一个文件进行写操作时,会出现预想不到的结果。因此需要保证原子操作。
原子操作
刚刚说到的多进程进行写操作时的问题。写操作涉及多个步骤。这多个操作不能一次性完成,会出现进程调用a进程执行了几个步骤,然后切换到了b进程执行。和多线程是一样的问题。这里只简单说一下,可以查阅其他资料,了解出现错误原因。
dup和dup2
dup返回当前最小未使用描述符,dup2可以用fd2指定为新的文件描述符。如果fd2存在,则先关闭,再打开。fd==fd2,则直接返回fd2。执行dup(1)后的样子。dup这两个函数是原子操作,执行过程不会被打断。
sync、fsync和fatasync
内核在对文件进行写入时,不会立即写入到硬盘,而是从用户区,拷贝到内核的缓冲区,然后排入队列中,再晚点的时候写入到硬盘中,比如缓冲区写满了时。
sync将所有修改过的快缓冲区排入写队列,不等待写磁盘操作结束。
系统有个守护进程,周期性的调用sync函数,保证定期刷新缓存区。
fsync只对指定的文件起作用,并且会等带写磁盘操作结束时,才会返回。
fdatasync,和fsync类似,但是此函数只影响文件数据部分。
fcntl
fcntl系统调用可以用来对已打开的文件描述符进行各种控制操作以改变已打开文件的的各种属性,返回值与cmd参数有关,返回-1出错。
在修改文件描述符标志或文件状态标志时必须谨慎,先要获得现在的标志值,然后按照期望修改它,最后设置新标志值。不能只是执行F_SETFD或F_SETFL命令,这样会关闭以前设置的标志位。
/dev/fd
此目录下存放的都是打开的文件描述符,使用open打开已存在的,表示复制描述符n(假设n已经被打开)。如果使用creat、open在此目录下进行重新创建操作,会导致底层文件被截断。