STDIN을 비차단 모드로 설정하여 발생하는 문제

STDIN을 비차단 모드로 설정하여 발생하는 문제

특정 명령은 특정 터미널 창에서 지속적으로 실패하기 시작합니다.

$ sudo apt-get install ipython
...
After this operation, 3,826 kB of additional disk space will be used.
Do you want to continue? [Y/n] Abort.
$ 

$ kinit -f <username>
Password for <username>@<domain>: 
kinit: Pre-authentication failed: Cannot read password while getting initial credentials
$

$ passwd
Changing password for <username>.
(current) UNIX password: 
passwd: Authentication token manipulation error
passwd: password unchanged
$ 

$ crontab -e
Too many errors from stdincrontab: "/usr/bin/sensible-editor" exited with status 1
$ 

$ sudo docker run -it ubuntu bash
(hangs forever)

원인을 찾는 과정에서,스트레스디스플레이 프로그램이 STDIN에서 읽으려고 시도했지만 오류가 발생했습니다.

read(0, 0x7fffe1205cc7, 1) = -1 EAGAIN (Resource temporarily unavailable)

~에서독서(2)매뉴얼 페이지:

ERRORS
    EAGAIN The file descriptor fd refers to a file other than a socket and has been marked nonblocking (O_NONBLOCK), and the read would block.

물론 터미널 창의 STDIN은 비차단으로 표시됩니다(4로 표시됨).배너):

$ cat /proc/self/fdinfo/0 
pos:    0
flags:  0104002
mnt_id: 25

나는 내가 사용하고 있는 일부 프로그램이 STDIN을 비차단 모드로 설정한 다음 종료 시 다시 설정하지 않는다고 가정합니다(또는 가능하기 전에 종료됩니다).

저는 명령줄에서 이 문제를 해결하는 방법을 몰랐기 때문에 이를 수행하기 위해 다음 프로그램을 작성했습니다. (또한 STDIN을 비차단 모드로 변경하여 무엇이 잘못되었는지 확인할 수 있습니다.)

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int makeStdinNonblocking(int flags) {
    if (fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK) < 0) { 
        printf("Error calling fcntl in %s: %s\n", __FUNCTION__, strerror(errno));
        return EXIT_FAILURE;
    } 
    return EXIT_SUCCESS;
}
int makeStdinBlocking(int flags) {
    if (fcntl(STDIN_FILENO, F_SETFL, flags & ~(O_NONBLOCK)) < 0) { 
        printf("Error calling fcntl in %s: %s\n", __FUNCTION__, strerror(errno));
        return EXIT_FAILURE;
    } 
    return EXIT_SUCCESS;
}
int main(int argc, char *argv[]) {
    int flags;
    if (argc != 2) {
        goto usage;
    }
    if ((flags = fcntl(STDIN_FILENO, F_GETFL, 0)) < 0) {
        printf("Error calling fcntl in %s: %s\n", __FUNCTION__, strerror(errno));
        return EXIT_FAILURE;
    }
    if (0 == strncmp(argv[1], "nonblock", 9)) {
        return makeStdinNonblocking(flags);
    }
    else if ( 0 == strncmp(argv[1], "block", 6)) {
        return makeStdinBlocking(flags);
    }
usage:
    printf("Usage: %s <nonblock|block>\n", argv[0]);
    return EXIT_FAILURE;
}

어쨌든, 나는 알고 싶습니다:

  1. 표준 명령줄 유틸리티를 사용하여 STDIN을 비차단으로 만드는 방법이 있습니까?
  2. 쉘(내 경우에는 bash)이 명령 사이의 STDIN(및/또는 STDOUT/STDERR)에 대한 플래그를 자동으로 복원해야 합니까? 다른 프로그램에서 수행한 STDIN 변경 사항에 의존하는 명령에 대한 사용 사례가 있습니까?
  3. 프로그램이 시작될 때 STDIN이 차단 모드에 있을 것이라고 가정하고 모든 프로그램이 문제를 일으킬 경우 비차단 모드를 특별히 꺼야 한다고 가정하는 것은 실수입니까(위의 예 참조)?

참고로 저는 Ubuntu 17.10 및 GNU bash 버전 4.4.12(1)-release(x86_64-pc-linux-gnu)를 사용하고 있습니다.

고쳐 쓰다: Fedora의 이 문제는 bash 패치로 수정된 것 같습니다.

https://bugzilla.redhat.com/show_bug.cgi?id=1068697

그러나 이 수정 사항은 적어도 버전 4.4.18(1) 릴리스(2018년 1월부터)에서는 업스트림에 적용된 것으로 보이지 않습니다. 또한 bash 관리자는 bash가 이에 대해 실제로 책임을 지지 않는다고 언급했습니다.

https://lists.gnu.org/archive/html/bug-bash/2017-01/msg00043.html

애플리케이션이 STDIN의 원래 플래그를 변경하면 해당 플래그를 복원해야 하는 것처럼 들리므로 다음 절차를 사용하여 STDIN을 확인합니다.

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main() {
    int flags;
    if ((flags = fcntl(STDIN_FILENO, F_GETFL, 0)) < 0) {
        printf("Error calling fcntl in %s: %s\n", __FUNCTION__, strerror(errno));
    }
    if (0 != (flags & (O_NONBLOCK))) {
        printf("Warning, STDIN in nonblock mode\n");
    }
    return EXIT_SUCCESS;
}

나는 프로그램( gcc -o checkstdin checkstdin.c)을 컴파일한 다음 각 명령 후에 실행되도록 .bashrc에 다음을 넣었습니다.

PROMPT_COMMAND+="/path/to/checkstdin"

STDIN이 현재 비차단 모드에 있음을 감지하면 STDOUT에 경고를 인쇄합니다.

답변1

이런 일이 발생하면 명령줄에서 bash를 실행하고 종료합니다(첫 번째 bash로 돌아갑니다). 다시 작동해야 합니다. 다음은 몇 가지 흥미로운 세부정보입니다.https://stackoverflow.com/questions/19895185/bash-shell-read-error-0-resource-temporarily-unavailable.

답변2

해결 방법을 스크립팅해야 하는 경우 다음을 사용할 수 있습니다.

perl -MFcntl -e 'fcntl STDIN, F_SETFL, fcntl(STDIN, F_GETFL, 0) & ~O_NONBLOCK'

관련 정보