在Linux通用I/O模型中,I/O
操作系列函数(系统调用)都是围绕一个叫做文件描述符的整数展开。这不禁让人产生疑问:这个整数代表什么?一个数值代表一个文件吗?随便传一个整数进去调用可以吗?
原文地址:https://learn-linux.readthedocs.io 。玩转Linux旧群已满,请加新群:278378501 。
欢迎关注我们的公众号:小菜学编程 (coding-fan)
解答以上疑问,需要更深入学习——文件描述符(File Descriptor)。
图解
理解具体情况,需要了解由内核维护的3个数据结构:
- 进程级文件描述符表(file descriptor table)
- 系统级打开文件表(open file table)
- 文件系统i-node表(i-node table)
这3个数据结构之间的关系如下图所示:
文件描述符表
内核为每个进程维护一个文件描述符表,该表每一条目都记录了单个文件描述符的相关信息,包括:
-
控制标志(flags),目前内核仅定义了一个,即
close-on-exec
- 打开文件描述体指针
打开文件表
内核对所有打开的文件维护一个系统级别的打开文件描述表(open file description table),简称打开文件表。表中条目称为打开文件描述体(open file description),存储了与一个打开文件相关的全部信息,包括:
-
文件偏移量(file offset),调用
read()
和write()
更新,调用lseek()
直接修改 -
访问模式,由
open()
调用设置,例如:只读、只写或读写等 i-node
对象指针
i-node表
每个文件系统会为存储于其上的所有文件(包括目录)维护一个i-node
表,单个i-node
包含以下信息:
-
文件类型(file type),可以是常规文件、目录、套接字或
FIFO
- 访问权限
- 文件锁列表(file locks)
- 文件大小
- 等等
i-node
存储在磁盘设备上,内核在内存中维护了一个副本,这里的i-node
表为后者。副本除了原有信息,还包括:引用计数(从打开文件描述体)、所在设备号以及一些临时属性,例如文件锁。
场景解析
上图中,详细描述了两个进程诸多文件描述符,以及相互关系。
文件描述符复制
在进程A
中,文件描述符1和文件描述符20都指向同一个打开文件描述体(标号23)。这很可能是通过调用dup()
系列函数形成的。
文件描述符复制,在某些场景下非常有用,比如:标准输入/输出重定向。在shell
下,完成这个操作非常简单,大部分人都会,但是极少人思考过背后的原理。
大概描述一下需要的几个步骤,以标准输出(文件描述符为1)重定向为例:
- 打开目标文件,返回文件描述符n;
- 关闭文件描述符1;
- 调用
dup
将文件描述符n复制到1; - 关闭文件描述符n;
子进程继承文件描述符
进程A
的文件描述符2和进程B
的文件描述符2都指向同一个打开文件描述体(标号73)。这种情形很可能发生在调用fork()
派生子进程之后,比如A
调用fork()
派生出B
。这时,B
作为子进程,从父进程A
继承了文件描述符表,其中包括图中标明的文件描述符2。这就是子进程继承父进程打开的文件
这句话的由来。
当然了,进程A
通过Unix
套接字将一个文件描述符传递给B
也会出现类似的情形,但一般文件描述符数值是不一样的。同时为2要非常凑巧才发生。
下一步
更多文章请访问:学习Linux
订阅更新,获取更多学习资料,请关注我们的 微信公众号 :
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。