Terminal

與控制終端分離時如何列印輸出?

  • May 15, 2017

顯而易見的答案是,如果我與終端分離,我將無法將輸出列印到終端。問題是我實際上可以將字元發送到我分離的終端,這些字元確實出現在我的終端中。

這確實是一個關於 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;
}

上面的程式碼所做的就是:

  1. 列印一些資訊;
  2. fork()確保setsid()作品從小就永遠不會成為流程組組長;
  3. setsid(),與終端分離;
  4. 等父母回來,孩子被收養init,以防萬一;
  5. 檢查我們是否無法打開/dev/tty
  6. 列印的東西,必鬚髮送到某個地方。

編譯並執行會產生以下輸出(注意混合提示,因為父返回並且 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我已經打開並正在執行的輸出?這應該發生嗎?

/dev/pts/0如果您從終端啟動程序,三個標准文件描述符(標準輸入、輸出和錯誤)預設指向終端行(例如)。您不修改這些描述符,因此它們在整個程序中仍然引用此終端。

如果您擁有適當的權限,您始終可以將數據發送到終端線路。例如,打開兩個終端仿真器,其中一個執行tty,假設它列印/dev/pts/0。然後從另一個執行類似的東西echo foo > /dev/pts/0,它會出現在第一個。

這與控制終端無關。

引用自:https://unix.stackexchange.com/questions/365255