Linux下C语言中的信号处理


=Start=

缘由:

在守护进程中需要实现根据收到的特定信号做特定操作的功能,所以在此学习整理一下Linux下信号处理的相关知识,方便以后查阅。

正文:

参考解答:

Linux下的信号处理有 signal() 和 sigaction() 函数。推荐使用符合 POSIX 标准的 sigaction() 函数。

signal() 函数的使用方法简单,但并不属于 POSIX 标准,在各类 UNIX 平台上的实现不尽相同,因此其用途受到了一定的限制。而 POSIX 标准定义的信号处理接口是 sigaction() 函数,其接口头文件及原型如下:

#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

◆ signum :要操作的信号。
◆ act :要设置的对信号的新处理方式。
◆ oldact :原来对信号的处理方式。
◆ 返回值 :0 表示成功,-1 表示有错误发生。

struct sigaction 类型用来描述对信号的处理,定义如下:

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

在这个结构体中:
成员 sa_handler 是一个函数指针,其含义与 signal 函数中的信号处理函数类似。
成员 sa_sigaction 则是另一个信号处理函数,它有三个参数,可以获得关于信号的更详细的信息。当 sa_flags 成员的值包含了 SA_SIGINFO 标志时,系统将使用 sa_sigaction 函数作为信号处理函数,否则使用 sa_handler 作为信号处理函数。在某些系统中,成员 sa_handler 与 sa_sigaction 被放在联合体中,因此使用时不要同时设置。
成员 sa_mask 用来指定在信号处理函数执行期间需要被屏蔽的信号,特别是当某个信号被处理时,它自身会被自动放入进程的信号掩码,因此在信号处理函数执行期间这个信号不会再度发生。
成员 sa_flags 用于指定信号处理的行为,它可以是一下值的“按位或”组合:
◆ SA_RESTART:使被信号打断的系统调用自动重新发起。
◆ SA_NOCLDSTOP:使父进程在它的子进程暂停或继续运行时不会收到 SIGCHLD 信号。
◆ SA_NOCLDWAIT:使父进程在它的子进程退出时不会收到 SIGCHLD 信号,这时子进程如果退出也不会成为僵尸进程。
◆ SA_NODEFER:使对信号的屏蔽无效,即在信号处理函数执行期间仍能发出这个信号。
◆ SA_RESETHAND:信号处理之后重新设置为默认的处理方式。
◆ SA_SIGINFO:使用 sa_sigaction 成员而不是 sa_handler 作为信号处理函数。
成员 re_restorer 则是一个已经废弃的数据域,不要使用。

下面以一个实际样例来说明sigaction()函数的使用:

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
static void sig_usr(int signum)
{
    if(signum == SIGUSR1)
    {
        printf("SIGUSR1 received\n");
    }
    else if(signum == SIGUSR2)
    {
        printf("SIGUSR2 received\n");
    }
    else
    {
        printf("signal %d received\n", signum);
    }
}
int main(void)
{
    char buf[512];
    int  n;
/*
    sigset_t mask;
    sigfillset(&mask); //将参数 mask 信号集初始化,然后把所有的信号加入到此信号集里,在这里表示屏蔽所有信号
    sigdelset(&mask, SIGUSR1); //删除set中的SIGUSR1信号,即——不屏蔽SIGUSR1信号
    sigdelset(&mask, SIGUSR2); //不屏蔽SIGUSR2信号
    sigprocmask(SIG_SETMASK, &mask, NULL); //参数SIG_SETMASK指定屏蔽mask中包含的信号集
*/
    struct sigaction sa_usr;
    sa_usr.sa_flags = 0;
    sa_usr.sa_handler = sig_usr;   //信号处理函数
    sigaction(SIGUSR1, &sa_usr, NULL);
    sigaction(SIGUSR2, &sa_usr, NULL);
    printf("My PID is %d\n", getpid());
    while(1)
    {
        if((n = read(STDIN_FILENO, buf, 511)) == -1)
        {
            if(errno == EINTR)
            {
                printf("read is interrupted by signal\n");
            }
        }
        else
        {
            buf[n] = '\0';
            printf("%d bytes read: %s\n", n, buf);
        }
    }
    return 0;
}

在这个例子中使用 sigaction 函数为 SIGUSR1 和 SIGUSR2 信号注册了处理函数,然后从标准输入读入字符。程序运行后首先输出自己的 PID,如:
My PID is 5904

这时如果从另外一个终端向进程发送 SIGUSR1 或 SIGUSR2 信号,用类似如下的命令:
kill -USR1 5904

则程序将继续输出如下内容:
SIGUSR1 received
read is interrupted by signal

这说明用 sigaction 注册信号处理函数时,不会自动重新发起被信号打断的系统调用。如果需要自动重新发起,则要设置 SA_RESTART 标志,比如在上述代码中可以进行类似一下的设置:
sa_usr.sa_flags = SA_RESTART;

参考链接:

Linux 进程学习(四)—— sigaction 函数 #简单明了
http://www.cnblogs.com/wblyuyang/archive/2012/11/13/2768923.html

Linux下的信号详解及捕捉信号 #全面
http://www.jb51.net/article/90695.htm

如何忽略(除了 SIGKILL 和 SIGSTOP 之外的)所有信号
https://stackoverflow.com/questions/10046916/is-it-possible-to-ignore-all-signals

linux系统编程之信号(五):信号集操作函数,信号阻塞与未决
http://www.cnblogs.com/mickole/p/3191281.html

关于Linux信号的一切(All about Linux signals) #全面细致
http://www.linuxprogrammingblog.com/all-about-linux-signals?page=show

Linux 信号处理
https://fixatom.com/deal-with-linux-signals/

http://man7.org/linux/man-pages/man2/sigaction.2.html
http://man7.org/linux/man-pages/man3/sigfillset.3.html
http://man7.org/linux/man-pages/man2/sigprocmask.2.html
https://stackoverflow.com/questions/27227585/what-is-sigaddset-used-for

linux c学习笔记—-信号(sigaction,sigaddset,sigprocmask)
http://lobert.iteye.com/blog/1739021

C语言信号处理函数
http://c.biancheng.net/cpp/u/hs9/

Unix signal handling example in C, SIGINT, SIGALRM, SIGHUP…
https://gist.githubusercontent.com/aspyct/3462238/raw/3498fff4351518d8d40592015a9d3b5b9c22f7d5/signal.c

Signal Handling in C
https://stackoverflow.com/questions/2485028/signal-handling-in-c

24 Signal Handling
https://www.gnu.org/software/libc/manual/html_node/Signal-Handling.html

Linux C实践(1):不可忽略或捕捉的信号—SIGSTOP和SIGKILL
http://blog.csdn.net/madpointer/article/details/13091705

Linux进程间通信(四)—信号通信之信号发送捕捉kill()、raise()、alarm()、pause()及其基础实验
http://www.cnblogs.com/dyllove98/archive/2013/06/12/3132798.html
http://blog.csdn.net/mybelief321/article/details/9078193

Can signal be ignored (lost)?
https://unix.stackexchange.com/questions/181433/can-signal-be-ignored-lost

=END=

, ,

《 “Linux下C语言中的信号处理” 》 有 2 条评论

  1. Linux使用KILL 0检测进程是否存在
    https://typecodes.com/cseries/kill0checkprocessifexist.html
    `
    sending the signal 0 to a given PID just checks if any process with the given PID is running and you have the permission to send a signal to it.

    kill函数中的形参sig是0的话,那么不会向pid进程发送任何信号,但是仍然会继续检测错误(进程ID或者进程组ID是否存在)。

    信号值大于0的时候发送对应信号给某个进程,等于0的时候表示检测某个进程是否存在。
    `
    https://stackoverflow.com/questions/11012527/what-does-kill-0-pid-in-a-shell-script-do
    https://serverfault.com/questions/436732/meaning-of-killall-0
    https://unix.stackexchange.com/questions/169898/what-does-kill-0-do
    https://unix.stackexchange.com/questions/17314/what-is-signal-0-in-a-trap-command

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注