linux信号(signal)详解.md
程序员文章站
2022-07-12 11:17:49
...
linux信号(signal)详解
信号
(signal)可以看作是进程级别的中断。(这样说主要是为了区别内核级别的硬件中断
和软件中断
)。它允许进程和内核中断其他进程。信号的功能当然就是发送通知事件了。
我们可以使用man 7 signal
命令查看关于信号的详细信息。用命令kill -l
直接查看所有的信号的number。
在版本的慢慢迭代下,linux的信号分为了两种:
- 标准信号:信号值为
1~31
.如我们最常用的SIGINT
的信号值为2。标准信号大多数都有自己的含义。比如SIGBUS
表示总线错误,它由内核发送给进程。每个信号
都有自己默认处理动作。下面是几种默认的处理动作:-
Term
:终止一个进程。 -
Ign
:进程忽略此信号。 -
Core
:终止进程并转储内存.(转储内存(dumping core):指把代码和数据内存段的映像写到磁盘上.)。 -
Stop
:暂停(挂起)进程。 -
Cont
:继续运行一个当前被挂起的进程。
-
- 实时信号:信号值为
32~64
(准确来说的范围应该SIGRTMIN~SIGRTMAX,)。由于版本迭代、移植方便等的原因,实时信号值的表示应该用SIGRTMIN+n
(SIGRTMIN+n <= SIGRTMAX).和标准信号不同,系统没有指定每个实时信号具体的含义(默认动作为终止进程),一般由用户通过函数sigaction()
或signal()
设置自定义的处理动作。
标准信号与实时信号相比,标准信号不支持排队,所以信号可能会丢失。实时信号支持排队,信号不会丢失。什么是信号不排队?不排队信号为什么可能会丢失?简单说就像RTOS中的事件
,一个事件用一个位表示,当这个事件已经发生但未处理之前,如果再发生此事件,就只能丢弃了(其实信号发生了2次,系统只能记录一次)。在这点上大家可以把标准信号
和实时信号
比作RTOS中的事件
和信号量
.
信号处理过程
我们必须要清楚的是信号的处理函数是在用户空间处理的(因为它是在用户空间编写的函数)。下面是信号处理的过程。
- 先假设一个进程运行在用户态。此时由于调用了系统调用或者被中断而陷入了内核态。
- 内核态中处理完相关任务后在返回用户态前,会检查是否有需要处理的信号。
- 如果有需要处理的信号,且没有被阻塞。那么内核就在用户栈上创建一个层,然后跳入到用户态,执行信号的回调处理函数。
- 执行完处理函数后,会再次进入内核态。
- 在内核态中再次检测信号等工作。最后恢复原先程序的中断执行点,恢复到用户态(之前进程运行状态)。
信号处理过程图
在使用信号时,可以选择执行信号的默认动作。或者忽略此信号。或者设置信号的回调函数。
SIGKILL
和SIGSTOP
信号不能被捕获、阻止或忽略。
设置信号处理动作的函数如下:
-
sighandler_t signal(int signum, sighandler_t handler)
- 描述:设置信号的处理动作。
-
handler
为用户自定义的回调函数指针, - 当
handler = SIG_IGN
:设置为忽略此信号。 - 当
handler = SIG_DFL
:恢复为系统默认动作。
-
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact)
- 描述:设置信号的处理动作。
-
act
:指定新的信号处理方式。详见man手册。 -
oldact
如果不为NULL,将保存先前的信号信息到oldact
.
信号的触发
- 用
kill
命令发送信号- 描述:我们可以在shell使用此命令发送信号。
- eg:
kill -9 111
表示发送信号9(SIGKILL)给进程111。 - 负的pid表示向pid的进程组发送次信号。进程组中的每个进程都会收到此信号。如
kill -9 -111
.
- 使用键盘按建发送信号
-
ctrl+c
:发送信号SIGINT
给前台进程组中的每个进程。用来终止进程。 -
ctrl+z
发送信号SIGTSTP
给前台进程组中的每个进程。用来挂起进程。
-
- 用标准函数发送信号
-
int kill(pid_t pid, int sig)
:- 描述:向指定进程、指定进程组的所有成员或系统上的所有进程发送信号。
- 当
pid>0
:向pid进程发送信号sig. - 当
pid=0
:向调用进程所在组中的每个进程都发送信号sig. - 当
pid=-1
:将sig发送给调用进程有权发送信号的每个进程,进程1(init)除外。 - 当
pid<-1
:向|pid|
进程组的所有进程发送信号sig. -
sig
:要发送的信号。如果sig为0,则不发送信号,但仍然执行错误检查;这可用于检查进程ID或进程组ID是否存在。
-
int killpg(int pgrp,int sig)
:- 描述:向指定进程组的所有成员发送一个信号。
- 当
pgrp>1
:向指定进程组发送信号. - 当
pgrp=0
:向调用进程组发送信号。
-
raise()
: 向调用线程发送一个信号。单线程时相当于kill(getpid(),sig)
,多线程时相当于pthread_kill(pthread_self(),sig)
. -
int pthread_kill(pthread_t thread, int sig)
: 向指定线程发送一个信号。 -
int sigqueue(pid_t pid, int sig, const union sigval value)
: 向指定的进程发送实时信号和相关数据。 -
unsigned int alarm(unsigned int seconds)
- 描述:在
seconds
秒之后,向调用进程发送SIGALRM
信号。
- 描述:在
-
等待信号捕捉
下面的系统调用
将挂起调用进程或线程,直到捕获到信号(或未处理的信号终止进程).
-
int pause(void)
: 挂起执行,直到捕捉到任何信号。 -
int sigsuspend(const sigset_t *mask)
: 临时更改信号掩码并挂起执行,直到捕获到一个未掩码的信号。
下面是一个最简单信号使用例程
#include "stdlib.h"
#include "stdio.h"
#include "signal.h"
#include "fcntl.h"
#include "sys/types.h"
#include "unistd.h"
void signal_handler(int num)
{
printf("this is signal(%d) test.\n",num);
exit(0);
}
int main(void)
{
pid_t thisPid;
signal(SIGINT,signal_handler);
thisPid = getpid();
printf("pid %d\n",thisPid);
while (1);
return 0;
}
其他与信号相关知识
以后再补充这里面的知识吧。
- linux线程与信号
- 多线程共享所在进程的信号处理函数
- posix函数kill()/sigqueue()必须面向进程。而不是进程下某个特定的线程。
- 阻塞和解除阻塞信号
- 隐式阻塞机制
- 显式阻塞机制
1。 进程中的每个线程都有一个独立的信号掩码,它指示线程当前阻塞的信号集。线程可以使用pthread_sigmask()操作它的信号掩码。
- 同步接受信号
- 不是通过信号处理程序异步捕获信号.可以同步地接受信号,也就是说,在发送信号之前一直执行阻塞,此时内核将向调用者返回关于信号的信息。
- sigwaitinfo(),sigtimedwait(),sigwait() 这三个函数将挂起执行,直到传递指定集合中的一个信号。每个调用都返回关于所传递信号的信息。
前一直执行阻塞,此时内核将向调用者返回关于信号的信息。 - sigwaitinfo(),sigtimedwait(),sigwait() 这三个函数将挂起执行,直到传递指定集合中的一个信号。每个调用都返回关于所传递信号的信息。
- signalfd()返回一个文件描述符,该文件描述符可用于读取关于发送给调用者的信号的信息。每次从这个文件描述符中读取(2)都会阻塞,直到signalfd(2)调用中指定的集合中的一个信号被传递给调用者。read(2)返回的缓冲区包含一个描述信号的结构。
关于技术交流
此处后的文字已经和题目内容无关,可以不看。
qq群:825695030
微信公众号:嵌入式的日常
如果上面的文章对你有用,欢迎打赏、点赞、评论。
上一篇: PHP进程信号处理
下一篇: 信号的捕捉与模拟实现sleep函数