当 stderr 不引用终端设备时,为什么 fprintf(stderr, ....) 可以输出到屏幕?

当 stderr 不引用终端设备时,为什么 fprintf(stderr, ....) 可以输出到屏幕?

鉴于一个简单的程序:

/* ttyname.c */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

int main(int argc, char **argv)
{
  char **tty = NULL;

  tty = ttyname(fileno(stderr));
  if (tty == NULL)
    {
      fprintf(stderr, "%s\n", strerror(errno));
      exit(EXIT_FAILURE);
    }

  printf("%s\n", tty);
  
  exit(EXIT_SUCCESS);
}

将其编译为ttyname并调用为在里面,结果如下:

Inappropriate ioctl for device

这意味着错误代码是ENOTTY
为什么 fprintf(stderr, ....)stderr不涉及终端设备时可以输出到屏幕?

答案1

如果你像这样调用init不是将输出输出到屏幕;输出被发送到内核,内核将其打印到屏幕上。 init是一个特殊的过程

您可以将其视为类似于以下 shell 脚本:

$ x=$(ttyname 2>&1)
$ echo $x
Inappropriate ioctl for device

这是通过/dev/console设备完成的; init 进程的 stdin/stdout/stderr 由内核附加到此。对该设备的写入由内核处理并发送到当前控制台设备,该设备可能是当前的 vty 或串行端口或其他地方。

答案2

问题是ttyname当 stderr 不是终端时总是会失败。因此,如果 stderr 可能是启动期间的套接字,则 ttyname 会失败,但您可以毫无问题地写入 stderr,这就是 fprintf 工作的原因。

您可以通过执行 ttyname 的操作来获取套接字名称,该名称位于readlink/proc/self/fd/FD 上,其中“FD”通常为 2(对于 stderr)。

  char tty[1024];

  ssize_t size = readlink("/proc/self/fd/2", tty, sizeof(tty)-1);
  if (size < 0)
    {
      fprintf(stderr, "%s\n", strerror(errno));
      exit(EXIT_FAILURE);
    }

  tty[size] = 0;

  printf("%s\n", tty);

相关内容