简体   繁体   中英

Why is not blocking read() interrupted after receiving SIGINT?

having this code:

   #include <signal.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

#define BSIZE 16

static void sigalarm_handler(int signo)
{
    //cause SIGINT by returning
    return;
}

int main()
{
    sigset_t sigset;
    ssize_t nbytes;
    char buf[BSIZE];
     struct sigaction action;

    //initialize sigset and signal handler
    sigemptyset(&sigset);
    sigaddset(&sigset, SIGALRM);
     memset(&action, 0, sizeof(action));
     action.sa_handler = sigalarm_handler;
     sigaction(SIGALRM, &action, NULL);

    //infinite loop
    while (1)
    {
        alarm(2);
        printf("enter some input (3 chars)\n");

        sigprocmask(SIG_UNBLOCK, &sigset, NULL);
        //should SIGALRM should interupt read after 2 secs
        nbytes = read(fileno(stdin), buf, 3);
        sigprocmask(SIG_BLOCK, &sigset, NULL);

        if (nbytes < 0)
        {
            if (errno == EINTR)
            {
                break;
            }
        }

        buf[3] = '\0';

        //sleep should get SIGALRM, which in turn calls sigalarm_handler which generates SIGINT and interupts sleep, but the SIGALRM shouldn't be wasted, because it is prolong until it is unblock and thus next loop should break out
        sleep(2);
        printf("you wrote: %s\n", buf);
    }
}

I would expect to interupt read() after 2 secs. But the read blocks indeterminately. How's that? shouldn't SIGINT interrupt the read()?

signal() is not perfectly clear about whether a system call is interrupted or automatically restarted; it may vary from one platform to another. See the man page , in the "NOTES/Portability" section at the end; it is stated that on Linux signal() uses the BSD semantics, which is equivalent to using SA_RESTART in sigaction() for certain system-calls (see this page in the "Interruption of system calls and library functions by signal handlers" section).

sigaction() offers a better control.

You may replace signal(SIGALRM, sigalarm_handler); by

struct sigaction action;
memset(&action, 0, sizeof(action));
action.sa_handler=sigalarm_handler;
sigaction(SIGALRM, &action, NULL);

Putting a printf() right after the call to read() shows that this call is interrupted when using sigaction() but not when using signal() (at least on my system).

As stated in the comments of the question, the fact that the loop does not stop is essentially due to clumsiness in the algorithm: various attempts of if with uninitialised or irrelevant variables (the code in the question was changed several times, now with nbytes it's OK), testing the wrong errno ...

Here is an example trying to make explicit the situation after read() .

sigprocmask(SIG_UNBLOCK, &sigset, NULL);
nbytes = read(fileno(stdin), buf, 3);
int read_errno=errno; // save errno because the next call to
                      // sigprogmask() could overwrite it
sigprocmask(SIG_BLOCK, &sigset, NULL);
if(nbytes==0)
{
  printf("EOF reached (Control-d for example)\n");
  break;
}
if(nbytes<0) // read() failed
{
  if(read_errno==EINTR)
  {
    printf("interrupted by a signal while reading\n");
  }
  else
  {
    printf("something really bad happened: %s\n",
           strerror(read_errno));
  }
  break;
}
printf("%d bytes were read\n", (int)nbytes); // read() succeeded
buf[nbytes] = '\0';
printf("you wrote: <%s>\n", buf);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM