博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
如何在 Linux 上通过 C API 判断给定的 fd 的类型?
阅读量:6256 次
发布时间:2019-06-22

本文共 2594 字,大约阅读时间需要 8 分钟。

最近接到一个任务,需要判断传过来的 fd 是不是属于 eventfd/signalfd 这一类特定的 fd。因为这一类 fd 不支持某些操作,如果调用时不加判断,会报 Invalid Argument 错误。按理说,如果能把 fd 类型作为一个额外的参数传进来,就能轻松解决问题了。不过因为一些限制,拿到手时只有 fd 这一个整数。好在需要过滤的地方不在重要的路径上,所以 hack 一点的办法应该也能被接受。

既然 Linux 能够根据一个整数 fd 调用特定的操作,那么内核层面上一定会有一个 fd 到实际文件类型的映射。所以最好的打算,就是 Linux 把这个映射通过 syscall 的方式暴露出来了,然后我只要调用它,就能完成工作了。可惜找了一段时间,依然没有找到对应的 syscall。看来是不存在这样的好事了。

补充一个前提,我正在编写的是 C 代码。如果写的是脚本的话,至少有两种方法可以获取特定 fd 的类型。

一种是调用 lsof -p $pid -a -d $fd

¥ lsof -p 16166 -a -d 15COMMAND   PID USER   FD      TYPE             DEVICE SIZE/OFF       NODE NAMExxxxx   16166  lzx   15u  a_inode               0,13        0      10094 [eventfd]

我们可以看到 进程 16166 的第 15 号 fd 是一个 eventfd。

另一种是调用 cat /proc/$pid/fdinfo/$fd

¥ cat /proc/16166/fdinfo/16pos:    0flags:    02004002mnt_id:    13sigmask:    0000000000010000

该操作并不会直接显示 fd 的类型。但是因为不同类型的 fdinfo 格式不一样,你可以根据 man procfs 里面的说明,解析出具体 fd 的类型。比如 进程 16166 的第 16 号 fd 是一个 signalfd。

毫无疑问,对于 C 程序,上面两种方法都不太合适。虽说并不是无法实现,但是你让一个 C 程序每次判断 fd 类型时都要读完整个 fdinfo 文件,然后再通过一个 parser 解析出具体的 fd 类型,无论从性能上还是代码量上都不可接受。

也许存在某个 API,可以直接获取 fd 类型?为什么不看看神奇的 lsof 的源码呢?

抱着源码里可能隐藏着谜底的想法,我下载了 lsof 的代码。打开底下一个名为 linux 的文件夹,看了里面的文件几眼,我感觉被欺骗了。

没想到 lsof 你这个浓眉大眼的,居然也是用读 /proc 的方式来获取 fd 信息的!

看来这条路也断了。难道真的非得去解析 fdinfo 不可吗……

仔细看多几眼,lsof 貌似只是从 /proc/$pid/fd 里面读取了基本的信息。有些信息,比如我想要的 NAME,不是直接从那里面获取的。看来还是有希望的。我试着找找看,看看有什么关键的操作。

以下略去一些曲折的碰壁和反复的试错…… 反正我费了若干个小时,走了些弯路,终于明白汲汲以求的 NAME 是怎么得到的。其实就是 readlink /proc/$pid/fd/$fd!

对于 regular file,readlink /proc/$pid/fd/$fd 返回的是文件路径。而对于 eventfd/signalfd 这一类不可能有真正的 inode 的文件,返回的是 anon_inode:[eventfd]。于是我需要做的事情相对简单了:

先拼接 readlink 的文件名参数,然后调用 readlink,解析返回来的文件名。由于我只需要知道某个整数 fd 在本进程内的类型,可以用 /proc/self 代替 /proc/$pid。这么一来,我就只需要拼接后面的 $fd 部分。因为 fd 是一个整数,整个文件名 buffer 大小是有上限的,而且这个上限不大。所以可以把这个 buffer 弄成静态分配的,不用操心内存管理。接下来是 readlink 这个系统调用,原来最好的打算里面,也是逃不开一次系统调用的,所以这部分不算额外的开销。最后是对返回文件名的解析,凭借该文件名有着固定前缀的特性,我们可以先 strncmp 判断是否是 anon_inode:[,接着一路解析出方括号内具体的 fd 类型。也不是很难就是了。


后来看了相关的内核代码,估计应该不会有更好的方法了。

下面是 eventfd 创建代码:

/* fs/eventfd.c */    struct file *file;    ...    file = anon_inode_getfile("[eventfd]", &eventfd_fops, ctx,                  O_RDWR | (flags & EFD_SHARED_FCNTL_FLAGS));

你可以这么认为,anon_inode_get_file 返回了 file 的一个实例,而这个实例组合了 eventfd_fops 这个 eventfd 功能的具体实现。eventfd 之所以跟其他 fd 不同,全靠 eventfd_fops 里面定义的方法。如果内核要想提供一个 API,返回某个 fd 的类型,最好是每个 fd 都实现返回自己的类型方法。但是 eventfd_fops 这个结构体里面,没法返回 eventfd 标识的方法。有一个 show_fdinfo 的方法,不过这个方法是提供给 /proc/$pid/fdinfo 展示用的。另外 eventfd_fops 这个结构体是 static 的,所以也排除存在 if (file_operation == &eventfd_fops) return enum_eventfd; 这样的代码的可能性。

signalfd 的情况也差不多。


最后我没有采用 readlink 的方式,而是改由创建特殊 fd 的代码顺便把 fd 编号记录下来,接收到 fd 之后,我再去查找,看看是否是特殊的 fd。

转载地址:http://emnsa.baihongyu.com/

你可能感兴趣的文章
数据之路 Day8 Matplotlib包
查看>>
Ye.云狐J2刷机笔记 | 完美切换内部存储卡和SD卡的改法.vold.fstab
查看>>
【转】WIFI基本知识整理
查看>>
普通GRE 隧道配置
查看>>
Vim编程常用命令
查看>>
【树莓派】RASPBIAN镜像初始化配置
查看>>
在按钮上添加倒计时激活功能(转自手册网)
查看>>
java过滤敏感词汇
查看>>
类似LIS+贪心(ZOJ1025)
查看>>
[C++再学习系列] 虚函数的4条规则
查看>>
Thread.sleep
查看>>
浅析 ThreadLocal
查看>>
Pycharm批量操作代码快捷键
查看>>
oracle备份与恢复
查看>>
LLDB调试器
查看>>
cordova Ionic 和cordova的区别是什么
查看>>
【ZZ】C 语言中的指针和内存泄漏 & 编写高效的C程序与C代码优化
查看>>
linux暂停一个在运行中的进程【转】
查看>>
设计安全的账号系统
查看>>
SP2 PRIME1 - Prime Generator
查看>>