制御端末から切り離された状態で出力をどのように印刷しますか?

制御端末から切り離された状態で出力をどのように印刷しますか?

明らかな答えは、端末から切り離すと端末に出力を印刷できないことです。問題は、実際に分離した端末に文字を送信でき、その文字が自分の端末に表示されることです。

これは実際には多くのCコードが含まれていますが、Unixが端末制御を処理する方法に関する質問です。

とにかく制御端末は、/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. 何かを印刷するにはどこかに送る必要があります。

コンパイルして実行すると、次の出力が生成されます(親が返され、シェルがプロンプトを印刷するため、混合プロンプトに注意してください)。

[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]

問題は次のとおりです。最後の3行が実際に印刷されるのはなぜですか?

制御端末がないため/dev/tty開けません。カーネルは、子プロセスの出力をxterm私が開き、実行中のプロセスにリダイレクトする必要があるかどうかを決定します。このようなことが起こるべきですか?

答え1

/dev/pts/0ターミナルでプログラムを起動すると、3つの標準ファイル記述子(標準入力、出力、およびエラー)はデフォルトでターミナルライン(たとえば)を指します。これらの記述子は変更されないため、プログラム全体で端末を参照します。

適切な権限があれば、いつでも端末回線にデータを送信できます。たとえば、2つのターミナルエミュレータを開き、そのうちの1つを実行するとtty印刷するとします/dev/pts/0。その後、他のものと同様の操作を実行すると、echo foo > /dev/pts/0最初の項目に表示されます。

これは以下に関連しています。コントロール端末。

関連情報