当前位置: 首页 > Linux

如何在Linux上通过CAPI确定给定fd的类型?

时间:2023-04-06 19:10:11 Linux

最近接到一个任务,判断传入的fd是否属于eventfd/signalfd的具体fd。因为这类fd不支持某些操作,调用时如果不做判断,会报InvalidArgument错误。按理说,如果能将fd类型作为附加参数传入,问题就很容易解决了。但是由于一些限制,得到的时候只有整数fd。好在需要过滤的地方不在重要的路径上,所以黑客的做法应该也是可以接受的。由于Linux可以根据整数fd调用特定操作,因此在内核级别必须存在从fd到实际文件类型的映射。所以最好的方案是Linux通过syscall暴露这个映射,然后我只需要调用它就可以完成工作。遗憾的是,找了一段时间,还是没有找到对应的syscall。好像没有这种好事。作为附加前提,我正在编写的是C代码。如果您正在编写脚本,至少有两种方法可以获取特定fd的类型。一种是调用lsof-p$pid-a-d$fd:$lsof-p16166-a-d15COMMANDPIDUSERFDTYPEDEVICESIZE/OFFNODENAMexxxxx16166lzx15ua_inode0,13010094[eventfd]我们可以可以看出进程16166的15号fd是一个eventfd。另一种是调用cat/proc/$pid/fdinfo/$fd¥cat/proc/16166/fdinfo/16pos:0flags:02004002mnt_id:13sigmask:0000000000010000这个操作不直接显示fd的类型。但是因为不同类型的fdinfo有不同的格式,可以根据manprocfs中的说明来分析具体的fd类型。比如进程16166的16号fd就是一个signalfd。毫无疑问,对于C程序来说,以上两种方式都不适合。虽然也不是不可以,但是如果让一个C程序每次判断fd类型的时候都读取整个fdinfo文件,然后用解析器解析出具体的fd类型,无论从性能上还是代码量上都是不能接受的。也许有一些API可以直接获取fd类型?为什么不看看神奇的lsof的源代码呢?抱着源代码中可能隐藏着玄机的想法,我下载了lsof的代码。我在最下面打开了一个名为linux的文件夹,查看了里面的文件。我觉得被骗了。没想到lsof你这个大眼睛,居然通过读取/proc来获取fd信息!看来这条路也断了。真的有必要解析fdinfo吗……仔细看,lsof好像只是从/proc/$pid/fd中读取了基本信息。有些信息,比如我想要的NAME,并不是直接从那里获取的。看来还是有希望的。我试图环顾四周,看看有哪些关键操作。下面省略一些曲折的阻碍和反复的试错……反正我花了好几个小时,走了一些弯路,终于明白怎么得到我要找的NAME了。其实就是readlink/proc/$pid/fd/$fd!对于常规文件,readlink/proc/$pid/fd/$fd返回文件路径。而对于不能有真实inode的eventfd/signalfd,返回的是anon_inode:[eventfd]。所以我要做的比较简单:先拼接readlink的文件名参数,然后调用readlink解析返回的文件名。由于我在这个过程中只需要知道一个整数fd的类型,所以可以用/proc/self代替/proc/$pid。这样,我只需要拼接下面的$fd部分即可。因为fd是一个整数,所以整个文件名缓冲区的大小是有上限的,而且这个上限并不大。所以你可以让这个缓冲区静态分配而不用担心内存管理。接下来是readlink系统调用。在原来的最佳方案中,系统调用是没有逃脱的,所以这部分不是额外的开销。最后是对返回文件名的解析。有了文件名固定前缀的特性,我们可以先用strncmp判断是否是anon_inode:[,然后一路分析方括号中的具体fd类型。这也不难。看了相关的内核代码,估计应该没有更好的办法了。下面是eventfd创建代码:/*fs/eventfd.c*/structfile*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之所以与其他fds不同,是因为eventfd_fops中定义的方法。如果内核要提供返回某个fd类型的API,最好每个fd都实现一个返回自己类型的方法。但是在eventfd_fops结构体中,没有办法返回eventfd标识的方法。有一个show_fdinfo方法,但是这个方法是提供给/proc/$pid/fdinfo显示的。另外,eventfd_fops结构是静态的,所以可能出现类似if(file_operation==&eventfd_fops)returnenum_eventfd;这样的代码。也排除在外。signalfd的情况类似。最后我没有使用readlink方式,而是改了代码创建了一个特殊的fd,顺便记录下fd号。收到fd后,我会搜索看是否是特殊的fd。