1. 文件描述符
一个非负整数,当打开一个现有文件或创建一个新文件时,内核向进程返回一个文件描述符。
每个文件打开时会在内核中建立一个文件表项,这个文件表项包括文件的状态信息、存储文件内容的缓冲区、当前文件的读写位置等,这些文件表项保存在内核的一个数组(文件表)里。
每个进程在内核中有一个整形数组,里面的元素就是文件表的下标,通过这个下标可引用打开的文件表项,这个下标就是文件描述符
文件描述符0或符号常量STDIN_FILENO与进程的标准输入关联,1或符号常量STDOUT_FILENO与标准输出关联,2或STDERR_FILENO与标准错误关联(unistd.h)
2. 文件I/O操作
open打开、close关闭、lseek定位、read读、write写
2.1 文件打开
(1)open
成功,返回一个可用的最小文件描述符数值;出错返回-1
flags参数:
这3个选项互斥,只能选一个且必须有一个
可通过 | 连接使用多个选项
mode_t mode参数:
当要打开的文件不存在,且需要在创建时才有用,参数代表文件的权限 并受 umask 文件权限掩码的影响
(2)openat
dirfd参数:
①若path指定了绝对路径,则dirfd参数无用,openat等同于open
②path为相对路径,dirfd参数指出了相对路径名在文件系统中的开始地址
③path为相对路径,dirfd为 AT_FDCWD,路径名在当前工作目录中获取
openat的作用:
①让线程可以使用相对路径名打开目录中的文件,而不再只能打开当前工作目录。
②避免time-of-check-to-time-of-use(TOCTTOU)错误
TOCTTOU错误:有两个基于文件的函数调用,第二个依赖于第一个调用的结果,由于两个调用不是原子操作可能导致结果有问题。
(3)create
等同 open(path,O_WRONLYY | O_CREAT | O_TRUNC,mdoe)
意义:如果文件不存在则创建该文件并以只写方式打开;如果已存在则将其 截短 为0,并以只写方式打开
2.2 文件关闭
关闭文件,释放进程加在该文件上的所有记录锁。成功返回1,失败返回-1
用close关闭远程的网络文件时,需要检查close函数是否成功执行
close关闭时,会将内存缓冲区中内容写到文件里,关闭网络文件相当于与外端进行一次数据通信,由于可能有数据包的丢失,因此容易出错。
2.3 文件定位
(1)
每个打开文件有一个当前文件偏移量current file offset, 非负整数 ,度量从文件开始处计算的字节数。除了 O_APPEND追加选项外,偏移量一般为0
成功返回新文件偏移量;出错返回-1
whence:
SEEK_SET:文件开始
SEEK_CUR:当前值
SEEK_END:文件尾
(2)
①检查是否等于-1,而不是检查是否小于0
②文件偏移量大于文件长度,对文件写,则文件加长了,新写的数据要分配磁盘块,在原文件尾端和新开始写位置之间不需要分配磁盘块。但是如果去读中间这部分,读出来结果都是0。
(3)其它作用:确定文件是否可以设置偏移量
对管道、FIFO、网络套接字调用,lseek返回-1,设置 errno 为 ESPIPE
1 #include <unistd.h> 2 #include <sys/types.h> 3 #include <stdio.h> 4 5 int main() 6 { 7 if(lseek(STDIN_FILENO,0,SEEK_CUR)==-1) { 8 printf("can not seek\n"); 9 } 10 else { 11 printf("seek ok\n"); 12 } 13 return 0; 14 }
结果,不能对标准输入设置偏移量
2.4 文件读
成功返回读到的字节数;若已到文件尾返回0;出错返回-1
2.5 文件写
成功返回已写字节数;失败返回-1
2.6 文件截短
去掉文件尾的一些数据,使文件保证在一定的尺寸之内。
成功返回0;失败返回-1
超过length字节数的部分被系统抛弃;如果length比文件实际字节数还长,则扩展文件,文件实际尾和扩展后文件尾中间形成文件空洞。
length为0则清空这个文件,相当于打开时加上参数 O_TRUNC
2.7 文件共享
内核使用3种数据结构表示打开文件,它们之间的关系决定了文件共享时一个进程对另一个进程可能产生的影响。
(1)进程在进程表里面有一个整型数组,数组元素为文件描述符
文件描述符:文件描述符标志
指向文件表里的某一个文件表项
(2)内核为所有打开文件维持一张文件表,每个文件表项包含:
文件状态标志:读、写、添加、同步、非阻塞等
文件偏移量
指向文件v节点表项的指针
(3)每个打开文件(或设备)都有一个 v 节点结构。包含了文件类型和对此文件进行各种操作函数的指针。
大多数文件的v节点还包含了该文件的 i 节点(i节点包含文件所有者、文件长度、文件实际数据块在磁盘所在位置的指针)
linux没有使用v节点,而是使用了通用的i结点
一个进程打开了两个文件的示意图:
两个进程打开了同一个文件示意图:
两个进程有各自的文件表项,各自的文件描述符,但对一个文件只有一个v节点表项。
tips:
会有多个文件描述符指向同一个文件表项的情况
ls /proc/进程号/fd 查看进程打开的文件描述符
3. 文件描述符操作
成功返回新的文件描述符;出错返回-1
新的文件描述符和原来的文件描述符共享同一个文件表项。
dup2:用newfd指定新描述符的值,若newfd已经打开则先将其关闭;若newfd和oldfd相等,则返回newfd而不关闭它。
CGI程序中,对标准输出重定向后,可用printf函数输出数据到连接套接字上去:
dup2(connfd,STDOUT_FILENO)
原来STDOUT_FILENO与终端设备关联,调用dup2后,这个文件描述符就和套接字一起指向套接字的文件表项了。
dup(fd)等效 fcntl(fd,F_DUPFD,0)
dup2(fd,fd2) 等效于 close(fd2); fcntl(fd,F_DUPFD,fd2);
但dup2是原子操作,且dup2和fcntl的errno不同
4.
向文件写入数据时,内核先将数据写到缓冲区,然后排入队列,晚些时候再写入磁盘。为了保证磁盘实际文件和缓冲器内容的一致,有如下几个函数:
成功返回0;失败返回-1
sync:将所有修改过的块缓冲区排入写队列,然后不等实际写磁盘结束就返回
由 update 守护进程周期性调用sync
fsync:对fd指定的文件起作用,等待写磁盘结束才返回
fdatasync:类似fsync,但只影响文件的数据部分,fsync还会更新文件的属性
5. fcntl函数
返回值与cmd标志有关;失败返回-1
作用:
F_DUPFD:复制文件描述符fd,返回新文件描述符。新描述符与原来的共享同一个文件表项,但有自己的文件描述符标志(FD_CLOEXEC标志被清除)
F_DUPFD_CLOEXEC:复制文件描述符,并设置新文件描述符关联的 FD_CLOEXEC标志的值
F_GETFD:回去文件描述符标志
F_SETFD:按第3个参数设置文件描述符标志
F_GETFL:获取fd对应文件的状态标志,如下:
F_SETFL:可设置O_APPEND、O_NONBLOCK、O_SYNC、O_RSYNC、O_FSYNC、O_ASYNC
F_GETOWN:获取当前接收一部I/O信号SIGIO、SIGURG信号的进程ID或进程组ID
F_SETOWN:设置接收这两个信号的进程ID或进程组ID(arg为正表示进程ID,为负表示进程组ID)
6. ioctl
设备驱动程序用
来源:https://www.cnblogs.com/taoXiang/p/12367162.html