找出哪些文件描述符共享相同的“打开文件描述”


17

如果我这样做(在类似Bourne的外壳中):

exec 3> file 4>&3 5> file 6>> file

由于3是dup()ed的4,所以文件描述符3和4 共享相同的打开文件描述(相同的属性,文件中的相同偏移量...)。虽然该过程的文件描述符5和6在不同的打开文件描述中(例如,它们各自在文件中都有自己的指针)。

现在,在lsof输出中,我们看到的是:

zsh     21519 stephane    3w   REG  254,2        0 10505865 /home/stephane/file
zsh     21519 stephane    4w   REG  254,2        0 10505865 /home/stephane/file
zsh     21519 stephane    5w   REG  254,2        0 10505865 /home/stephane/file
zsh     21519 stephane    6w   REG  254,2        0 10505865 /home/stephane/file

更好lsof +fg

zsh     21519 stephane    3w   REG          W,LG  254,2        0 10505865 /home/stephane/file
zsh     21519 stephane    4w   REG          W,LG  254,2        0 10505865 /home/stephane/file
zsh     21519 stephane    5w   REG          W,LG  254,2        0 10505865 /home/stephane/file
zsh     21519 stephane    6w   REG       W,AP,LG  254,2        0 10505865 /home/stephane/file

(在Linux 3.16上),因为我们看到fd 6具有不同的标志,因此它必须是与fd 3、4或5上的打开文件描述不同的打开文件描述,但是由此我们不能看出fd 5在a上。不同的打开文件描述。使用-o,我们还可以看到偏移量,但是同样的偏移量并不能保证它是相同的打开文件说明

有没有非侵入式的1种方法可以找出答案?外部还是进程自己的文件描述符?


1。一种启发式方法可能是更改一个fd的标志,fcntl()并查看结果是否更新了其他文件描述符的标志,但这显然不是理想的方法,也不是简单的证明


这种方法原则上应该有效,并且在大多数情况下不会造成太大破坏:首先派生一个孩子(如果从外部进行操作,请使用ptrace)。然后,在子进程中,对文件描述符执行不影响其他进程的操作。在Linux上,租约应适用于此。
吉尔(Gilles)'所以

@吉尔斯,谢谢,但这差不多是我在问题中建议的方法。租约(假设您的意思是F_SETLEASE fcntl,感谢您让我意识到BTW)仅对您拥有的常规文件有效,并且如果在同一文件中有另一个“写入” 打开文件描述(EBUSY),则该租约无效,并且并非完全不一样-侵入性。
斯特凡Chazelas

你放弃这个问题了吗?我发布了一些有关SystemTap如何执行您想要的操作的信息,但是您尚未将任何答案标记为完成...?
Azhrei 2015年

Answers:


2

对于Linux 3.5及更高版本,可以使用kcmp(3)完成此操作:

KCMP_FILE

  • 检查文件描述符是否IDX1在过程PID1是指相同的开放文件描述(参见开放(2) )作为文件描述符IDX2在过程PID2dup(2)(和类似的结果)fork(2)或通过域套接字传递文件描述符(参见unix(7))的结果可能导致存在两个引用相同打开文件描述的文件描述符。

手册页提供了一个特定于OP要求的用例的示例。请注意,此系统调用要求使用CONFIG_CHECKPOINT_RESTOREset 编译内核。


谢谢。正是我想要的。需要注意的是,除非你是超级用户,它必须是你的两个过程(而不是为setuid / setgid的...)(理解)
斯特凡Chazelas

@StéphaneChazelas正是。如果出于某种原因,您的内核中没有内置CPIU支持,并且您不想重建它,那么我想您总是可以编写一个内核模块,该模块可以导出一些用户界面,让您比较struct file *指针。
minmaxavg

3

您要比较的是struct file文件描述符指向的指针。(在内核内部task_struct,每个线程都有一个数据结构。它包含一个指向另一个结构的指针,称为files_struct.。结构包含一个指针数组,每个指针都指向一个struct file。它是struct file用于保持搜寻偏移量,打开标志和一个其他几个领域。)

files_struct除了使用某些侵入性工具之外,我不知道有任何其他可见的方式来查看指针。例如,可以给SystemTap一个PID,它可以找到对应的task_struct并跟随指针。但是,如果您正在寻找被动,我想就是这样。戴尔很久以前发布了一个名为KME(内核内存编辑器)的工具,该工具为实时内核内存提供了类似于电子表格的界面,它可以执行您想要的操作,但从未移植到64位。(我尝试过但从未使其完全起作用,并且不确定为什么。)

您发现没有lsof帮助的原因之一是,它也没有看到那些指针(但是请查看+f非Linux系统的选项)。从理论上讲,您可以比较中的所有字段,struct file并认为两个结构相同,但是它们仍然可以来自不同的open(2)调用。

看一下pfiles SystemTap脚本中的想法。如果您修改了它以打印的地址struct file,那么您将拥有解决方案。您还可以检查opens_file_by_pid.stp,因为其中有一个遍历的函数files_struct,即。文件描述符表,查看struct file对象...

我可能会问你要完成什么?


我不得不承认,我不记得我当时需要的那种情况。毫无疑问,某些调试或取证任务。
斯特凡Chazelas

我期待着到PoC SystemTap的代码:-)
斯特凡Chazelas

在发布问题之前,我确实看过systemtap或/ proc / kcore方法。困难的部分是获取每个任务的每个fd的信息。我发现的最挑剔的方法是挂接到生成/ proc / * / task / fd目录内容的函数中,但是我能想到的唯一可行的方法是挂接到源文件中的特定行号上,而不是从一个内核版本移植到另一个内核版本。您实际上无法在systemtap中循环浏览任务列表。也许可以通过/ proc / kcore来实现,但是需要付出过多的努力并且可能不可靠。
斯特凡Chazelas

感谢您迄今为止的最佳答复。我看看你的指针。
斯特凡Chazelas

你当然可以!设置一个probe begin块,并使其for_each_process在脚本中嵌入的C代码块中使用宏(您需要使用“大师”模式SystemTap嵌入C代码)。实际上,为使这个有趣(!),您可以使用SystemTap的关联数组之一;使用files_struct地址作为键,使用PID / TID列表作为值。现在,您有了每个打开的文件的列表,以及正在共享的任务(可以在父/子之间共享)。如果您想讨论SystemTap,请再次答复。
Azhrei

0

这是特定于Linux的解决方案:/ proc / self / fd是当前进程中打开文件句柄的符号链接目录。您可以只比较链接值。使用子进程时,它变得更加复杂,因为子进程将具有不同的/ proc / self,因为它是与pid相关的符号链接。您可以通过使用/ proc / $$ / fd解决此问题,其中$$是所需的pid。


谢谢。但这不是我要的。在Linux上,lsof确实确实使用/ proc / pid / fd来获取每个文件描述符的路径,并使用/ proc / pid / fdinfo来获取标志。但是我想要的是对于两个指向相同文件的fds,无论它们指向相同的打开文件描述,还是两个文件描述符都已独立打开。
斯特凡Chazelas

好的,找到对相同文件名可打开的文件描述符对之后,对两者进行区分并比较结果(如果它们不同则分开)。如果它们在一个文件描述符上是相同的,然后重复;如果它们仍然匹配,则它们是相同的。
hildred

好吧,这是我在问题中提到的启发式方法的一种更侵入性的变体,并且仅适用于常规文件(不适用于套接字,设备(如终端),管道...)。
斯特凡Chazelas
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.