0%

How-to-Write-Advanced-Signal-Handlers-in-UNIX

使用 sigaction 来完成信号量触发的分析

sigaction

注册普通 signal handler 使用的是 sa_handler,此时参数只有一个 signo

使用 Flag SA_SIGINFO 需要使用 sa_sigaction,此时参数有三个

  • An int, for the signal number (just like signal)
  • A siginfo_t *, which is a structure containing all sorts of information about the source of the signal, including the pid of the sender if applicable. (It also includes some information about the cause of the signal for automatic signals like SIGSEGV.)
  • A ucontext_t *, which has to do with which thread got the signal. Mostly ignorable.
void (*sa_sigaction)(int, siginfo_t *, void *)

相关结构体

struct sigaction {
    void     (*sa_handler)(int);
    void     (*sa_sigaction)(int, siginfo_t *, void *);
    sigset_t   sa_mask;
    int        sa_flags;
    void     (*sa_restorer)(void);
};

siginfo_t {
    int      si_signo;    /* Signal number */
    int      si_errno;    /* An errno value */
    int      si_code;     /* Signal code */
    int      si_trapno;   /* Trap number that caused
                             hardware-generated signal
                             (unused on most architectures) */
    pid_t    si_pid;      /* Sending process ID */
    uid_t    si_uid;      /* Real user ID of sending process */
    int      si_status;   /* Exit value or signal */
    clock_t  si_utime;    /* User time consumed */
    clock_t  si_stime;    /* System time consumed */
    sigval_t si_value;    /* Signal value */
    int      si_int;      /* POSIX.1b signal */
    void    *si_ptr;      /* POSIX.1b signal */
    int      si_overrun;  /* Timer overrun count; POSIX.1b timers */
    int      si_timerid;  /* Timer ID; POSIX.1b timers */
    void    *si_addr;     /* Memory location which caused fault */
    int      si_band;     /* Band event */
    int      si_fd;       /* File descriptor */
}

注册

struct sigaction pipe_act;
pipe_act.sa_flags = SA_SIGINFO;
pipe_act.sa_sigaction = sigpipehandler
sigaction(SIGPIPE, &pipe_act, NULL);

Example of using sigaction

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>

static void hdl (int sig, siginfo_t *siginfo, void *context)
{
    printf ("Sending PID: %ld, UID: %ld\n",
            (long)siginfo->si_pid, (long)siginfo->si_uid);
}

int main (int argc, char *argv[])
{
    struct sigaction act;

    memset (&act, '\0', sizeof(act));

    /* Use the sa_sigaction field because the handles has two additional parameters */
    act.sa_sigaction = &hdl;

    /* The SA_SIGINFO flag tells sigaction() to use the sa_sigaction field, not sa_handler. */
    act.sa_flags = SA_SIGINFO;

    if (sigaction(SIGTERM, &act, NULL) < 0) {
        perror ("sigaction");
        return 1;
    }

    while (1)
        sleep (10);

    return 0;
}

Using Extended Information

The context information contains details about the machine state: the values of current registers, pointers to the stack, and so on. So it is possible to produce a rudimentary profiling infrastructure using the timers and the machine context information.

第三个参数为 ucontext_t *context = (ucontext_t*)vcontext;

Contexts may be used via the makecontext()/setcontext()/getcontext()/swapcontext() functions to implement user-space threads.

#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/ucontext.h>

struct itimerval timeout={0};

static void handle_prof_signal(int sig_no, siginfo_t* info, void *vcontext)
{
  char output[100];
  ucontext_t *context = (ucontext_t*)vcontext;
  unsigned long pc = context->uc_mcontext.gregs[REG_PC];

  snprintf(output,100,"Sample at %lx\n",pc);
  write(1,output,strlen(output)+1);
  setitimer(ITIMER_PROF, &timeout, 0);
}


void main()
{
  struct sigaction sig_action;
  memset(&sig_action, 0, sizeof(sig_action));
  sig_action.sa_sigaction = handle_prof_signal;
  sig_action.sa_flags = SA_RESTART | SA_SIGINFO;
  sigemptyset(&sig_action.sa_mask);
  sigaction(SIGPROF, &sig_action, 0);

  timeout.it_value.tv_sec=1;
  setitimer(ITIMER_PROF, &timeout, 0);
  volatile int i=0;
  do { i++; } while(1);
}

kernel support

主要的结构体为 struct sigframe

需要内核支持,例如在内核 2.6 中

i386 ,文件 arch/um/sys-i386/signal.c,函数 setup_signal_stack_si

   stack_top &= -8UL;
   frame = (struct rt_sigframe __user *) stack_top - 1;
   if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
       return 1;

   restorer = frame->retcode;
   if (ka->sa.sa_flags & SA_RESTORER)
       restorer = ka->sa.sa_restorer;

   /* See comment above about why this is here */
   PT_REGS_SP(regs) = (unsigned long) frame;

   err |= __put_user(restorer, &frame->pretcode);
   err |= __put_user(sig, &frame->sig);
   err |= __put_user(&frame->info, &frame->pinfo);
   err |= __put_user(&frame->uc, &frame->puc);
   err |= copy_siginfo_to_user(&frame->info, info);
   err |= copy_ucontext_to_user(&frame->uc, &frame->fpstate, mask,
                    save_sp);

arm ,文件 arch/arm/kernel/signal.c,函数 setup_sigframe

Ref

  1. How to find out the source of a POSIX signal
  2. Linux C: upon receiving a signal, is it possible to know the PID of the sender?
  3. Using the third parameter context of a sigaction handler with SIG_INFO results in a Segmentation Fault
  4. How to Write Advanced Signal Handlers in UNIX
  5. sigaction(7): semantics of siginfo_t’s si_code member