文件描述符到底是啥

1,601 阅读4分钟

在平时的 Android 开发中,你与文件描述符打过交道吗?一些知识点会涉及到文件描述符,比如:

  • mmap 函数的文件描述符参数
  • epoll 机制对文件描述符的限制问题
  • ...

这时,如果让你说说对文件描述符的了解,你能回答上来吗?

回答不上来也没关系,来,集中注意力,我们一起来学习:


Linux 中一切都可以看作文件,包括普通文件、链接文件、Socket 以及设备驱动等,对其进行相关操作时,都可能会创建对应的文件描述符。文件描述符(file descriptor)是内核为了高效管理已被打开的文件所创建的索引,用于指代被打开的文件,对文件所有 I/O 操作相关的系统调用都需要通过文件描述符。

文件描述符与文件是什么关系呢?下图 Linux 中的三张表可以体现:

  • 进程级别的文件描述符表:内核为每个进程维护一个文件描述符表,该表记录了文件描述符的相关信息,包括文件描述符、指向打开文件表中记录的指针。

  • 系统级别的打开文件表:内核对所有打开文件维护的一个进程共享的打开文件描述表,表中存储了处于打开状态文件的相关信息,包括文件类型、访问权限、文件操作函数(file_operations)等。

  • 系统级别的 i-node 表:i-node 结构体记录了文件相关的信息,包括文件长度,文件所在设备,文件物理位置,创建、修改和更新时间等,"ls -i" 命令可以查看文件 i-node 节点

文件描述符是一种系统资源,可以通过以下命令来查看文件描述符的上限:

#查看所有进程允许打开的最大 fd 数量
126|generic_x86:/ # cat /proc/sys/fs/file-max
174139

#查看所有进程已经打开的 fd 数量以及允许的最大数量
generic_x86:/ # cat /proc/sys/fs/file-nr
11040   0       174139

#查看单个进程允许打开的最大 fd 数量.
generic_x86:/ # ulimit -n
32768

也可以查看某进程当前已使用的 fd :

#查看某进程(进程 id 为 15077)已经打开的 fd
generic_x86:/ # ls -l /proc/15077/fd/
total 0
lrwx------ 1 u0_a136 u0_a136 64 2020-04-15 23:04 0 -> /dev/null
lrwx------ 1 u0_a136 u0_a136 64 2020-04-15 23:04 1 -> /dev/null
lrwx------ 1 u0_a136 u0_a136 64 2020-04-15 23:04 35 -> /dev/binder
lrwx------ 1 u0_a136 u0_a136 64 2020-04-09 01:01 44 -> socket:[780404]
lrwx------ 1 u0_a136 u0_a136 64 2020-04-15 23:04 55 -> /dev/ashmem
lrwx------ 1 u0_a136 u0_a136 64 2020-04-15 23:04 60 -> /dev/ashmem
...

上面这个进程是一个 Android 应用进程,所以能看到 ashmem、binder 等 Android 特有设备文件相关的 fd 。再来看一个实际打开磁盘文件的例子:

     File file = new File(getCacheDir(), "testFdFile");
     FileOutputStream out = new FileOutputStream(file);

执行上面代码后会申请一个对应的 fd:

# ls -l /proc/{pid}/fd/
...
l-wx------ u0_a55   u0_a55  2020-04-16 00:24 995 -> /data/data/com.example.test/cache/testFdFile
...

实际开发中,可能会遇到 fd 资源超过上限导致的 "Too many open files" 之类的问题,一般都是因为没有及时释放掉 fd,比如上面代码中 FileOutputStream 没有关闭,若循环执行超过单个进程允许打开的最大 fd 数量,程序就会出现异常。


通过上面对文件描述符的学习,你一定能回答 "说说对文件描述符的了解" 这个问题了。

学无止境,又一个问题出现了:怎么排查 fd 泄漏的原因 ,你能回答上来吗?

看完了这三位同学的面试表现,你有什么感想呢?

听说🤔同学也关注我了哦