signal driven I/O 예제가 배포본에 따라 다른 결과를 냅니다.

raymundo의 이미지

코드는 다음과 같습니다.

#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 사이의 차이인지?)

에 대해서 알 수가 없네요. 조언 주시면 감사하겠습니다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.