我写了一个分析 Linux 平台打开文件描述符跨进程传递的工具

Linux 系统的设计中,继承了 Unix "一切皆文件" (Everything is a file) 的思想,系统中的众多对象,都可以表示为文件,可以对它们执行文件操作,如 read()write()mmap()ioctl()close()poll() 等。Linux 系统支持通过 Unix 域 socket 等机制跨进程传递打开的文件描述符。跨进程传递打开的文件描述符,常常用于各种各样的目的,如:

  • 跨进程传递打开的 eventfd,用来做进程间的事件通知。
  • 跨进程传递打开的设备文件的文件描述符。
  • 跨进程传递 memfd,以实现高效的跨进程数据传递。
  • bluez 和系统音频服务器之间,跨进程传递打开的蓝牙 socket。
  • 跨进程传递打开的普通文件。

跨进程传递打开的文件描述符时,不同进程间的文件描述符的数值不一定完全一样,这常常给相关的调试带来障碍。

笔者利用 /proc 虚拟文件系统中导出的 fdinfo 信息,开发了一个分析跨进程打开文件传递的工具 duplicated_fd_in_system.py。对于进程打开的文件描述符,在 /proc/[pid]/fdinfo/ 目录下,有一个对应的文件描述它的基本信息,不同类型的文件,这些信息有一些差异。一些典型的 fdinfo 信息如下:

复制代码
fdinfo_path:/proc/1960/fdinfo/0, fdinfo:
pos:	0
flags:	0100000
mnt_id:	26
ino:	5

fdinfo_path:/proc/1960/fdinfo/1, fdinfo:
pos:	0
flags:	02
mnt_id:	9
ino:	28250
scm_fds: 0

fdinfo_path:/proc/1960/fdinfo/2, fdinfo:
pos:	0
flags:	02
mnt_id:	9
ino:	28250
scm_fds: 0

fdinfo_path:/proc/1960/fdinfo/4, fdinfo:
pos:	0
flags:	02000002
mnt_id:	16
ino:	2086
tfd:        5 events:       19 data:     564036e84f00  pos:0 ino:826 sdev:f
tfd:        7 events:       19 data:     564036e84f90  pos:0 ino:826 sdev:f
tfd:       53 events:       19 data:     564036ed0ec0  pos:0 ino:16efd sdev:8
tfd:       16 events:       19 data:     564036ea0290  pos:0 ino:826 sdev:f
tfd:       22 events:       19 data:     564036f993f0  pos:0 ino:4e38 sdev:8
tfd:       17 events:       19 data:     564036ea11a0  pos:0 ino:826 sdev:f
tfd:       12 events:       19 data:     564036e917d0  pos:0 ino:826 sdev:f
tfd:        8 events:       19 data:     564036e85000  pos:0 ino:826 sdev:f
tfd:       54 events:       19 data:     564036ea0190  pos:0 ino:1bd65 sdev:8
tfd:       49 events:       19 data:     564037065e20  pos:0 ino:17812 sdev:8
tfd:       31 events:       19 data:     564036f70f90  pos:0 ino:739a sdev:8
tfd:        3 events:       19 data:     564036ea0220  pos:0 ino:8d78 sdev:8
tfd:       32 events:       19 data:     564036f0cc20  pos:0 ino:5f44 sdev:8
tfd:       30 events:       19 data:     564036fe2cd0  pos:0 ino:6782 sdev:8
tfd:       50 events:       19 data:     7fea7381b0a8  pos:0 ino:274 sdev:5
tfd:       29 events:       19 data:     564036ed1880  pos:0 ino:7301 sdev:8
tfd:       18 events:       19 data:     564036ea8a40  pos:0 ino:826 sdev:f
tfd:       20 events:       18 data:     564036eaade0  pos:0 ino:6e6f sdev:8
tfd:       21 events:       19 data:     564036eaae80  pos:0 ino:6e6f sdev:8
tfd:       35 events:       19 data:     564036eccf70  pos:0 ino:6e7a sdev:8
tfd:       34 events:       19 data:     564036ecc6e0  pos:0 ino:6e79 sdev:8
tfd:       52 events:       19 data:     5640370af920  pos:0 ino:12bc3 sdev:8
tfd:       33 events:       19 data:     564036f0ab60  pos:0 ino:77e6 sdev:8

fdinfo_path:/proc/1960/fdinfo/5, fdinfo:
pos:	0
flags:	02004002
mnt_id:	16
ino:	2086
eventfd-count:                0
eventfd-id: 175
eventfd-semaphore: 0

fdinfo_path:/proc/1960/fdinfo/6, fdinfo:
pos:	0
flags:	02000002
mnt_id:	16
ino:	2086
eventfd-count:                0
eventfd-id: 177
eventfd-semaphore: 1

fdinfo_path:/proc/1960/fdinfo/7, fdinfo:
pos:	0
flags:	02004002
mnt_id:	16
ino:	2086
sigmask:	0000000000000002

fdinfo_path:/proc/1960/fdinfo/8, fdinfo:
pos:	0
flags:	02004002
mnt_id:	16
ino:	2086
sigmask:	0000000000004000

fdinfo_path:/proc/1960/fdinfo/9, fdinfo:
pos:	0
flags:	02000002
mnt_id:	16
ino:	2086
tfd:       10 events:       19 data:     564036e916a0  pos:0 ino:826 sdev:f
tfd:       26 events:       19 data:     564036eb7dd8  pos:0 ino:826 sdev:f
tfd:       23 events:       19 data:     564036eb6648  pos:0 ino:826 sdev:f

fdinfo_path:/proc/1960/fdinfo/15, fdinfo:
pos:	0
flags:	02100000
mnt_id:	2601
ino:	79
lock:	1: FLOCK  ADVISORY  WRITE 1960 00:45:79 0 EOF

fdinfo_path:/proc/1960/fdinfo/17, fdinfo:
pos:	0
flags:	02004002
mnt_id:	16
ino:	2086
clockid: 1
ticks: 0
settime flags: 00
it_value: (0, 0)
it_interval: (0, 0)

各个文件描述符信息,都包含posflagsmnt_idino 四个字段。在整个 Linux 系统中,对于一般的文件,可以通过挂载点和 inode 号唯一地确定一个文件,在文件描述符信息中,它们分别用 mnt_idino 字段描述。更特殊的文件描述符,包含一些其它字段,eventfd 的包含 eventfd-id 等字段,且它们的 mnt_idino 都是 162086 ,不同的 eventfd 用 eventfd-id 来区分;UNIX 域 socket 的包含 scm_fds 等字段;timerfd 的包含 clockidticks 等字段;signalfd 的包含 sigmask 等字段;epollfd 的包含 tfd 等字段;inotifyfd 的包含 inotify wd 等字段。

/proc/self/mountinfo 文件中,包含 Linux 系统的文件系统挂载信息,这个文件的内容类似于下面这样:

复制代码
$ cat /proc/self/mountinfo 
24 29 0:22 / /sys rw,nosuid,nodev,noexec,relatime shared:7 - sysfs sysfs rw
25 29 0:23 / /proc rw,nosuid,nodev,noexec,relatime shared:13 - proc proc rw
26 29 0:5 / /dev rw,nosuid,relatime shared:2 - devtmpfs udev rw,size=20329208k,nr_inodes=5082302,mode=755,inode64
27 26 0:24 / /dev/pts rw,nosuid,noexec,relatime shared:3 - devpts devpts rw,gid=5,mode=620,ptmxmode=000
28 29 0:25 / /run rw,nosuid,nodev,noexec,relatime shared:5 - tmpfs tmpfs rw,size=4073528k,mode=755,inode64
29 1 259:5 / / rw,relatime shared:1 - ext4 /dev/nvme0n1p5 rw,errors=remount-ro
30 24 0:6 / /sys/kernel/security rw,nosuid,nodev,noexec,relatime shared:8 - securityfs securityfs rw
31 26 0:26 / /dev/shm rw,nosuid,nodev shared:4 - tmpfs tmpfs rw,inode64
32 28 0:27 / /run/lock rw,nosuid,nodev,noexec,relatime shared:6 - tmpfs tmpfs rw,size=5120k,inode64
33 24 0:28 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime shared:9 - cgroup2 cgroup2 rw,nsdelegate,memory_recursiveprot
34 24 0:29 / /sys/fs/pstore rw,nosuid,nodev,noexec,relatime shared:10 - pstore pstore rw
35 24 0:30 / /sys/firmware/efi/efivars rw,nosuid,nodev,noexec,relatime shared:11 - efivarfs efivarfs rw
36 24 0:31 / /sys/fs/bpf rw,nosuid,nodev,noexec,relatime shared:12 - bpf bpf rw,mode=700
37 25 0:32 / /proc/sys/fs/binfmt_misc rw,relatime shared:14 - autofs systemd-1 rw,fd=29,pgrp=1,timeout=0,minproto=5,maxproto=5,direct,pipe_ino=32794
38 26 0:33 / /dev/hugepages rw,relatime shared:15 - hugetlbfs hugetlbfs rw,pagesize=2M
39 26 0:20 / /dev/mqueue rw,nosuid,nodev,noexec,relatime shared:16 - mqueue mqueue rw
40 24 0:7 / /sys/kernel/debug rw,nosuid,nodev,noexec,relatime shared:17 - debugfs debugfs rw
41 24 0:12 / /sys/kernel/tracing rw,nosuid,nodev,noexec,relatime shared:18 - tracefs tracefs rw
42 24 0:34 / /sys/fs/fuse/connections rw,nosuid,nodev,noexec,relatime shared:19 - fusectl fusectl rw
43 24 0:21 / /sys/kernel/config rw,nosuid,nodev,noexec,relatime shared:20 - configfs configfs rw
66 28 0:35 / /run/credentials/systemd-sysusers.service ro,nosuid,nodev,noexec,relatime shared:21 - ramfs ramfs rw,mode=700
44 28 0:36 / /run/qemu rw,nosuid,nodev,relatime shared:22 - tmpfs tmpfs rw,mode=755,inode64
45 29 7:0 / /snap/bare/5 ro,nodev,relatime shared:23 - squashfs /dev/loop0 ro,errors=continue,threads=single
46 29 7:1 / /snap/core18/2846 ro,nodev,relatime shared:24 - squashfs /dev/loop1 ro,errors=continue,threads=single
47 29 7:2 / /snap/core18/2855 ro,nodev,relatime shared:25 - squashfs /dev/loop2 ro,errors=continue,threads=single
48 29 7:4 / /snap/core20/2501 ro,nodev,relatime shared:26 - squashfs /dev/loop4 ro,errors=continue,threads=single
49 29 7:3 / /snap/core20/2496 ro,nodev,relatime shared:27 - squashfs /dev/loop3 ro,errors=continue,threads=single
51 29 7:6 / /snap/core22/1908 ro,nodev,relatime shared:29 - squashfs /dev/loop6 ro,errors=continue,threads=single
53 29 7:8 / /snap/firefox/6042 ro,nodev,relatime shared:31 - squashfs /dev/loop8 ro,errors=continue,threads=single
54 29 7:9 / /snap/gnome-3-38-2004/112 ro,nodev,relatime shared:32 - squashfs /dev/loop9 ro,errors=continue,threads=single
55 29 7:10 / /snap/gnome-3-38-2004/143 ro,nodev,relatime shared:33 - squashfs /dev/loop10 ro,errors=continue,threads=single
56 29 7:12 / /snap/gnome-42-2204/202 ro,nodev,relatime shared:34 - squashfs /dev/loop12 ro,errors=continue,threads=single
57 29 7:11 / /snap/gnome-42-2204/176 ro,nodev,relatime shared:35 - squashfs /dev/loop11 ro,errors=continue,threads=single
58 29 7:13 / /snap/gtk-common-themes/1535 ro,nodev,relatime shared:36 - squashfs /dev/loop13 ro,errors=continue,threads=single
59 29 7:15 / /snap/pycharm-community/465 ro,nodev,relatime shared:37 - squashfs /dev/loop15 ro,errors=continue,threads=single
61 29 7:17 / /snap/snap-store/1216 ro,nodev,relatime shared:39 - squashfs /dev/loop17 ro,errors=continue,threads=single
62 29 7:16 / /snap/snap-store/1113 ro,nodev,relatime shared:40 - squashfs /dev/loop16 ro,errors=continue,threads=single
64 29 7:19 / /snap/snapd/23771 ro,nodev,relatime shared:42 - squashfs /dev/loop19 ro,errors=continue,threads=single
65 29 7:20 / /snap/snapd-desktop-integration/247 ro,nodev,relatime shared:43 - squashfs /dev/loop20 ro,errors=continue,threads=single
67 29 259:5 /usr/share/hunspell /var/snap/firefox/common/host-hunspell ro,noexec,noatime shared:1 - ext4 /dev/nvme0n1p5 rw,errors=remount-ro
159 29 7:21 / /snap/snapd-desktop-integration/253 ro,nodev,relatime shared:44 - squashfs /dev/loop21 ro,errors=continue,threads=single
163 29 259:13 / /media/data rw,relatime shared:55 - ext4 /dev/nvme1n1p2 rw
166 29 259:6 / /home rw,relatime shared:94 - ext4 /dev/nvme0n1p6 rw
169 29 259:10 / /media/data2 rw,relatime shared:96 - ext4 /dev/nvme0n1p10 rw
172 29 259:2 / /boot/efi rw,relatime shared:98 - vfat /dev/nvme0n1p2 rw,fmask=0077,dmask=0077,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro
175 37 0:37 / /proc/sys/fs/binfmt_misc rw,nosuid,nodev,noexec,relatime shared:100 - binfmt_misc binfmt_misc rw
1982 28 0:25 /snapd/ns /run/snapd/ns rw,nosuid,nodev,noexec,relatime - tmpfs tmpfs rw,size=4073528k,mode=755,inode64
2601 28 0:69 / /run/user/1000 rw,nosuid,nodev,relatime shared:1105 - tmpfs tmpfs rw,size=4073524k,nr_inodes=1018381,mode=700,uid=1000,gid=1000,inode64
2640 2601 0:70 / /run/user/1000/gvfs rw,nosuid,nodev,relatime shared:1167 - fuse.gvfsd-fuse gvfsd-fuse rw,user_id=1000,group_id=1000
2679 2601 0:71 / /run/user/1000/doc rw,nosuid,nodev,relatime shared:1187 - fuse.portal portal rw,user_id=1000,group_id=1000
3359 1982 0:4 mnt:[4026532459] /run/snapd/ns/snap-store.mnt rw - nsfs nsfs rw
1528 29 7:22 / /snap/firefox/6073 ro,nodev,relatime shared:718 - squashfs /dev/loop22 ro,errors=continue,threads=single
4011 1982 0:4 mnt:[4026532936] /run/snapd/ns/firefox.mnt rw - nsfs nsfs rw
52 29 259:8 / /media/hanpfei/649E63CF9E6397F6 rw,nosuid,nodev,relatime shared:30 - ntfs3 /dev/nvme0n1p8 rw,uid=1000,gid=1000,windows_names,iocharset=utf8
4836 29 7:7 / /snap/pycharm-community/471 ro,nodev,relatime shared:917 - squashfs /dev/loop7 ro,errors=continue,threads=single
60 29 7:14 / /snap/snapd/24505 ro,nodev,relatime shared:38 - squashfs /dev/loop14 ro,errors=continue,threads=single
63 29 7:18 / /snap/core22/1963 ro,nodev,relatime shared:41 - squashfs /dev/loop18 ro,errors=continue,threads=single

/proc/self/mountinfo 文件的格式如下:

复制代码
<mount_id> <parent_id> <major:minor> <root> <mount_point> <options> <optional_fields> <filesystem_type> <source> <super_options>

/proc/self/mountinfo 文件中的 shared 字段可以简单理解为 mount_id 的别名。根据 fdinfo 信息中的 mnt_id/proc/self/mountinfo 文件中的信息,可以找到文件的挂载点。

duplicated_fd_in_system.py 工具的设计思路大概为:

  1. 加载各个进程打开文件的 fdinfo 信息,fdinfo 信息用字典保存,不同类型的文件,其 fdinfo 信息的字典字段不完全一样。各个打开文件的 fdinfo 信息,用一个以文件描述符为 key 的字典保存。所有打开文件的 fdinfo 信息和进程的其它信息,如进程名,用一个字调保存。
  2. 为这些打开的文件描述符的 fdinfo 信息建立索引,其中包含文件的类型、mount_idino 及不同文件类型特有的一些信息,如 eventfd 的 eventfd-id 等,创建一个以该索引为键,数组为值的字典,数组的元素为进程 ID 和文件描述符组成的元组。
  3. 检查 fdinfo 信息索引,输出进程间共享的打开文件描述符。

duplicated_fd_in_system.py 工具对于分析 eventfd 的跨进程传递比较有效,其它一些文件类型,可能很少有跨进程传递的需要,如 epollfd 等,只能提供一些提示性信息。

duplicated_fd_in_system.py 工具的更多详细信息,可以参考其源码。

欢迎各位朋友帮忙给 Gitee 的 Repo star,感谢感谢。

Done.

相关推荐
一刀到底2111 小时前
从实列中学习linux shell9 如何确认 服务器反应迟钝是因为cpu还是 硬盘io 到底是那个程序引起的。cpu负载多高算高
linux·服务器·shell
qq_282195313 小时前
内核spi驱动流程图
linux·流程图·driver
Gui林4 小时前
ros2 humble 控制真实机械臂(以lerobot为例)
linux·python
hi0_64 小时前
Linux 第六讲 --- 工具篇(一)yum/apt与vim
linux·服务器·c++·vim·yum
ronshi4 小时前
Linux 内核升级问题
linux·运维·服务器
事橙19995 小时前
Ubuntu18 登录界面死循环 Ubuntu进不了桌面
linux·ubuntu·lucene
Johny_Zhao6 小时前
Oracle、MySQL、SQL Server、PostgreSQL、Redis 五大数据库的区别
linux·redis·sql·mysql·信息安全·oracle·云计算·shell·yum源·系统运维
杜大哥6 小时前
Linux:如何查看Linux服务器的磁盘、CPU、内存信息?
linux·运维·服务器
mljy.7 小时前
Linux《进程概念(下)》
linux