handora
handora

Reputation: 659

Why my sig_int() function can't prevent my function from exit in c?

The codes is as below, and is the same as the one in book apue3e:

#include "apue.h"
#include "sys/wait.h"

static void sig_int(int);

int
main(int argc, char *argv[]) {
  pid_t pid;
  char buf[MAXLINE];
  int status;

  if (signal(SIGINT, sig_int) == SIG_ERR) {
    err_sys("signal error");
  }

  printf("%% ");
  while (fgets(buf, MAXLINE, stdin) != NULL) {
    if (buf[strlen(buf)-1] == '\n') {
      buf[strlen(buf)-1] = '\0';
    }

    if ((pid = fork()) < 0) {
      err_sys("fork error");
    } else if (pid == 0) {
      execlp(buf, buf, (char *)NULL);
      err_ret("couldn't execlvp: %s\n", buf);
      exit(127);
    }

    if ((pid = waitpid(pid, &status, 0)) < 0) {
      err_sys("waitpid_error");
    }
    printf("%% ");
  }
  exit(0);
}

static void
sig_int(int signo/* arguments */) {
  /* code */
  printf("Interrupted\n%%3 ");
}

So, my question is why this signal handler doesn't handle the SIGINT signal and exit immediately after pressing the Ctrl+c which i was testing on archlinux.

Upvotes: 3

Views: 1656

Answers (2)

Strings Ninety-nine
Strings Ninety-nine

Reputation: 1

I also encountered this problem when I was learning apue, I think it might be a problem with apue.h, because after I removed this header file and added the header files required by the program, the program could run normally, probably like so:

#include <sys/wait.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

#define MAXLINE 4096

static void sig_int(int);

int main(int argc, char* argv[])
{
    char buf[MAXLINE];
    pid_t pid;
    int status;
    if (signal(SIGINT, sig_int) == SIG_ERR) {
        //err_sys("signal error");
    }
    printf("%% ");
    while (fgets(buf, MAXLINE, stdin) != NULL) {
        if (buf[strlen(buf) - 1] == '\n') {
            buf[strlen(buf) - 1] = 0;
        }
        if ((pid = fork()) < 0) {
            //err_sys("fork error");
        }
        else if (pid == 0) {
            execlp(buf, buf, (char*)0);
            //err_ret("couldn't execute: %s", buf);
            exit(127);
        }
        if (pid = waitpid(pid, &status, 0) < 0) {
            //err_sys("waitpid error");
        }
        printf("%% ");
    }
    exit(0);
}

void sig_int(int signo)
{
    printf("interrupt\n%% ");
}

This program can roughly get the desired results

Upvotes: 0

Andrew Henle
Andrew Henle

Reputation: 1

[W]hy this signal handler doesn't handle the SIGINT signal and exit immediately after pressing the Ctrl+c which i was testing on archlinux.

Given

static void
sig_int(int signo/* arguments */) {
  /* code */
  printf("Interrupted\n%%3 ");
}

and

signal(SIGINT, sig_int)

Your process doesn't exit when you press CTRL-C for the simple reason your signal handler doesn't cause the process to exit.

You replaced the default SIGINT handler with your own, so the default action of exiting the process no longer happens.

Since you're running on Linux, I'll refer to the GNU glibc documentation on termination signals:

24.2.2 Termination Signals

These signals are all used to tell a process to terminate, in one way or another. They have different names because they’re used for slightly different purposes, and programs might want to handle them differently.

The reason for handling these signals is usually so your program can tidy up as appropriate before actually terminating. For example, you might want to save state information, delete temporary files, or restore the previous terminal modes. Such a handler should end by specifying the default action for the signal that happened and then reraising it; this will cause the program to terminate with that signal, as if it had not had a handler. (See Termination in Handler.)

The (obvious) default action for all of these signals is to cause the process to terminate.

...

Macro: int SIGINT

The SIGINT (“program interrupt”) signal is sent when the user types the INTR character (normally C-c).

The Termination in Handler glibc documentation states:

24.4.2 Handlers That Terminate the Process

Handler functions that terminate the program are typically used to cause orderly cleanup or recovery from program error signals and interactive interrupts.

The cleanest way for a handler to terminate the process is to raise the same signal that ran the handler in the first place. Here is how to do this:

volatile sig_atomic_t fatal_error_in_progress = 0;

void
fatal_error_signal (int sig)
{

  /* Since this handler is established for more than one kind of signal, 
     it might still get invoked recursively by delivery of some other kind
     of signal.  Use a static variable to keep track of that. */
  if (fatal_error_in_progress)
    raise (sig);
  fatal_error_in_progress = 1;


  /* Now do the clean up actions:
     - reset terminal modes
     - kill child processes
     - remove lock files */
  …


  /* Now reraise the signal.  We reactivate the signal’s
     default handling, which is to terminate the process.
     We could just call exit or abort,
     but reraising the signal sets the return status
     from the process correctly. */
  signal (sig, SIG_DFL);
  raise (sig);
}

Also, note that there can be significant differences between signal() and sigaction(). See What is the difference between sigaction and signal?

Finally, calling printf() from with a signal handler is undefined behavior. Only async-signal-safe functions can be safely called from within a signal handler. See POSIX 2.4 Signal Concepts for the gory details.

Upvotes: 3

Related Questions