如何在 C 中可靠地找到与进程关联的终端名称?
How to find the terminal name associated with a process reliably in C?
看下面的代码。
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("ttyname(0): %s\n", ttyname(0));
printf("ttyname(1): %s\n", ttyname(1));
printf("ttyname(2): %s\n", ttyname(2));
printf("ctermid(NULL): %s\n", ctermid(NULL));
/* Sleep for sometime so that we can manually run the ps command to
* see the terminal associated with the process. */
sleep(10);
return 0;
}
我编译 运行 如下。
$ gcc foo.c
$ ./a.out
ttyname(0): /dev/pts/3
ttyname(1): /dev/pts/3
ttyname(2): /dev/pts/3
ctermid(NULL): /dev/tty
在另一个终端,我运行ps
命令确认终端名称。
$ ps -ef | grep a.out | grep -v grep
coder 1498 1473 0 19:19 pts/3 00:00:00 ./a.out
到目前为止一切都很好。我的程序正确打印了终端。
然而,当stdin、stdout和stderr被如下重定向时,我的程序无法输出终端信息。
$ ./a.out < /dev/null > foo.txt 2> /dev/null
在另一个终端中,我 运行 ps
并且我可以看到确实 /dev/pts/3
与进程相关联。
$ ps -ef | grep a.out | grep -v grep
coder 1536 1473 0 19:22 pts/3 00:00:00 ./a.out
但是在 ./a.out
退出并且我在 foo.txt
中检查它的输出后,我没有看到捕获到此信息。
$ cat foo.txt
ttyname(0): (null)
ttyname(1): (null)
ttyname(2): (null)
ctermid(NULL): /dev/tty
由于 ttyname()
在重定向 stdin、stdout 和 stderr 时无法提供任何有意义的信息,并且由于 ctermid()
always returns /dev/tty
,我可以做些什么来可靠地确定与进程关联的终端?理想情况下,我正在寻找适用于任何标准 Unix 或 Linux 系统的解决方案,但如果不可能,那么 Linux 特定解决方案也可以。
my program fails to output the terminal information when stdin, stdout as well as stderr are redirected
是的,ttyname()
return 是在指定文件描述符上打开的终端设备的 (a) 名称。当文件描述符不引用终端时,就没有这样的设备。在这种情况下,ttyname()
被记录为 return NULL
,以及指定的文件描述符与终端无关的任何其他记录。
what can I do to reliably determine the terminal associated with a process?
您首先必须自己确定与进程关联的 "the" 终端的含义。原则上,文件描述符 0、1 和 2,甚至更多可能都连接到不同的终端设备。可能 none 是进程的 controlling 终端,我认为这可能是您真正要找的。
如果一个进程想要访问它的控制终端(假设它有一个),独立于它的标准流的重定向,那么它应该打开由字符串命名的设备return 由 ctermid()
编辑,如果有的话。 glibc ctermid()
always returns "/dev/tty"
是无关紧要的——因为设备通常被命名为访问进程的控制终端的同义词。
然而,据我所知,没有可移植的、可靠的方法来确定 任何 设备(包括控制终端)的独立于进程的名称。内核本身并不按照设备名称工作——它们存在于文件系统中,是为了方便用户空间进程而提供的。此外,原则上,任何给定的设备都可以有多个文件名引用它。
但是,在 Linux 上,您可以从 /proc/self/stat
获得进程控制终端的 主次设备号 。原则上,您可以将这些数字解包,然后
将它们转换为相应的常规文件名,大概是在/proc/devices
或
的帮助下
扫描 /dev
文件系统以查找匹配的设备文件,并报告其名称。
前者比较便宜,但有些投机;后者,如果成功,将 return 存在的设备文件的名称,并且明确指代所需的设备。
您也可以查看此处的建议:https://unix.stackexchange.com/questions/151812/get-device-node-by-major-minor-numbers-pair,但我发现至少其中一些在我的系统上不起作用。
看下面的代码。
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("ttyname(0): %s\n", ttyname(0));
printf("ttyname(1): %s\n", ttyname(1));
printf("ttyname(2): %s\n", ttyname(2));
printf("ctermid(NULL): %s\n", ctermid(NULL));
/* Sleep for sometime so that we can manually run the ps command to
* see the terminal associated with the process. */
sleep(10);
return 0;
}
我编译 运行 如下。
$ gcc foo.c
$ ./a.out
ttyname(0): /dev/pts/3
ttyname(1): /dev/pts/3
ttyname(2): /dev/pts/3
ctermid(NULL): /dev/tty
在另一个终端,我运行ps
命令确认终端名称。
$ ps -ef | grep a.out | grep -v grep
coder 1498 1473 0 19:19 pts/3 00:00:00 ./a.out
到目前为止一切都很好。我的程序正确打印了终端。
然而,当stdin、stdout和stderr被如下重定向时,我的程序无法输出终端信息。
$ ./a.out < /dev/null > foo.txt 2> /dev/null
在另一个终端中,我 运行 ps
并且我可以看到确实 /dev/pts/3
与进程相关联。
$ ps -ef | grep a.out | grep -v grep
coder 1536 1473 0 19:22 pts/3 00:00:00 ./a.out
但是在 ./a.out
退出并且我在 foo.txt
中检查它的输出后,我没有看到捕获到此信息。
$ cat foo.txt
ttyname(0): (null)
ttyname(1): (null)
ttyname(2): (null)
ctermid(NULL): /dev/tty
由于 ttyname()
在重定向 stdin、stdout 和 stderr 时无法提供任何有意义的信息,并且由于 ctermid()
always returns /dev/tty
,我可以做些什么来可靠地确定与进程关联的终端?理想情况下,我正在寻找适用于任何标准 Unix 或 Linux 系统的解决方案,但如果不可能,那么 Linux 特定解决方案也可以。
my program fails to output the terminal information when stdin, stdout as well as stderr are redirected
是的,ttyname()
return 是在指定文件描述符上打开的终端设备的 (a) 名称。当文件描述符不引用终端时,就没有这样的设备。在这种情况下,ttyname()
被记录为 return NULL
,以及指定的文件描述符与终端无关的任何其他记录。
what can I do to reliably determine the terminal associated with a process?
您首先必须自己确定与进程关联的 "the" 终端的含义。原则上,文件描述符 0、1 和 2,甚至更多可能都连接到不同的终端设备。可能 none 是进程的 controlling 终端,我认为这可能是您真正要找的。
如果一个进程想要访问它的控制终端(假设它有一个),独立于它的标准流的重定向,那么它应该打开由字符串命名的设备return 由 ctermid()
编辑,如果有的话。 glibc ctermid()
always returns "/dev/tty"
是无关紧要的——因为设备通常被命名为访问进程的控制终端的同义词。
然而,据我所知,没有可移植的、可靠的方法来确定 任何 设备(包括控制终端)的独立于进程的名称。内核本身并不按照设备名称工作——它们存在于文件系统中,是为了方便用户空间进程而提供的。此外,原则上,任何给定的设备都可以有多个文件名引用它。
但是,在 Linux 上,您可以从 /proc/self/stat
获得进程控制终端的 主次设备号 。原则上,您可以将这些数字解包,然后
将它们转换为相应的常规文件名,大概是在
/proc/devices
或 的帮助下
扫描
/dev
文件系统以查找匹配的设备文件,并报告其名称。
前者比较便宜,但有些投机;后者,如果成功,将 return 存在的设备文件的名称,并且明确指代所需的设备。
您也可以查看此处的建议:https://unix.stackexchange.com/questions/151812/get-device-node-by-major-minor-numbers-pair,但我发现至少其中一些在我的系统上不起作用。