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)
--- SIGIO (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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With