Linux 信号处理

1: 信号

信号(signal)是一种软件中断,它提供了一种处理异步事件的方法,也是进程间惟一的异步通信方式。

2:信号名和编号
每个信号都有一个名字和编号,这些名字都以“SIG”开头。
信号定义在signal.h头文件中,信号名都定义为正整数。

3:查看linux所有的信号

命令: kill -l

Linux 信号处理_第1张图片

4:常见的信号解释

SIGINT:用户按下组合键时。默认动作为终止进程。

SIGKILL:无条件终止进程。

 SIGUSR1:用户定义的信号,即程序可以在程序中定义并使用该信号。默认动作为终止进程。

 SIGSEGV:指示进程进行了无效的内存访问(段错误)。默认动作为终止进程并使用该信号。

SIGUSR2:这是另外一个用户定义信号,程序员可以在程序中定义并使用该信号。默认动作为终止进程。

5:信号的处理
信号的处理有三种方法,分别是:忽略、捕捉和默认动作

  • 忽略信号:大多数信号可以使用这个方式来处理。
    • 有两种信号不能被忽略(分别是 SIGKILLSIGSTOP)。因为他们向内核和超级用户提供了进程终止和停止的可靠方法。
  • 捕捉信号:需要告诉内核,用户希望如何处理某一种信号,说白了就是写一个信号处理函数,当该信号产生时,调用用户自定义的函数,以此来实现某种信号的处理。
  • 系统默认动作:对于每个信号来说,系统都对应由默认的处理动作,当发生了该信号,系统会自动执行。

6:各种信号的默认处理情况

Linux 信号处理_第2张图片

7:常用的信号处理函数:       https://blog.csdn.net/FallingU/article/details/52751091

(1)signal

#include typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);

signal的第1个参数signum表示要捕捉的信号,第2个参数是个函数指针,表示要对该信号进行捕捉的函数。函数也可以是SIG_IGN(表示忽略掉该信号而不做任何处理,就会执行这个信号的默认处理动作)。signal如果调用成功,返回以前该信 号的处理函数的地址,否则返回SIG_ERR。

(2)sigaction (为了应对signal函数对于信号处理阻塞中遇到的问题)

#include int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

参数signum为需要捕捉的信号;参数 act是一个结构体,里面包含信号处理函数地址、处理方式等信息。参数oldact是一个传出参数,sigaction函数调用成功后,oldact里面包含以前对signum的处理方式的信息。如果函数调用成功,将返回0,否则返回-1

结构体 struct sigaction(注意名称与函数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和sa_sigaction只应该有一个生效,如果想采用老的信号处理机制,就应该让sa_handler指向正确的信号处 理函数;否则应该让sa_sigaction指向正确的信号处理函数,并且让字段sa_flags包含SA_SIGINFO选项。

8:针对sigset_t结构体,信号集操作函数

        int sigemptyset(sigset_t *set);      //清空信号集合set
        int sigfillset(sigset_t *set);      //将所有信号填充进set中
        int sigaddset(sigset_t *set, int signum);    //往set中添加信号signum
        int sigdelset(sigset_t *set, int signum);    //从set中移除信号signum
        int sigismember(const sigset_t *set, int signum); //判断signnum是不是包含在set中

9:当一个进程调动了 fork 函数,那么子进程会继承父进程的信号处理方式。

10: 信号的发送函数

kill 的函数原型

#include 
#include 
int kill(pid_t pid, int sig);

正如我之前所说的,信号的处理需要有接受者,显然发送者必须要知道发给谁,根据 kill 函数的远行可以看到,pid 就是接受者的 pid,sig 则是发送的信号的类型

11:linux多线程中的信号处理

在Linux的多线程中使用信号机制,与在进程中使用信号机制有着根本的区别,可以说是完全不同。在进程环境中,对信号的处理是,先注册信号处理函数,当信号异步发生时,调用处理函数来处理信号。它完全是异步的(我们完全不知到信号会在进程的那个执行点到来!)。

多线程中处理信号的原则却完全不同,它的基本原则是:将对信号的异步处理,转换成同步处理,也就是说用一个线程专门的来“同步等待”信号的到来,而其它的线程可以完全不被该信号中断/打断(interrupt)。这样就在相当程度上简化了在多线程环境中对信号的处理。而且可以保证其它的线程不受信号的影响。这样我们对信号就可以完全预测,因为它不再是异步的,而是同步的。

    一些关于线程级的信号函数使用见如下:              https://blog.csdn.net/sjin_1314/article/details/83050668

    (1): pthread_sigmask函数:

 每个线均有自己的信号屏蔽集(信号掩码),可以使用pthread_sigmask函数来屏蔽某个线程对某些信号的响应处理,仅留下需要 处理该信号的线程来处理指定的信号。实现方式是:利用线程信号屏蔽集的继承关系(在主进程中对sigmask进行设置后,主进程创建出来的线程将继承主进程的掩码)     

   (2)pthread_kill函数:
在多线程程序中,一个线程可以使用pthread_kill对同一个进程中指定的线程(包括自己)发送信号。注意在多线程中 一般不使用kill函数发送信号,因为kill是对进程发送信号,结果是:正在运行的线程会处理该信号,如果该线程没有注册信号处理函数,那么会导致整个进程退出

    (3): sigwait或者sigwaitinfo或者sigtimedwait等函数

sigwait 监听信号集set中所包含的信号,sigwait()函数暂停调用线程的执行,直到信号集中指定的信号之一被传递为止。在多线程代码中,总是使用sigwait或者sigwaitinfo或者sigtimedwait等函数来处理信号。而不是signal或者sigaction等函数。因为在一个线程中调用signal或者sigaction等函数会改变所有线程中的信号处理函数。而不是仅仅改变调用signal/sigaction的那个线程的信号处理函数。
注意:调用sigwait同步等待的信号必须在调用线程中被屏蔽,并且通常应该在所有的线程中被屏蔽(这样可以保证信号绝不会被送到除了调用sigwait的任何其它线程),这是通过利用信号掩码的继承关系来达到的。

  

   

        

你可能感兴趣的:(Linux,postgresql,c语言)