栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

[linux] 进程间信号

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

[linux] 进程间信号

信号在生活中无处不在,上下课铃,红绿灯,闹钟等,这一切都是信号,可是我们是如何识别信号的呢?无非就是有人定义将信号与意义绑定,并灌输。在操作系统中也存在信号,那么我们一起来看看吧


文章目录
  • 1 理解信号
    • 1.1 见识Linux中的信号
    • 1.2 如何产生信号
    • 1.3 信号的处理
      • 1.3.1 屏蔽信号与获取pending
      • 1.3.2 信号捕捉(自定义处理)
    • 1.4 SIGCHILD

1 理解信号

信号不管是在生活中还是在操作系统中其实都可以简单看为三部分

  1. 是什么
  2. 为什么
  3. 怎么处理

而在操作系统层面来看并不需要知道为什么(毕竟信号也是工程师定义的),他只需要知道是啥信号和收到信号后要如何处理,其实生活中收到信号也是如此,过马路红灯停,绿灯行,并不会有人问为啥。


1.1 见识Linux中的信号

在linux中查看信号需要用到命令 kill -l,一共62个信号,1~31为默认信号,24~64为实时信号

浅谈: 默认信号 VS 实时信号

普通信号会丢失,实时信号不会丢失,也就是是否要存储信号,后文会提

在Linux下的全部信号都是宏,也就是一个标志位,这样做的目的节省空间,用一个bit位标识一种状态,默认信号存放在该路径下/usr/include/bits/signum.h

1.2 如何产生信号

一切事情都是有前因后果的,当然信号也是,先要如何产生才可以知道如何处理

  1. 键盘产生
  2. 命令行向进程发送信号
  3. 调用函数向os发送请求
  4. 代码出错(访问硬件出错)

键盘产生

ctrl+c 终止进程 ctrl+ 终止进程并保存core文件 ,ctrl+z挂起一个文件,他们其实和信信号列表一一对应,后文信号捕捉验证


命令行向进程发送信号

使用命令 kill -信号(数字or宏名) 进程pid

调用函数向os发送请求

其实就是和os说快给我发信号,C语言接口alarm(延迟xx秒让os发送14号信号给该进程),abort(让os给当前发6号信号),raise(指定让os发送x号信号),kill(给别的进程发送信号)

alarm
代码:

int main()
{
  alarm(5);
  int count=0;
  while(1)
  {

    cout< 

结果:

kill

代码:

#include
#include
#include
#include
using namespace std;


int main()
{
  kill(1213,1);
}

结果:

代码出错(访问硬件出错)

当子进程退出的时候可以用wait函数等待回收他的资源,且也可以知道他的退出码,core dump ,信号 。

代码:

#include
#include
#include
#include
#include

using namespace std;


int main()
{
  pid_t id=fork();
  while(id==0)
  {
    //child
    int count=0;
    while(count!=5)
    {
      count++;
      cout<<"I am child proc"< 

结果:

问

os是如何知道代码出错而发对应的信号呢?

答

除0对应的硬件 == cup中的状态寄存器
野指针 == 内存/页表MMU
os是软硬件的管理者,自然就知道哪里出错,在根据硬件在对进程发送信号

core dump


1.3 信号的处理

在现实中我们处理信号一般有三种方式

  1. 默认(识别信号马上处理 , 红绿灯)
  2. 忽略 (识别信号不处理,打游戏妈妈叫吃饭)
  3. 捕捉自定义 (识别后用自己的方法,听到闹钟声跳个舞)

在操作系统中也是如此处理信号也是以上三种方式,且也知道一个进程收到信号不一定马上处理,那么不处理就要保存起来(该进程的PCB中)。按照老的套路先描述在组织,这里的组织方式就是一个位图

当os发送信号给进程时,看信号是xx号并在对应的pending位图中,把xx号位置设为1 ,再看看block位图xx号位置是否设为1,如果为1屏蔽信号,不然直接执行方法。其实信号被屏蔽还有一个专业的名词(未决),信号没有被屏蔽执行的方法(递达)

递达信号流程图


1.3.1 屏蔽信号与获取pending

在C语言中有一个类型sigset_t 可以表示一个每个信号的有效与无效,也就是一个位图,我们可以用它和信号集操作函数等来告诉os屏蔽那些信号

信号集操作函数

 #include 
int sigemptyset(sigset_t *set);//初始化 ”位图“ 为0
int sigfillset(sigset_t *set);//初始化 ”位图“ 为1
int sigaddset (sigset_t *set, int signo);//给 “位图”指定位置为1
int sigdelset(sigset_t *set, int signo);//给 “位图”指定位置为0
int sigismember(const sigset_t *set, int signo);//判断 ”位图“指定位置是否为1

上述的接口都是语言接口,也就说明,我们还没有告诉os要屏蔽那个信号,那么就要用到系统接口,sigprocmask

sigprocmask使用介绍

使用这个接口就可以让os吧pcb中block变成自己所设置位图的样子,屏蔽信号

sigpending使用介绍


输入形参数,获取pending当前状态

代码:
实现屏蔽信号获取pending,删除屏蔽,抵达信号

#include
#include
#include
#include
using namespace std;


void showpending(sigset_t * set)
{
	for(int i =1; i<=31;i++)
	{
		if(sigismember(set,i))
		{
			cout<<'1';
		}
		else
		{
			cout<<'0';
		}
	}
	cout<
	sigset_t set,oset;
	sigemptyset(&set);
	sigemptyset(&oset);


	sigaddset(&set,2);
	sigprocmask(SIG_SETMASK,&set,&oset);

	int count=0;
	while(1)
	{
		sigpending(&set);
		showpending(&set);
		sleep(1);
    count++;
		if(count==7)
		{
			break;
		}
	}


	return 0;
}

结果:



1.3.2 信号捕捉(自定义处理)

上述说过信号的处理方式, 默认,忽略,捕捉,其实捕捉就是自定义信号(递达),也就是修改pcb中的hander函数数组中的方法,系统接口signal ,sigaction

接口介绍:

接口使用

signal

#include
#include
#include
#include
using namespace std;


void hander(int signo)
{
  cout<<"signo: "<
  signal(2,hander);
  int count=0;
  while(1)
  {
    cout< 

结果:


sigaction

#include
#include
#include
#include
using namespace std;


void hander(int signo)
{
  cout<<"signo: "<

  //signal(2,hander);
   struct sigaction act;
   struct sigaction oact;
  act.sa_handler=hander;
  sigaction(2,&act,&oact);
  int count=0;
  while(1)
  {
    cout< 

结果:

问

既然可以屏蔽捕捉信号,那我全部屏蔽我这个进程不就杀不死了吗?

答

既然你可以想到,没错工程师也想到了,在linux下有SIGKILL与SIGTOP信号是无法捕捉的和屏蔽,专门防止你小心思


问

问信号时如何捕捉的呢?

答

抽象理解,捕捉函数是一个地雷,而触发的条件就是信号。

回顾进程虚拟地中空间:

之前说每个进程都有自己的一张独有的页表,其实还不太准确,其实有俩张一张是用户级页表,一张是系统级页表(每个进程共享),在执行代码时,没有遇到系统接口前用的都是用户级页表,当执行到系统接口时转换为系统页表

图解捕捉捕捉的流程图:
捕捉的过程其实很像数学中的一个符号∞


1.4 SIGCHILD

之前说过子进程退出的时候父进程会回收子进程,一方面是防止内存泄漏,一方面是获取子进程的退出结果。但等待,父进程就在阻塞等待里(or非阻塞轮询),着都让父进程无法一心一意的执行自己的代码?那么如何解决呢?

解决方案

当子进退出的时候会给父进程发送一个信号SIGCHILD,,那配合signal不就…………子进程退出发送信号,捕捉,改变SIGCHILD的函数实现方法

代码:

using namespace std;

void hander(int signo)
{
	cout<0)
	{
		printf("wait pid %d success",id);
	}


}
int main()
{

	signal(17,hander);
	pid_t id =fork();
	if(id==0)
	{
		//child
		int count=4;
		while(count>0)
		{
			count--;
			cout<
		cout<<"I am father proce"< 

结果:

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/939092.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号