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 사이의 차이인지?)
에 대해서 알 수가 없네요. 조언 주시면 감사하겠습니다.


댓글 달기