显而易见的答案是,如果我与终端分离,我将无法将输出打印到终端。问题是,我实际上可以将字符发送到我从中分离的终端,并且这些字符确实出现在我的终端中。
这实际上是一个关于unix如何处理控制终端的问题,尽管它确实包含大量的C代码。
无论如何,控制终端是/dev/tty
,我当然可以将输出打印到我的,xterm
如下所示:
[grochmal@haps term]$ echo yay > /dev/tty
yay
但如果我脱离那个终端,我就不能再这样做了。即如果/dev/tty
不存在,则因为当前进程没有控制终端。我采取这样的假设man 4 tty
:
TIOCNOTTY
Detach the calling process from its controlling terminal.
If the process is the session leader, then SIGHUP and SIGCONT signals are sent to the foreground process group and
all processes in the current session lose their controlling tty.
This ioctl(2) call works only on file descriptors connected to /dev/tty. It is used by daemon processes when they
are invoked by a user at a terminal. The process attempts to open /dev/tty. If the open succeeds, it detaches
itself from the terminal by using TIOCNOTTY, while if the open fails, it is obviously not attached to a terminal
and does not need to detach itself.
现在,要与我使用的终端分离,man 2 setsid
因为新会话将在没有控制终端的情况下启动。这是我正在使用的片段:
/* use latest but standard stuff */
#define _XOPEN_SOURCE 700
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
int
main (int argc, char **argv)
{
int chk;
char *def_term = "/dev/tty";
/* print info to the terminal */
printf("PID [%ld] PPID [%ld] GRPID [%ld] SESID [%ld]\n"
, (long) getpid(), (long) getppid()
, (long) getgid(), (long) getsid(0)
);
/* check terminal */
chk = open(def_term, O_RDONLY);
if (-1 != chk)
printf("We have %s\n", def_term);
else
printf("NO %s\n", def_term);
fflush(NULL); /* flush stdio buffers */
chk = fork();
switch(chk) {
case -1:
printf("BOOM!");
exit(1); /* exit flushing buffers */
break;
case 0:
/* ensure that the parent died, so we are adopted by init */
sleep(2);
chk = setsid();
if (-1 != chk)
printf("We got a new session.\n");
else
printf("Session failed! [%s]\n", strerror(errno));
/* use the *non-existent!* terminal */
chk = open(def_term, O_RDONLY);
if (-1 != chk)
printf("We have %s\n", def_term);
else
printf("NO %s\n", def_term);
printf("PID [%ld] PPID [%ld] GRPID [%ld] SESID [%ld]\n"
, (long) getpid(), (long) getppid()
, (long) getgid(), (long) getsid(0)
);
break;
default:
_exit(1); /* do not flush, we have children */
break;
}
return 0;
}
上面的代码所做的就是:
- 打印一些信息;
fork()
确保setsid()
从小工作起,永远不会成为流程组组长;setsid()
,与终端分离;- 等待父母回来,孩子被收养
init
,以防万一; - 检查我们是否无法打开
/dev/tty
; - 打印的东西,必须发送到某个地方。
编译并运行会产生以下输出(请注意混合提示,因为父级返回并且 shell 打印了提示。)
[grochmal@haps term]$ gcc -Wall -o detach detach.c
[grochmal@haps term]$ ./detach
PID [29943] PPID [679] GRPID [100] SESID [679]
We have /dev/tty
[grochmal@haps term]$ We got a new session.
NO /dev/tty
PID [29944] PPID [1] GRPID [100] SESID [29944]
问题是:为什么实际上打印了最后三行?
我没有控制终端,/dev/tty
无法打开。内核如何确定它应该将子进程的输出重定向到xterm
我已打开并正在运行的进程?这应该发生吗?
答案1
/dev/pts/0
如果您从终端启动程序,则三个标准文件描述符(标准输入、输出和错误)默认指向终端行(例如)。您不修改这些描述符,因此它们仍然在整个程序中引用该终端。
如果您有适当的权限,您始终可以将数据发送到终端线路。例如,打开两个终端模拟器,在其中一个执行tty
,假设它打印/dev/pts/0
。然后从另一个执行类似的操作echo foo > /dev/pts/0
,它将出现在第一个中。
这与控制终端。