为什么我不能`tail -f / proc / $ pid / fd / 1`?


10

我写了一个简单的脚本echo-es其PID:

#/bin/bash

while true; do
    echo $$;
    sleep 0.5;
done

3844在一个终端上运行上述脚本(反复说),并尝试在另一终端上运行tail文件描述符:

$ tail -f /proc/3844/fd/1

它不会在屏幕上打印任何内容,并挂起直到^c。为什么?

另外,所有STD文件描述符(IN / OUT / ERR)都链接到相同的点:

$ ls -l /proc/3844/fd/
total 0
lrwx------ 1 mg mg 64 sie 29 13:42 0 -> /dev/pts/14
lrwx------ 1 mg mg 64 sie 29 13:42 1 -> /dev/pts/14
lrwx------ 1 mg mg 64 sie 29 13:42 2 -> /dev/pts/14
lr-x------ 1 mg mg 64 sie 29 13:42 254 -> /home/user/test.sh
lrwx------ 1 mg mg 64 sie 29 13:42 255 -> /dev/pts/14

这正常吗?

运行Ubuntu GNOME 14.04。

如果您认为此问题属于SO或SU而不是UL,请告知。


Answers:


13

做出stracetail -f,这说明了一切。有趣的部分:

13791 fstat(3, {st_mode=S_IFREG|0644, st_size=139, ...}) = 0
13791 fstatfs(3, {...}) = 0
13791 inotify_init()                    = 4
13791 inotify_add_watch(4, "/path/to/file", IN_MODIFY|IN_ATTRIB|IN_DELETE_SELF|IN_MOVE_SELF) = 1
13791 fstat(3, {st_mode=S_IFREG|0644, st_size=139, ...}) = 0
13791 read(4, 0xd981c0, 26)             = -1 EINTR (Interrupted system call)

它能做什么?它将inotify为该文件设置一个处理程序,然后等待直到该文件发生问题。如果内核tail通过该inotify处理程序说出文件已更改(通常是已附加),则tail1)寻求2)读取更改3)将它们写到屏幕上。

/proc/3844/fd/1在系统上是指向的符号链接/dev/pts/14,后者是字符设备。没有诸如“内存映射”之类的东西可以被访问。因此,没有什么可以将其更改签名给inotify的,因为那里没有可以访问的磁盘或内存区域。

这个字符设备是一个虚拟终端,实际上就像是一个网络套接字一样工作。在此虚拟终端上运行的程序正在连接到该设备(就像您远程登录到tcp端口一样),并写入要写入的内容。也有更复杂的事情,例如锁定屏幕,终端控制序列等,这些通常由ioctl()调用来处理。

我认为,您想以某种方式观看虚拟终端。可以在linux上完成,但不是那么简单,它需要一些类似于网络代理的功能,并且需要一些棘手的用法ioctl()。但是有些工具可以做到这一点。

目前,我不记得是哪个debian软件包提供了实现此目标的工具,但是通过稍加搜索,您可能会很容易找到它。

扩展名:如@Jajesh在这里提到的(如果您给我,请给他+1),该工具名为watch

扩展名2:提到@kelnos,简单cat /dev/pts/14就足够了。我试过了,是的,它奏效了,但是没有正确。我没有对此做很多实验,但是在我看来,进入该虚拟终端的输出要么去了cat命令,要么去了它的原始位置,而从没有去过。但是不确定。


peterh的答案tail是正确的(inotify监视位),但是他不正确,因为做您想要的事情实际上非常简单:只需使用cat而不是即可tail
kelnos 2014年

@kelnos谢谢,我将尝试尝试并用结果扩展答案。
彼得-恢复莫妮卡2014年

@kelnos cat也不适合我,它的挂起方式与tail相同,而我只能做的是to ctrl+c
cprn 2014年

1
我还是不明白。我更改echo $$echo $$ >> foo现在有一个文件,进程打开该文件,并每0.5秒追加一次。我仍然无法通过文件描述符访问它,并且/proc/$pid/fd/(中的254链接到test.sh脚本本身)中的所有文件描述符都链接到/dev/pts/14。它如何foo写入bash访问?
cprn 2014年

1
奇怪的是,它似乎仅在某些情况下有效。使用问题中的脚本,它不起作用。但是如果我在外壳中执行“ echo $$”操作,然后在另一个外壳中的那个pid上使用cat FD 1,则在第二个外壳中回显我键入的所有内容。
kelnos 2014年

4

中的文件/dev/pts不是常规文件,它们是虚拟终端的句柄。一个pts用于阅读和写作行为是不是对称的(也就是说,什么是写在那里以后可以读取它,像一个普通的文件或FIFO /管),但其创造的虚拟终端的过程介导:一些常见的有xterm或ssh或agetty或screen。控制过程通常会将按键分配给读取pts文件的过程,并在屏幕上呈现他们在上写入的内容pts

因此,tail -f /dev/pts/14将在您从中启动脚本的终端上打印您点击的键,如果您这样做echo meh > /dev/pts/14meh消息将出现在终端中。


您说对了,我可以写到pts设备,但似乎看不懂它。如下所示:tail -f /dev/pts/14不打印我在该终端上点击的键。不过,这是一个有趣的答案。谢谢。
cprn 2014年

0

前段时间我发现了一个有点变通方法,有时回答需要检查什么正在输出到标准输出,假设你有一个pid过程的,你可以裸眼中不友好的结果:

sudo strace -p $pid 2>&1 | grep write\(

-2

我想,为此,您需要做的就是观察输出,而不是拖尾。

$ watch -n2 ls -l /proc/3844/fd/

希望这是您所需要的。


3
该命令将每2秒显示一次打开的fds列表,而不是stdout上输出的内容
安赫尔

Ángel,是的。他可以将手表和猫一起使用,以查看要监视的描述符的结果。我猜@ peter-horvath为这个问题提供了完美的解释。
Jayesh 2014年

我知道watch。我想做的是偷看已经运行的进程的输出,所以watch无济于事。
cprn 2014年
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.