O_ASYNC stops generating SIGIO
This is a little long... For starters I'm on Linux 2.6.33, gcc 4.4.4.
I've written a small program, which creates a named pipe and reads it, until it sees a certain string, whereupon it gets rid of the FIFO, and re-executes itself.
#include<unistd.h>
#include<fcntl.h>
#include<signal.h>
#include<sys/types.h>
#include<sys/stat.h>
int fifo;
#define put(x) write(1, x, (sizeof x)-1)
void reader(int a)
{
char buf[26];
int n;
while((n=read(fifo, buf, 25))>0){
buf[25] = '\0';
if(!strncmp(buf, "moo", 3)){
put("exec()-ing\n");
close(fifo);
unlink("lefifo");
execl("/home/dave/a.out", "a.out", 0);
}
write(1, buf, n);
}
}
main()
{
signal(SIGIO, reader);
mknod("lefifo", 0600|S_IFIFO,0);
fifo = open("lefifo", O_RDONLY|O_NONBLOCK );
fcntl(fifo, F_SETOWN, getpid());
fcntl(fifo, F_SETFL, O_ASYNC);
for(;;)
pause();
}
When compiled, and run in the background, I can echo to lefifo
and it works as expected, until I enter a string beginning with "moo". The following example session:
$ gcc fifo.c
$ ./a.out&
$ echo klar > lefifo
klar
$ echo moo > lefifo
exec()-ing
$ echo klar2 > lefifo
$ echo where did you go > lefifo
$ echo moo > lefifo
$ pkill a.out
Generates this trace (some fat trimmed):
execve("./a.out", ["./a.out"], [/* 36 vars */]) = 0
mknod("lefifo", S_IFIFO|0600) = 0
open("lefifo", O_RDONLY|O_NONBLOCK) = 3
getpid() = 3945
fcntl(3, F_SETOWN, 3945) = 0
fcntl(3, F_SETFL, O_RDONLY|O_ASYNC) = 0
pause() = ? ERESTARTNOHAND (To be restarted)
--- SIGIO (I/O possible) @ 0 (0) ---
read(3, "klar\n"..., 25) = 5
write(1, "klar\n"..., 5) = 5
read(3, ""..., 25) = 0
sigreturn() = ? (mask now [])
pause() = ? ERESTARTNOHAND (To be restarted)
--- SI开发者_如何学PythonGIO (I/O possible) @ 0 (0) ---
read(3, "moo\n"..., 25) = 4
write(1, "exec()-ing\n"..., 13) = 13
close(3) = 0
unlink("lefifo") = 0
execve("/home/dave/a.out", ["a.out"], [/* 36 vars */]) = 0
mknod("lefifo", S_IFIFO|0600) = 0
open("lefifo", O_RDONLY|O_NONBLOCK) = 3
getpid() = 3945
fcntl(3, F_SETOWN, 3945) = 0
fcntl(3, F_SETFL, O_RDONLY|O_ASYNC) = 0
pause() = ? ERESTARTNOHAND (To be restarted)
--- SIGTERM (Terminated) @ 0 (0) ---
As you can see, the first time around, there is no trouble making the FIFO, and SIGIO is generated just fine; but after the exec()
the new FIFO won't generate any signals. The old one shows a successful close and seems to get deleted successfully.
I'm quite stumped as to why it might behave this way. Any ideas?
When you install a signal handler with signal()
, in the default configuration glibc will give BSD signal semantics: that signal will be blocked while the signal handler executes, and unblocked when it returns.
When you call exec()
from the signal handler, the signal handler doesn't return, so SIGIO
remains blocked. The process signal mask is inherited on exec()
, so it remains blocked in the new instance of the process.
Explicitly unblock SIGIO
using sigprocmask()
at the start of main()
and you should get the behaviour that you're after.
When a signal handler is called the signal is blocked till the signal handler is executed.Once the signal handler is completed, sigreturn() is called to unblock the signal. That you can see in your working case. But, when you type 'moo' you are invoking execl from the signal handler and execl() will not return back, so the signal handler doesn't return. If the signal handler doesn't return, sigreturn() will not be called and the signal will not be removed from the block list.
You can see the state of signals using cat /proc//status. You run your program and see /proc//status, there you can see sigio is pending and also it is blocked.
Visit www.rulingminds.com for linux kernel articles.
I would suggest that execl
(or any other processing) should be moved out of signal handler. You can set a flag in signal handler and poll on it in you main
.
精彩评论