제어 터미널에서 분리된 상태에서 출력을 어떻게 인쇄합니까?

제어 터미널에서 분리된 상태에서 출력을 어떻게 인쇄합니까?

분명한 대답은 터미널에서 분리하면 터미널에 출력을 인쇄할 수 없다는 것입니다. 문제는 실제로 분리한 터미널에 문자를 보낼 수 있고 해당 문자가 내 터미널에 나타난다는 것입니다.

이것은 실제로 많은 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]

문제는 다음과 같습니다마지막 세 줄이 실제로 인쇄되는 이유는 무엇입니까?

제어 터미널이 없어서 /dev/tty열 수 없습니다. 커널은 하위 프로세스의 출력을 xterm내가 열고 실행 중인 프로세스로 리디렉션해야 하는지 어떻게 결정합니까? 이런 일이 일어나야 할까요?

답변1

/dev/pts/0터미널에서 프로그램을 시작하면 세 가지 표준 파일 설명자(표준 입력, 출력 및 오류)는 기본적으로 터미널 라인(예:)을 가리킵니다. 이러한 설명자는 수정하지 않으므로 프로그램 전체에서 여전히 터미널을 참조합니다.

적절한 권한이 있으면 언제든지 터미널 회선으로 데이터를 보낼 수 있습니다. 예를 들어 두 개의 터미널 에뮬레이터를 열고 그 중 하나를 실행하면 tty인쇄한다고 가정해 보겠습니다 /dev/pts/0. 그런 다음 다른 것과 유사한 작업을 수행하면 echo foo > /dev/pts/0첫 번째 항목에 나타납니다.

이는 다음과 관련이 있습니다.제어단말기.

관련 정보