비제어 프로세스에 대한 "ioctl(fd, TIOCNOTTY)"는 "close(fd)"와 동일합니까?

비제어 프로세스에 대한 "ioctl(fd, TIOCNOTTY)"는 "close(fd)"와 동일합니까?

세션에 제어 터미널이 있다고 가정합니다. 세션 리더가 아닌 프로세스에 의해 호출된 경우에만 자체적으로 닫히는 것이 맞습니까 ioctl(fd, TIOCNOTTY)? fd와 같은가요 close(fd)?

감사해요.

답변1

파일 설명자를 닫는 것이 아니라 프로세스의 제어 터미널을 포기할 뿐입니다. 프로세스가 세션 리더가 아니기 때문에 아무 일도 일어나지 않습니다. 내 Linux 시스템의 매뉴얼 페이지에서는 이를 이렇게 설명합니다. 문서에 의존해야 하지만 구현을 테스트한다고 해서 모든 경우에 작동한다는 것이 입증되는 것은 아니기 때문에 잘못된 코드가 있어도 괜찮다면 시도해 보는 것은 매우 간단합니다.

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#define ERR(x) \
    do { \
    perror(x); \
    return; \
    } while (0)

void
main(void)
{
    /* create a new session */
    pid_t leader = setsid();

    if (leader == (pid_t) -1) {
        /* EPERM means we are already a process group leader */
        if (errno != EPERM)
            ERR("setsid");
    }

    int pid = fork();

    if (pid < 0)
        ERR("fork");

    if (pid > 0) {
        /* super-lazy clean-up and sync in one */
        wait(NULL);
        return;
    }

    pid_t parent = getppid();
    pid_t child = getpid();

    printf("Child PID: %lld\n", (long long) child);

    pid = fork();

    if (pid < 0)
        ERR("fork");

    if (pid > 0) {
        int fd = open(ctermid(NULL), O_RDWR);
        if (fd < 0)
            ERR("open");

        /* middle child gives up controlling terminal */
        if (ioctl(fd, TIOCNOTTY) < 0) {
            close(fd);
            ERR("ioctl");
        }

        close(fd);
        printf("Child gave up ctty, now waiting\n");

        /* super-lazy clean-up and sync in one */
        wait(NULL);
        return;
    }

    /* even lazier sync */
    sleep(1);

    pid_t grandchild = getpid();
    printf("Grandchild PID: %lld\n", (long long) grandchild);

    char cmd[256] = {0};
    snprintf(cmd, 256,
             "head -c 60 /proc/%lld/stat /proc/%lld/stat /proc/%lld/stat",
             (long long) parent, (long long) child, (long long) grandchild);

    system(cmd);

    /* the output of the command will not end with newline */
    printf("\n");
}

이를 실행하면 다음이 표시됩니다.

Child PID: 293750
Child gave up ctty, now waiting
Grandchild PID: 293751
==> /proc/293749/stat <==
293749 (test) S 290544 293749 290544 34822 293749 4210688 10
==> /proc/293750/stat <==
293750 (test) S 293749 293749 290544 0 -1 4210752 40 0 0 0 0
==> /proc/293751/stat <==
293751 (test) S 293750 293749 290544 34822 293749 4210752 32

출력을 보면 일곱 번째 필드는 매뉴얼 페이지 tty_nr에 있으며 proc(5)제어 터미널을 포기하는 하위 프로세스만 해당 터미널을 잃으며( tty_nr = 0) 다른 프로세스는 잃음을 보여줍니다.

즉, 연구/학습 목적 이외의 다른 용도로는 사용해서는 안 된다고 생각합니다. 일반적으로 setsid(2)자녀를 생성하기 전에 호출하고, 원하는 경우 A(새) 터미널을 O_NOCTTY할당 하지 않고 켜십시오. 터미널을 제어합니다.

관련 정보