I am trying to receive a signal when I/O is possible on a file descriptor. The program needs to be doing something else when it is not doing I/O, so using select(2) is not an option.
When I run the sample code is below, it is printing the message from inside the handler as fast as it can, even when there is no data on stdin. Even weirder is that the file descriptor reported in the siginfo_t structure varies from run to run. I only set it up for stdin (fd 0); why would the handler report any other value? Sometimes I see 0, sometimes, I see 1, most of the time I see '?', which indicates a value other than 0, 1, or 2.
This is on OpenSUSE 12.3, Linux kernel 3.7.10-1.16, but I see what appears to be the same problem on CentOS 6.4 with its stock kernel.
I am using write in the handler, because signal(7) says that it is reentrant and hence legal for use in a signal handler. This is also why I do not print the value of sinfo->si_fd; snprintf is not reentrant. For a while I was suspecting the stdio library of using SIGIO, which is why there are no stdio calls anywhere in the sample program (other than possibly in the library function err(3)).
Thank you for the time spent reading my code.
#include <fcntl.h>
#include <time.h>
#include <string.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <err.h>
#include <errno.h>
int needRead = 0;
const unsigned int bufsize = 256;
void handler(int sig, siginfo_t *sinfo, void *value)
{
char *cp;
cp = "in handler. fd: ";
write(2, cp, strlen(cp));
switch(sinfo->si_fd) {
case 0: cp = "0
"; break;
case 1: cp = "1
"; break;
case 2: cp = "2
"; break;
default: cp = "?
"; break;
}
write(2, cp, strlen(cp));
needRead = 1;
}
int main(int argc, char *argv[])
{
struct sigaction act;
unsigned int counter = 0;
int flags;
char *outp = ".";
/* set up the signal handler for SIGIO */
act.sa_sigaction = handler;
act.sa_flags = 0;
act.sa_flags = SA_RESTART;
sigemptyset(&act.sa_mask);
if (sigaction(SIGIO, &act, NULL) == -1)
err(1, "attempt to set up handler for SIGIO failed");
/* arrange to get the signal */
if (fcntl(0, F_SETOWN, getpid()) == -1)
err(1, "fnctl to set F_SETOWN failed");
flags = fcntl(0, F_GETFL);
if (flags >= 0 && fcntl(0, F_SETFL, flags | O_ASYNC ) == -1)
err(1, "fnctl F_SETFL to set O_ASYNC failed");
while (1) {
char in_buf[bufsize];
int nc;
counter++;
write(STDERR_FILENO, outp, strlen(outp));
if (needRead) {
needRead = 0;
if ((nc = read(STDIN_FILENO, in_buf, bufsize)) == -1) {
err(1, "read from stdin failed");
} else {
outp = "Read '";
write(STDERR_FILENO, outp, strlen(outp));
write(STDERR_FILENO, in_buf, nc);
outp = "'
";
write(STDERR_FILENO, outp, strlen(outp));
}
}
}
return 0;
}
See Question&Answers more detail:
os