0

I am writing a program dealing with Linux signals. To be more specific, I want to re-install signal SIGINT in child process, only to find that it doesn't work.

Here is a simpler version of my code:

void handler(int sig){
    //do something
    exit(0);
}

void handler2(int sig){
    //do something
    exit(0);
}
int main(){

    signal(SIGINT, handler);

    if ((pid = fork()) == 0) {
        signal(SIGINT, handler2); // re-install signal SIGINT

        // do something that takes some time
        printf("In child process:\n");
        execve("foo", argv, environ); // foo is a executable in local dir

        exit(0);
    }else{
        int status;
        waitpid(pid, &status, 0); // block itself waiting for child procee to exit
    }

    return 0;
}

When shell is printing "In child process:", I press ctrl+c. I find that function handler is executed without problem, but handler2 is never executed.

Could you help me with this bug in my code?

Update: I want the child process to receive SIGINT signal during foo running process, is that possible?

Shuai Zhang
  • 2,011
  • 3
  • 22
  • 23
  • 1
    After you call execve() the child process no longer has any signal settings, they have all been reset to SIG_DFL - default. Is that what you are asking? – jim mcnamara Oct 07 '13 at 08:03
  • @jimmcnamara I didn't know signal settings have been reset to default. Thanks! – Shuai Zhang Oct 07 '13 at 09:13

2 Answers2

2

It is not a bug - calling execve has replaced the running binary image. The function handler2() (and any other function of your binary) is no longer mapped in the program memory having been replaced by the image of "foo" and therefore all signal settings are replaced to a default.

If you wish the signal handler to be active during "foo" run, you have to:

  1. make sure the handler function is mapped into the memory of foo
  2. a signal handler is registered after "foo" starts.

One way to do this is to create a shared library that contains the signal handler and an init function that is defined as a constructor that registers said signal handler and force it into the "foo" memory by manipulating the environment under which you execve foo (the environ variable) to include

LD_PRELOAD=/path/to/shared_library.so
Nik
  • 2,885
  • 2
  • 25
  • 25
gby
  • 14,900
  • 40
  • 57
  • The "shared library" solution seems complicated for me. Could you please be more specific with your suggestion "If you wish the signal handler to be active during "foo" run,..." ? – Shuai Zhang Oct 07 '13 at 09:22
  • execve replaces your program with program "foo". If you want to your code (signal handler) to run when a signal is sent to foo, you need to make it load your code. that is what the shared library does – gby Oct 07 '13 at 10:26
0

@gby's anwser has given comprehensive background knowlegde. I am here to give another solution without shared library.

Every time child process stops or terminates, parent process will receive SIGCHLD. You can handler this SIGCHLD signal to know if child process was terminated by SIGINT. In your handler:

pid_t pid = waitpid(pid_t pid,int * status,int options)

You can get status of child process through waitpid function.

if(WIFSIGNALED(status) && (pid == child_pid)){
    if(WTERMSIG(status) == SIGINT){
       // now you know your foo has received SIGINT.
       // do whatever you like.
    }
 }