如何在 C 中可靠地找到与进程关联的终端名称?

C C*_*der 3 c terminal posix

请参阅以下代码。

#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;
}
Run Code Online (Sandbox Code Playgroud)

我按如下方式编译并运行它。

$ gcc foo.c
$ ./a.out 
ttyname(0): /dev/pts/3
ttyname(1): /dev/pts/3
ttyname(2): /dev/pts/3
ctermid(NULL): /dev/tty
Run Code Online (Sandbox Code Playgroud)

在另一个终端中,我运行ps命令来确认终端名称。

$ ps -ef | grep a.out | grep -v grep
coder     1498  1473  0 19:19 pts/3    00:00:00 ./a.out
Run Code Online (Sandbox Code Playgroud)

到目前为止一切都很好。我的程序正确打印终端。

但是,当 stdin、stdout 和 stderr 重定向时,我的程序无法输出终端信息,如下所示。

$ ./a.out < /dev/null > foo.txt 2> /dev/null
Run Code Online (Sandbox Code Playgroud)

在另一个终端中,我运行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
Run Code Online (Sandbox Code Playgroud)

./a.out退出后并检查其输出后foo.txt,我没有看到捕获的信息。

$  cat foo.txt 
ttyname(0): (null)
ttyname(1): (null)
ttyname(2): (null)
ctermid(NULL): /dev/tty
Run Code Online (Sandbox Code Playgroud)

由于ttyname()在重定向 stdin、stdout 和 stderr 时无法提供任何有意义的信息并且ctermid()始终返回/dev/tty,因此我该怎么做才能可靠地确定与进程关联的终端?我理想地寻找一种适用于任何标准 Unix 或 Linux 系统的解决方案,但如果不可能,那么特定于 Linux 的解决方案也可以。

Joh*_*ger 5

当 stdin、stdout 以及 stderr 重定向时,我的程序无法输出终端信息

是的,ttyname()返回在指定文件描述符上打开的终端设备的 (a) 名称。当文件描述符不引用终端时,则不存在这样的设备。 ttyname()记录NULL在这种情况下返回,以及指定文件描述符不与终端关联的任何其他情况。

我该怎么做才能可靠地确定与进程关联的终端?

您首先必须自己确定与进程关联的“终端”的含义。原则上,文件描述符 0、1 和 2 甚至更多都可能连接到不同的终端设备。可能它们都不是进程的控制终端,我认为这可能是您真正想要的。

如果一个进程想要访问其控制终端(假设它有一个),而不考虑其标准流的重定向,那么它应该打开由 返回的字符串命名的设备(ctermid()如果有)。glibcctermid()总是返回"/dev/tty"是无关紧要的——因为设备的命名通常是访问进程的控制终端的同义词。

然而,据我所知,没有可移植、可靠的方法来确定任何设备(包括控制终端)的独立于进程的名称。内核本质上并不按照设备名称工作——设备名称存在于文件系统中,是为了方便用户空间进程而提供的。此外,原则上,任何给定设备都可以有多个文件名引用它。

然而,在 Linux 上,您可以从 获取进程控制终端的主设备号和次设备号/proc/self/stat。原则上,您可以解压这些数字,或者

  • 将它们转换为相应的常规文件名,大概是在/proc/devices, 或

  • 扫描/dev文件系统以查找匹配的设备文件,并报告其名称。

前者成本较低,但有些投机性;后者如果成功,将返回存在的设备文件的名称,并且明确引用所需的设备。

您还可以在此处查看建议: https: //unix.stackexchange.com/questions/151812/get-device-node-by-major-minor-numbers-pair,但我发现至少其中一些没有在我的系统上工作。