signal driven I/O 예제가 배포본에 따라 다른 결과를 냅니다.
코드는 다음과 같습니다.
#include <unistd.h> #include <signal.h> #include <fcntl.h> #define BUFSZ 1024 int sigflg; main() { int n; int flags; char buf[BUFSZ]; sigset_t sigio_mask, empty_mask; void sigio_handler(); /* * Step 1: Set up signal handler for SIGIO */ signal(SIGIO, sigio_handler); /* * Step 2: Mark this process to receive the signals. */ if ( fcntl(STDIN_FILENO, F_SETOWN, getpid()) < 0 ) { perror("fcntl - F_SETOWN"); exit(1); } /* * Step 3: Enable asynchronous I/O on the socket. * The procedure involves adding the flags O_NONBLOCK and * O_ASYNC * to whatever flags are already set for the descriptor. * First retrieve the existing flags. */ flags = fcntl(STDIN_FILENO, F_GETFL); if ( flags == (-1) ) { perror("fcntl - F_GETFL"); exit(2); } /* * Now add the flags for asynchronous I/O and nonblocking I/O. * We should be careful here, since * FNOBIO is POSIX and generates the error EAGAIN * FNDELAY is BSD and generates the error EWOULDBLOCK * although both indicate nonbblocking I/O. */ flags |= ( O_ASYNC | O_NONBLOCK ); /* * Now set the flags, enabling asynchronous I/O. */ if ( fcntl(STDIN_FILENO, F_SETFL, flags) < 0 ) { perror("fcntl - F_SETFL"); exit(3); } /* * The main loop will copy everything from the socket descriptor to * standard output. */ for ( ; ; ) { /* * Set up the two signal masks; one for SIGIO and one as an * empty * mask. */ sigemptyset(&empty_mask); sigemptyset(&sigio_mask); sigaddset(&sigio_mask, SIGIO); /* * Mark SIGIO to be blocked, then wait for a signal. * Note that SIGIO will be unblocked while the process * is suspended * so its signal handler will be called. */ sigprocmask(SIG_BLOCK, &sigio_mask, NULL); printf("Before waiting for signal\n"); while ( sigflg == 0 ) { sigsuspend(&empty_mask); } printf("Signal has arrived\n"); /* * This means sigflg is non-zero, which can only happen if SIGIO * has come in. * Also, SIGIO is guaranteed blocked here, so * we can read safely. */ if ( (n = read(STDIN_FILENO, buf, BUFSZ)) > 0 ) { if ( write(1, buf, n) != n ) { perror("write"); exit(4); } } else if (n < 0) { perror("read"); exit(5); } else if ( n == 0) /* EOF */ exit(0); /* * Turn off the flag and prepare to go round again. */ sigflg = 0; sigprocmask(SIG_UNBLOCK, & sigio_mask, NULL); } } /* * Signal handler for SIGIO. * Just set the flag and return. */ void sigio_handler(int signo) { sigflg = 1; }
표준입력을 O_ASYNC, O_NONBLOCK 을 세팅하고,
while 문 안에서 시그널을 기다리고 있다가
표준입력이 들어오면 시그널 핸들러가 플래그를 바꿔주고,
while 문에서 나와서 read 로 읽은 후 출력한다..는 예제인데,
레드햇 & 데비안 우디(커널 2.4.18 )과 한컴리눅스(커널2.4.10)에서의 결과가 다릅니다.
레드햇과 데비안 우디에서는 다음과 같이 원하는 결과가 나옵니다.
$ ./a.out Before waiting for signal <-- 여기서 대기 hello <-- 입력 Signal has arrived hello Before waiting for signal <-- 여기서 대기 world <-- 입력 Signal has arrived world Before waiting for signal <-- 여기서 대기, ^D 입력 Signal has arrived $
그런데 한컴리눅스에서는
$ ./a.out Before waiting for signal Signal has arrived read: Resource temporarily unavailable $
위에서 보듯이 바로 시그널이 발생하고 while 을 빠져나오나,
read 에서 에러를 내어 버립니다. EAGAIN 에러인 것으로 봐서
읽을 게 없으니 그렇겠지요.
뒤에 에러는 당연하다치고, 도대체 왜 실행 직후에 SIGIO 가 생겨서
(발생한 것이 SIGIO 임은 따로 확인했습니다) 핸들러를 부르게 되는지
알 수가 없었습니다.
혹시 printf 로 인한 출력이 SIGIO 를 내어 버리는 것인가 싶어서,
while 들어가기 직전의 printf("Before..."); 부분을 주석처리 했습니다.
그랬더니 다음의 결과가 나옵니다.
$ ./a.out <-- 일단 블럭은 되나 hSignal has arrived <-- "hello" 를 입력하기 위해 h 를 누르는 순간 시그널 발생 Resource temporarily unavailable read: Resource temporarily unavailable $
따라서, 일단 저 printf 문이 시그널 발생의 원인이라 생각합니다.
printf 대신 write 를 써도 동일한 결과였습니다. (곧바로 시그널 발생)
그렇다면,
1. 어째서 fcntl 로 명시하지도 않은 "표준출력"에 대해 시그널을 받아들이는가
2. 어째서 배포본에 따라 차이가 나는가 (아니면 커널 2.4.18 과 20 사이의 차이인지?)
에 대해서 알 수가 없네요. 조언 주시면 감사하겠습니다.
댓글 달기