ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

4.进程信号量

2021-04-18 17:00:37  阅读:309  来源: 互联网

标签:函数 signal 信号量 kill 内核 信号 进程


linux中的信号

  1. 用户在Linux命令行输入命令,在Shell下启动一个前台进程。
  2. 用户按下Ctrl-C, 产生一个硬件中断,被OS获取,解释成信号,发送给目标前台进程
  3. 前台进程因为收到信号,进而引起进程退出

Ctrl-C特点

  1. Ctrl-C 产生的信号只能发给前台进程。一个命令后面加个& 可以放到后台运行,这样Shell不必等待进程结束就可以接受新的命令,启动新的进程。
  2. Shell可以同时运行一个前台进程和任意多个后台进程,只有前台进程才能接到像Ctrl-C 这种控制键产生的信号。
  3. 前台进程在运行过程中用户随时可能按下Ctrl-C而产生一个信号,也就是说该进程的用户空间代码执行到任何地方都有可能收到SIGINT信号而终止,所以信号相对于进程的控制流程来说是异步(Asynchronous)的。

信号概念

信号是进程之间事件异步通知的一种方式,属于软中断

信号本质

软中断信号(signal,又简称为信号)用来通知进程发生了异步事件。在软件层次上是对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是进程间通信机制中唯一的异步通信机制,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。进程之间可以互相通过系统调用kill发送软中断信号。内核也可以因为内部事件而给进程发送信号,通知进程发生了某个事件。信号机制除了基本通知功能外,还可以传递附加信息。

查看系统定义的信号量
kill -l
  • 每个信号都有一个编号和一个宏定义名称,这些宏定义可以在signal.h中找到,宏对应的值就是编号的值
  • 编号34以上的是实时信号
查看信号各自产生条件,默认的处理动作
man 7 signal

信号处理常见方式

  1. 忽略此信号。
  2. 执行该信号的默认处理动作。
  3. 提供一个信号处理函数,要求内核在处理该信号时切换到用户态执行这个处理函数,这种方式称为捕捉(Catch)一个信号。

linux中的信号的两种分类方式

可靠信号与不可靠信号

不可靠信号

Linux信号机制基本上是从Unix系统中继承过来的。早期Unix系统中的信号机制比较简单和原始,信号值小于SIGRTMIN(34)的信号都是不可靠信号。这就是"不可靠信号"的来源。它的主要问题是信号可能丢失。

可靠信号
产生

有必要对信号的原始机制加以改进和扩充。由于原来定义的信号已有许多应用,不好再做改动,又新增加了一些信号,并在一开始就把它们定义为可靠信号,这些信号支持排队,不会丢失。

兼容性

信号值位于SIGRTMIN(34)和SIGRTMAX(64)之间的信号都是可靠信号

Linux在支持新版本的信号安装函数sigation()以及信号发送函数sigqueue()的同时,仍然支持早期的signal()信号安装函数,支持信号发送函数kill()。

信号的可靠与不可靠只与信号值有关,与信号的发送及安装函数无关。目前linux中的signal()是通过sigation()函数实现的,因此,即使通过signal()安装的信号,在信号处理函数的结尾也不必再调用一次信号安装函数。同时,由signal()安装的实时信号支持排队,同样不会丢失。

对于目前linux的两个信号安装函数:signal()及sigaction()来说,它们都不能把SIGRTMIN以前的信号变成可靠信号(都不支持排队,仍有可能丢失,仍然是不可靠信号),而且对SIGRTMIN以后的信号都支持排队。这两个函数的最大区别在于,经过sigaction安装的信号都能传递信息给信号处理函数,而经过signal安装的信号不能向信号处理函数传递信息。对于信号发送函数来说也是一样的。

实时信号与非实时信号

早期Unix系统只定义了32种信号,Ret hat7.2支持64种信号,编号0-63(SIGRTMIN=31,SIGRTMAX=63),将来可能进一步增加,这需要得到内核的支持。前32种信号已经有了预定义值,每个信号有了确定的用途及含义,并且每种信号都有各自的缺省动作。如按键盘的CTRL ^C时,会产生SIGINT信号,对该信号的默认反应就是进程终止。后32个信号表示实时信号,等同于前面阐述的可靠信号。这保证了发送的多个实时信号都被接收。

非实时信号都不支持排队,都是不可靠信号;实时信号都支持排队,都是可靠信号。

产生信号

1.通过终端按键产生信号(硬件)

SIGINT的默认处理动作是终止进程,SIGQUIT的默认处理动作是终止进程并且Core Dump

Ctrl + c --> 2)SIGINT(终止/中断) “INT”—Interrupt
Ctrl + z --> 20)SIGTSTP(暂停/停止) “T”—Terminal 终端
Ctrl + \ --> 3)SIGQUIT(退出)

2.硬件异常产生信号

硬件异常被硬件以某种方式检测到并通知内核,然后内核向当前进程发送适当的信号。例如当前进程执行了除以0的指令,CPU的运算单元会产生异常,内核将这个异常解释为SIGFPE信号发送给进程。再比如当前进程访问了非法内存地址,MMU会产生异常,内核将这个异常解释为SIGSEGV信号发送给进程。

3.调用系统函数kill向进程发信号

写一个死循环程序
#include <stdio.h>
int main(){
    while(1){
        sleep(1);
        printf("immortal...\n");
    }
    return 0;
}

保存为endless.c

编译(make方式也可以)

gcc endless.c -o endless

复制SSH隧道在另一个窗口查看死循环进程的pid(在xshell中)

ps -ef|head -1;ps -ef | grep endless

记住Pid,然后使用11号信号SIGSEGV终止进程

kill -SIGSEGV pid  //等效于kill -11 pid

在这里插入图片描述

在这里插入图片描述

屏蔽死循环的输出
#include <stdio.h>
int main(){
    while(1){
        sleep(1);
        //printf("immortal...\n");
    }
    return 0;
}

./endless运行在后台,查看pid并使用11号信号SIGSEGV终止进程

在这里插入图片描述

  • kill -宏或者宏编号 pid 命令都是等价的

在这里插入图片描述

  • kill -SIGSEGV pid之后再回车才显示Segmentation fault ,是因为在pid进程终止掉之前已经回到了Shell提示符等待用户输入下一条命令Shell不希望Segmentation fault信息和用 户的输入交错在一起,所以等用户输入命令之后才显示。

  • 以往遇到的段错误都是由非法内存访问产生的,而这个程序本身没错,给它发SIGSEGV也能产生段错误

  • kill命令是调用kill函数实现的。kill函数可以给一个指定的进程发送指定的信号。raise函数可以给当前进程发送指定的信号(自己给自己发信号)。两个函数都是成功返回0,错误返回-1。

  • abort函数使当前进程接收到信号而异常终止。和exit函数一样,abort函数总是会成功的,所以没有返回值。

4.由软件条件产生信号

常见的产生信号的系统函数有:kill, raise, alarm和setitimer以及sigqueue函数

SIGPIPE是一种由软件条件产生的信号,客户端程序向服务器端程序发送了消息,然后关闭客户端,服务器端返回消息的时候就会收到内核给的SIGPIPE信号。

alarm函数与SIGALRM信号

alarm的返回值是0或者是以前设定的闹钟时间还余下的秒数

调用alarm函数可以设定一个闹钟,也就是告诉内核在seconds秒之后给当前进程发SIGALRM信号, 该信号的默认处理动作是终止当前进程。

#include <stdio.h>
#include <unistd.h>
int main(){
    int count = 0;
    alarm(1); //1秒钟之后,给当前进程发送SIGALRM信号
    for(;1;count++){
        printf("count = %d\n", count);
    }
    return 0;
}

这个程序的作用是1秒钟之内不停地数数,1秒钟到了就被SIGALRM信号终止

信号捕捉

siganl函数进行信号捕捉

#include <stdio.h>
#include <signal.h>
void handler(int sig)
{
	printf("catch a sig : %d\n", sig);
}
int main()
{
	signal(2, handler);
		while (1);
	return 0;
}

在这里插入图片描述

模拟一下野指针异常

#include <stdio.h>
#include <signal.h>
void handler(int sig)
{
	printf("catch a sig : %d\n", sig);
}
int main()
{
	signal(SIGSEGV, handler);
	sleep(1);
	int* p = NULL;
	*p = 100;
	while (1);
	return 0;
}

上述代码会无限循环捕捉信号量: 11) SIGSEGV

在这里插入图片描述

在C/C++当中除零,内存越界等异常,在系统层面上,是被当成信号处理的

总结

  • OS是进程的管理者,因此产生的所有信号,最终都要有OS来进行执行

  • 信号的处理不是立即处理,在合适的时候,因为信号是异步的

  • 信号如果不是被立即处理,那么信号需要暂时被进程记录下来,记录在进程地址空间的一个位图中(无符号整形)

  • OS向进程发送信号,实际上就是修改信号发往的目标进程PCB位图的对应记录信号的比特位

信号处理的三种方式

第一种是类似中断的处理程序,对于需要处理的信号,进程可以指定处理函数,由该函数来处理。

第二种方法是,忽略某个信号,对该信号不做任何处理,就象未发生过一样。

第三种方法是,进程通过系统调用signal来指定进程对某个信号的处理行为。对该信号的处理保留系统的默认值,这种缺省操作,对大部分的信号的缺省操作是使得进程终止。

信号处理流程

1.信号产生(见上文4个产生信号的例子),总结一下:

硬件来源(比如我们按下了键盘或者其它硬件故障);

软件来源:最常用发送信号的系统函数是kill, raise, alarm和setitimer以及sigqueue函数,软件来源还包括一些非法运算等操作。

–>

2.信号在进程中注册

–>

3.信号的执行和注销

信号产生到处理的状态

  1. 信号从产生到递达之间的状态,称为信号未决(Pending)。
  2. 进程可以选择阻塞 (Block )某个信号,被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作.
  3. 实际执行信号的处理动作称为信号递达(Delivery)
  4. 忽略是在递达之后可选的一种处理动作。

信号在内核中的表示

在这里插入图片描述

  • 每个信号都有两个标志位分别表示阻塞(block)和未决(pending),还有一个函数指针表示处理动作。信号产生时,内核在进程控制块中设置该信号的未决标志,直到信号递达才清除该标志。在上图的例子中,SIGHUP信号未阻塞也未产生过,当它递达时执行默认处理动作。

  • SIGINT信号产生过,但正在被阻塞,所以暂时不能递达。虽然它的处理动作是忽略,但在没有解除阻塞之前不能忽略这个信号,因为进程仍有机会改变处理动作之后再解除阻塞。

  • SIGQUIT信号未产生过,一旦产生SIGQUIT信号将被阻塞,它的处理动作是用户自定义函数sighandler。如果在进程解除对某信号的阻塞之前这种信号产生过多次,将如何处理?POSIX.1允许系统递送该信号一次或多次。Linux是这样实现的:常规信号在递达之前产生多次只计一次,而实时信号在递达之前产生多次可以依次放在一个队列里。本章不讨论实时信号。

内核实现信号捕捉

在这里插入图片描述

如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号。由于信号处理函数的代码是在用户空间的,处理过程比较复杂,举例如下: 用户程序注册了SIGQUIT信号的处理函数sighandler。当前正在执行main函数,这时发生中断或异常切换到内核态。在中断处理完毕后要返回用户态的main函数之前检查到有信号SIGQUIT递达。内核决定返回用户态后不是恢复main函数的上下文继续执行,而是执行sighandler函数,sighandler和main函数使用不同的堆栈空间,它们之间不存在调用和被调用的关系,是两个独立的控制流程。sighandler函数返回后自动执行特殊的系统调用sigreturn再次进入内核态。如果没有新的信号要递达,这次再返回用户态就是恢复main函数的上下文继续执行了。

在这里插入图片描述

标签:函数,signal,信号量,kill,内核,信号,进程
来源: https://blog.csdn.net/qq_43808700/article/details/115834703

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有