文件描述符
- 文件描述符是一个非负整数
- 所有打开的文件都通过文件描述符引用
- 按照惯例,UNIX系统shell把文件描述符0与进程的标准输入关联,文件描述符1与标准输出关联,文件描述符2与标准错误关联。使用时可替换为符号常量
STD_FILENO
,STDOUT_FILENO
以及STDERR_FILENO
,常量定义在头文件<unistd.h>
函数open和openat
#include <fcntl.h>
int open(const char *path,int aflag, ... /* mode_t mode */);
int openat(int fd,const char *path, int aflag, ... /* mode_t mode */);
//两函数的返回值:成功返回文件描述符,失败返回-1
参数:
path:打开或创建的文件路径。
aflag:说明对该文件的处理方式
O_RDONLY
只读打开O_WRONLY
只写打开O_RDWR
读写打开O_EXEC
只执行打开O_SEARCH
只搜索打开
以下为一些可选常量
O_APPEND
每次写时都追加到文件的尾端O_NONBLOCK
文件的本次打开操作和后续的I/O操作设置为非阻塞
对于openat函数中的fd
- path为绝对路径,fd被忽略。
- path为相对路径,fd指出相对路径名在文件系统中的开始地址。
- path为相对路径且fd被指定为特殊值AT_FDCWD,即表示相对路径从当前工作目录中获取。
注意:由open和openat函数返回的文件描述符一定是最小的未用描述符数值。
函数creat
#include <fcntl.h>
//创建一个新文件
int creat(const char *path,mode_t mode);
//返回值:若成功返回为只写打开的文件描述符,出错返回-1
//此函数等效
open(path,O_WRONLY|O_CREAT|O_TRUNC,mode);
现在可以直接用open加上设置参数来完成creat函数的作用。
函数close
#include <unistd.h>
//关闭文件
int close(int fd);
//返回值:若成功返回0,出错返回-1
函数lseek
#include <unistd.h>
//设置文件偏移量
off_t lseek(int fd,off_t offset,int whence);
//返回值:若成功返回新的文件偏移量,出错返回-1
参数:
- fd : 对应的文件描述符
- offset: 偏移量
- whence 关于偏移开始点的设置
参数whence的若干值如下:
SEEK_SET
文件开始处SEEK_CUR
文件当前处SEEK_END
文件结尾处,此时offset可以为负值
注意:
- 返回的文件便宜量是相对于文件开始处的偏移量,offset的值是根据whence参数确定的偏移量。
- 读,写操作都从当前文件偏移量处开始,并使偏移量增加所读写的字节数。
- 当打开一个文件时,除非指定
O_APPEND
选项,否则偏移量设置为0. - 可能存在偏移量为负的情况,故因此对于函数返回值的检验应判断其是否等于-1。
- 对文件的偏移量可以大于文件的当前长度,在这种情况下,对该文件的下一次写将加长该文件,并且在文件中构成一个空洞,位于文件中但没有写过的字节都被读为0。
- 文件中的空洞不占用存储区。
函数read与write
#Include <unistd.h>
//读取数据
ssize_t read(int fd,void *buf,size_t nbytes);
//返回值: 成功则返回读取到的字节数,若到达文件尾则返回0,出错返回-1
//写入数据
ssize_t write(int fd,const void *buf,size_t nbypes);
//返回值:成功则返回写入的字节数,若出错返回-1
注意:
- 读普通文件时,在读到要求字节数之前已到达文件尾端,则直接返回读取到的字节数,同时下一次调用read时将会返回0。
- 当从终端设备读时,通常一次最多读一行。(可以改变)
- 当从网络读时,网络中的缓冲机制可能造成返回值小于所要求读的字节数。(读缓冲区数据不够,read会阻塞)
- 当从管道或者FIFO读取时,若管道包含的字节少于所需的数量,read将返回实际读取的字节数。
文件共享
内核使用3种数据结构表示打开文件。
- 每个进程在进程表中都有一个记录项,记录项中包含一张打开文件描述符表。
- 内核为所有打开文件维持一张文件表,其中有若干文件表项。
- 每个打开文件(或设备)都有一个v节点结构与一个i节点结构。其中v节点包含了文件类型和对此文件进行各种操作函数的指针。i节点包含了文件的所有者,文件长度,指向文件实际数据块在磁盘上所在位置的指针等。
linux系统只使用i节点而不使用v节点。
注意:
- 一个文件可以对应多个文件表项,这意味着可以多个进程同时操作一个文件。同时每个进程拥有自己对应的文件偏移量,文件状态设置等参数。
- 在write的完成后,在文件表项中,当前文件偏移量即增加写入的字节数,若这导致文件偏移量大于当前文件长度,此时文件长度将会被设置为当前文件偏移量。
- 如果用O_APPEND标志打开一个文件,则相应标志会被蛇者到文件表项的文件状态标志中。每次对拥有这标志的文件进行写操作时,文件表项中的当前文件偏移量首先会被设置成i节点表项中的文件长度。
- 使用lseek函数只能修改文件表项中的当前文件偏移量。
函数 dup和dup2
#include <unistd.h>
//两个函数均用来负值一个现有的文件描述符
int dup(int fd);
int dup2(int fd,inf fd2);
//返回值:成功则返回新的文件描述符,出错返回-1
差别:由dup返回的新文件描述符一定是当前可用文件描述符中的最小数值。对于dup2,可以用fd2参数指定新描述符的值。若fd2已经打开,则先将其关闭。若fd等于fd2,则dup2返回fd2。
注意:这复制的两个文件描述符共用同一个文件表项,此时文件表项中的文件状态标志与当前文件偏移量是公用的。
函数sync,fsync和fdatasync
#include <unistd.h>
int fsync(int fd);
int fdatasynv(int fd);
//返回值:若成功返回0,出错返回-1
void sync(void);
- sync函数将所有修改过的块缓冲区排入写队列,然后就返回,并不等待实际写磁盘操作结束。通常,称为update的系统守护进程会周期性地调用(一般为30s)sync函数,保证了定期冲洗(flush)内核的块缓冲区。
- fsync函数只对文件描述符fd指定的一个文件起作用,并且等待写磁盘操作结束才返回。fsync可用于数据库这样的应用程序,这种应用程序需要确保修改过的块立即写到磁盘上。
- fdatasync函数类似于fsync,但他只影响文件的数据部分,而除数据外,fsync还会同步更新文件的属性。
函数fcntl
#include <fcntl.h>
//文件控制函数
int fcntl(int fd,int cmd,.../*int arg*/);
//返回值:成功则返回值依赖于不同的cmd,出错返回-1
fcntl函数的功能
- 复制一个已有的描述符(
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_SETLK
或F_SETLKW
)
fcntl各功能具体分析
F_DUPFD
复制文件描述符fd,新文件描述符作为函数值返回。F_GETFD
对应于fd的文件描述符标志作为函数值返回。F_SETFD
对于fd设置文件描述符标志,新标志值按第三个参数设置F_GETFL与F_SETFL
与上述两者相似,不同在于是获得与修改文件状态标志。
注意:- 在设置文件描述符标志或者文件状态标志前,应当先获取当前的标志值,然后修改它在将其设置为新的标志值,不应当只调用设置函数,避免关闭以前设置的其他无关标志位。
来源:CSDN
作者:来日可期cy
链接:https://blog.csdn.net/weixin_44524204/article/details/104631221