Linux信号

Linux信号

信号是一种软件中断,它提供了一种处理异步事件的方法,也是进程间唯一的异步通信方式。在Linux系统中,根据POSIX标准扩展以后的信号机制,不仅可以用来通知某种程序发生了什么事件,还可以给进程传递数据。

一、信号的来源:

两种方式:硬件方式和软件方式

二、哪些情况会引发信号/p>

1.键盘事件 ctrl+c ctrl+l

2.非法内存 如果内存管理出错,系统就会发送一个信号进行处理

3.硬件故障

4.环境切换:比如从用户态切换到其他态,状态的改变也会发送一个信号,这个信号会告知给系统。

 

三.信号种类

kill -l  查看信号的种类

Linux信号

信号的值定义在signal.h中,在Linux中没有16和32这两个信号。

其中编号34以上的是实时信号(可靠信号),34以下的是普通信号(不可靠信号)

通过案例观察信号

out.sh

chmod u+x out.sh

./out.sh

另开一个终端

查找进程

ps -ef | head -n 1 ; ps -ef | grep “out.sh” | grep -v “grep”

进程ID是2774

kill给进程发信号,9(SIGKILLL)信号是杀死进程

kill -9 2774

此时终端1中会看到killed,进程被杀死了

 

linux中的这64个信号各自在什么条件下产生,默认的处理动作是什么,在signal(7)中都有详细说明,在命令行输入man 7 signal:

Linux信号

Linux信号

四、可靠信号和不可靠信号

信号的可靠性是指信号是否会丢失,或者说该信号是否支持排除。SIGHUP(1)~SIGSYS(31)之间的信号都是继承自UNIX系统是不可靠信号,Linux系统根据POSIX标准定义了SIGRTMIN(33) ~ SIGRTMAX(64)之间的信号,它们都是可靠信号,也称为实时信号。

五、信号的处理

知道了什么是信号,那么为什么有信号,信号是谁发送给谁的呢,怎么发送/p>

信号的发送者很多,比如终端驱动程序,进程,系统。而接收者大多是一个进程。

进程收到此信号,其可选的处理动作有以下三种:

  1. 忽略此信号(大多数信号可以使用这个方式来处理,但是有两种信号不能被忽略SIGKILL和SIGSTOP,因为他们向内核和超级用户提供了进程终止和停止的可靠方法,如果忽略了,那么这个进程就变成了没人能管理的进程,显然是内核设计者不希望看到的场景)
  2. 执行该信号的默认处理动作(终止该信号)对于每个信号来说,系统都对应默认的处理动作,当发生了该信号,系统会自动执行。不过,对系统来说,大部分处理方式都比较粗暴,就是直接杀死该进程。
  3. 提供一个信号处理函数(自定义动作),要求内核在处理该信号时切换到用户态执行这个处理函数,这种方式称为捕捉一个信号。(即就是需要告诉内核,用户希望如何处理某一种信号,说白了就是写一个信号处理函数,然后将这个函数告诉内核。当该信号产生时,由内核来调用用户自定义的函数,以此来实现某种信号的处理)

六.信号处理函数

1.signal()

函数原型:

#include<signal.h>

void (*signal(int signum,void(*handler))(int)))(int);

第一个参数指定信号的值,第二个参数指定针对前面信号值得处理,可以忽略该信号(参数设为SIG_IGN);课以采用系统默认方式处理信号(参数设为SIG_DFL);也可以自己实现处理方式(参数指定一个函数地址)

SIG_GIN:忽略信号

SIG_DFL:恢复信号的默认行为

如果signal调用成功,返回最后一次为安装信号signum而调用signal()时的handler值,失败则返回SIG_ERR.

2.sigaction()

#include<signal.h>

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

sigaction函数用于改变进程接收到特定信号后的行为。

sigaction函数的第一个参数为信号的值,可以为除SIGKILL及SIGSTOP外的任何一个特定有效的信号(为这两个信号定义自己的处理函数,将导致信号安装错误)第二个参数是指向结构sigaction的一个实例指针,在结构sigaction的实例中,指定了对特定信号的处理,可以为空,进程会以缺省的方式对信号处理;第三个参数oldact指向的对象用来保存原来对相应信号的处理,可指定oldact为NULL。如果把第二个第三个参数都设为NULL,那么该函数可用于检查信号的有效性。

3、发送信号函数

(1) int raise(int sig);  对当前进程发送指定信号

(2) int pause(void);  将进程挂起等待信号

(3) int kill(pid_t pid,int sig); 通过进程编号发送信号

 (4) unsigned int alarm(unsigned int seconds); 指定时间(秒)发送SIGALRM信号。 seconds 为0 时取消所有已设置的alarm请求;

 (5)int sigqueue(pid_t pid,int sig,const union sigval val);类似于kill函数,多了附带共用体 union sigval形数,将共用体中的成员 int sival_int 或 void *sival_ptr 的值传递给 信号处理函数中的定义类型 siginfo_t 中的 int si_int 或 void *si_ptr;

(6)int setitimer(int which,const struct itimerval *value,struct itimerval *oldvalue);  可定时发送信号,根据which可指定三种信号类型:SIGALRM、SIGVTALRM 和 SIGPROF;作用时间也因which值不同而不同;struct itimerval 的成员 it_interval定义间隔时间,it_value 为0时,使计时器失效;

(7) void abort(void) 将造成进程终止;除非捕获SIGABORT信号; 

4. 信号集及信号集操作

sigfillset(sigset_t *set); 设置所有的信号到set信号集中;

sigemptyset(sigset_t *set); 从set信号集中清空所有信号;

sigaddset(sigset_t *set,int sig);在set信号集中加入sig信号;

sigdelset(sigset_t *set,int sig);在set信号集中删除sig信号;

5、阻塞信号相关函数

 int sigprocmask(int how,const sigset_t *set,sigset_t *set);  根据how值,设置阻塞信号集,或释放阻塞的信号集

 int sigpending(sigset_t *set); 获取在阻塞中的所有信号;

 int sigsuspend(const sigset_t *set);    类似于 pause()函数!

七、信号处理函数的过程

(1)注册信号处理函数

 

信号的处理是由内核来代理的,首先程序通过sigal或sigaction函数为每个信号注册处理函数,而内核中维护一张信号向量表,对应信号处理机制。这样,在信号在进程中注销完毕之后,会调用相应的处理函数进行处理。

 

(2)信号的检测与响应时机

在系统调用或中断返回用户态的前夕,内核会检查未决信号集,进行相应的信号处理。

 

(3)处理过程:

程序运行在用户态时->进程由于系统调用或中断进入内核->转向用户态执行信号处理函数->信号处理函数完毕后进入内核->返回用户态继续执行程序

 

首先程序执行在用户态,在进程陷入内核并从内核返回的前夕,会去检查有没有信号没有被处理,如果有且没有被阻塞就会调用相应的信号处理程序去处理。首先,内核在用户栈上创建一个层,该层中将返回地址设置成信号处理函数的地址,这样,从内核返回用户态时,就会执行这个信号处理函数。当信号处理函数执行完,会再次进入内核,主要是检测有没有信号没有处理,以及恢复原先程序中断执行点,恢复内核栈等工作,这样,当从内核返回后便返回到原先程序执行的地方了。

参考图:

 

Linux信号

文章知识点与官方知识档案匹配,可进一步学习相关知识CS入门技能树Linux入门初识Linux24720 人正在系统学习中

来源:凤鸣九天_

声明:本站部分文章及图片转载于互联网,内容版权归原作者所有,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2019年7月6日
下一篇 2019年7月6日

相关推荐