操作系统:信号

一 信号概念

1. 信号简介

进程可以两耳不问窗外事,一心一意演自己的剧本。
但是进程是依附于操作系统上实现的应用,进程需要被管理。其它进程或者操作系统可能会随时来本进程家门口敲门拜访,这就是信号!

信号有两个特点:
第一,信号是异步的,理解为异步的事件;
第二,进程对外来事件的处理是异步的!

2. 信号/事件 产生方式

  • 用户按键:用户使用某种特殊字符递送给终端,产生一个信号(事件)递送给进程
  • 硬件故障:进程执行错误,比如访问一个无效内存地址,这时候先由硬件报告给内核,再由内核把事件递送给进程
  • kill函数或者命令:通过函数把需要的事件直接递送给进程

3. 信号处理方式

当我们把信号形象的理解为异步事件后,分析问题会更加清晰
信号/事件 发生时,谁去处理事件,怎么处理呢?是有进程自己处理吗,还是操作系统处理?
现代操作系统给出的答案是,有些事件进程可以指定要如何处理。但是一些特殊的事件过于严重,操作系统决定默认处理方式!

这样,信号处理就分为三种形式:
第一,捕获:进程调用自己的函数处理事件
第二,忽略:事件发生,进程与操作系统都不做理会
第三,默认:事件发生,操作系统的默认操作

二 信号实现

1. 不可靠信号

不可靠指:信号发生了,但是它可能会丢失;

下面图文说明了一个信号常见的处理过程:
第一步,信号产生:终端按键/指令故障/kill函数事件发生后,事件会被记录在进程描述结构当中
第二步,信号处理:如果信号未被阻塞,信号处理方式选择为忽略/默认/捕获中的一种
第三步,信号未决:这一步和第二步互斥,如果信号被阻塞,信号暂时将得不到处理

sigpending 函数用于查询被阻塞并且pending状态的信号(已经发生的信号)
sigprocmask 用于查询当前哪些信号(未必发生过)需要被阻塞
在这里插入图片描述

那么我们继续回到最初的问题:信号不可靠指的是什么?
假如我们已经有一个SIGCHLD信号在signal pending的状态中,这个时候从其他进程递送过来好多个SIGCHLD信号,按照道理这些信号需要记录在进程表结构当中------但是,进程表相关结构可能不支持链表,它记录不下这么多信号,于是后面的信号可能覆盖原来的信号(最终只得到一个信号)
最后N个信号只保留了一个!丢失了N-1个,也就是不可靠的概念
与之相对,有的信号在发生时,会在进程表中进行排队,这样一个信号也不会丢失,这样的信号叫做可靠信号
在这里插入图片描述

2. 可靠与不可靠信号类型

使用kill -l命令可以得到系统支持的信号类型
其中 1~31项为不可靠信号, 34~64为可靠信号
在这里插入图片描述

3. 被中断的系统调用

与I/O相关的接口通常会引起进程被挂起/睡眠
正常情况下,IO操作完成后,操作系统将唤醒进程,进程获取了自己期望的结果
但是也有异常,进程因为IO操作被挂起后,可能因为信号被唤醒!同时,不能操作系统可能实现可能也不同!

读者可以参考《UNIX高级环境编程》一书中相关章节
附表;部分平台上系统调用被中断的情况
在这里插入图片描述

4. 信号函数

4.1 信号发送函数 kill/raise

kill

   #include <sys/types.h>
   #include <signal.h>

   int kill(pid_t pid, int sig);

功能:
pid == 0,信号发送给当前进程所属进程组
pid > 0,信号发送给指定的进程(用户权限满足情况下)
pid < 0,信号发送给指定进程组:进程组ID ==|pid|(用户权限满足情况下)
pid == -1,信号发送给所有有权限发送信号的进程

raise

信号发送给进程自身,同时raise函数支持多线程模型,这是POSIX规定的
     
#include <signal.h>

int raise(int sig);

4.2 定时发送信号函数 alarm

alarm
#include <unistd.h>

   unsigned int alarm(unsigned int seconds);

功能:
进程休眠指定时间,超时时产生一个SIGALRM信号,此信号默认终止进程
另外,信号产生到信号处理程序被调用不是立刻完成的,需要额外时间开销

4.3 信号捕获等待函数 pause

pause
#include <unistd.h>

   int pause(void);

功能:
进程休眠,并且只在捕获一个信号并从信号处理程序返回时,pause才会返回

5. 信号集

5.1 信号集来由

早期操作系统上信号不超过32个,因此信号使用一个int整型表示,每一个bit对应一个信号

现代操作系统,信号种类越来越多,信号不再使用一个int表示,定义了新的类型sigset_t-------信号集

函数集数据结构处理 函数原型

       #include <signal.h>

       int sigemptyset(sigset_t *set);

       int sigfillset(sigset_t *set);

       int sigaddset(sigset_t *set, int signum);

       int sigdelset(sigset_t *set, int signum);

       int sigismember(const sigset_t *set, int signum);
       /*
        sigemptyset() initializes the signal set given by set to empty, with all signals excluded from the set.

       sigfillset() initializes set to full, including all signals.

       sigaddset() and sigdelset() add and delete respectively signal signum from set.

       sigismember() tests whether signum is a member of set.
       */

5.2 信号集函数

#include <iostream>
using namespace std;

extern "C"
{
	#include <signal.h>
}

/*

       int sigemptyset(sigset_t *set);

       int sigfillset(sigset_t *set);

       int sigaddset(sigset_t *set, int signum);

       int sigdelset(sigset_t *set, int signum);

       int sigismember(const sigset_t *set, int signum);
*/

void check_sig(sigset_t const *set)
{
	if(NULL == set)
	return;
	
	for(int cnt = 1;cnt <= SIGRTMAX;cnt++)
	{
		/*注意:把错误的signum值传递给函数,会返回-1,即错误*/
		int res = sigismember(set,cnt);
		if( res == -1)
		{
			cout << "err during sigismember" << endl;
			return;
		}
		else if(1 == res)
		{
			cout << "Signal " << cnt << " is a member of sigset" << endl;
		}
	}
}
int main()
{
	sigset_t set;
	
	/*没有信号*/
	sigemptyset(&set);
	check_sig(&set);

	cout << "--------------" << endl;
	/*系统支持的所有信号都包含在内 [1,31],[33,64]*/
	sigfillset(&set);
	check_sig(&set);
	
	cout << "---------SIGQUIT = " << SIGQUIT << endl;
	cout << "-----Del signal----SIGQUIT = " << endl;	
	/*删除一个信号 SIGQUIT*/
	sigdelset(&set,SIGQUIT);
	check_sig(&set);

	cout << "-----------add SIGQUIT-----------" << endl;
	/*添加回来被删掉的信号 SIGQUIT*/
	sigaddset(&set,SIGQUIT);
	check_sig(&set);

	return 0;
}

6 进程与信号控制

6.1 sigprocmask

获取或者设置进程对信号集的阻塞控制

#include <iostream>
using namespace std;

extern "C"
{
	#include <signal.h>
}

/*

       int sigemptyset(sigset_t *set);

       int sigfillset(sigset_t *set);

       int sigaddset(sigset_t *set, int signum);

       int sigdelset(sigset_t *set, int signum);

       int sigismember(const sigset_t *set, int signum);
	
	// how = {SIG_BLOCK | SIG_UNBLOCK | SIG_SETMASK}
       int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
*/

void check_sig(sigset_t const *set)
{
	if(NULL == set)
	return;
	
	for(int cnt = 1;cnt <= SIGRTMAX;cnt++)
	{
		/**/
		int res = sigismember(set,cnt);
		if( res == -1)
		{
			cout << "err during sigismember" << endl;
			return;
		}
		else if(1 == res)
		{
			cout << "Signal " << cnt << " is a member of sigset" << endl;
		}
	}
}

int main()
{
	sigset_t set;
	
	/*默认所有信号 非阻塞*/
	cout << "read default blocked status" << endl << endl;
	sigprocmask(0,NULL,&set);
	check_sig(&set);

	/*尝试阻塞所有信号:结果信号都被阻塞,SIGKILL(9)和SIGSTP(19)除外
	(这两个信号不允许阻塞)*/
	cout << "set filled signals to blocked status" << endl << endl;
	sigfillset(&set);
	if(-1 == sigprocmask(SIG_SETMASK,&set,NULL))
	{
		cout << "err while calling sigprocmask" << endl;
		return -1;
	}
	sigprocmask(0,NULL,&set);
	check_sig(&set);

	/*尝试去除SIGCHLD阻塞:结果成功*/
	cout << "unblockSIGCHLD signal" << endl << endl;
	sigemptyset(&set);
	sigaddset(&set,SIGCHLD);
	if(-1 == sigprocmask(SIG_UNBLOCK,&set,NULL))
	{
		cout << "err while calling sigprocmask" << endl;
		return -1;
	}
	sigprocmask(0,NULL,&set);
	check_sig(&set);
	return 0;
}

6.2 sigpending

检查未决的信号,未决信号条件:
1、信号被阻塞
2、信号处理方式为默认方式或者捕获方式
3、 信号已经发生

#include <iostream>
using namespace std;

extern "C"
{
	#include <signal.h>
}

/*

       int sigemptyset(sigset_t *set);
       int sigaddset(sigset_t *set, int signum);
       int sigismember(const sigset_t *set, int signum);
	// how = {SIG_BLOCK | SIG_UNBLOCK | SIG_SETMASK}
       int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
	   int sigpending(sigset_t *set);
	   typedef void (*sighandler_t)(int);
      sighandler_t signal(int signum, sighandler_t handler);
*/

void check_sig(sigset_t const *set)
{
	if(NULL == set)
	return;
	
	for(int cnt = 1;cnt <= SIGRTMAX;cnt++)
	{
		/**/
		int res = sigismember(set,cnt);
		if( res == -1)
		{
			cout << "err during sigismember" << endl;
			return;
		}
		else if(1 == res)
		{
			cout << "Signal " << cnt << " is a member of sigset" << endl;
		}
	}
}

void sig_quit_handler(int signum)
{
	/*do nothing*/
	
}

int main()
{
	/*SIGQUIT信号处理方式设置为捕获*/
	signal(SIGQUIT,sig_quit_handler);

	/*阻塞SIGQUIT信号*/
	sigset_t set;
	sigemptyset(&set);
	sigaddset(&set,SIGQUIT);
	sigprocmask(SIG_BLOCK,&set,NULL);

	/*产生SIGQUIT信号*/
	raise(SIGQUIT);

	cout << "raise is called" << endl;
	/*获取pending 阻塞的信号*/
	sigpending(&set);
	/*检查返回信号,打印出3 ---即 SIGQUIT/
	check_sig(&set);
	return 0;
}

6.3 sigaction

功能:
1、注册信号处理函数 以及 信号处理函数执行时,信号屏蔽/阻塞控制
2、挂起的系统调用被信号中断后:是返回"被中断状态"还是系统调用重启
3、其它信号处理细节控制
4、拓展:信号底层函数,可以用来实现signal函数…

  • 代码略

6.4 sigsetjmp 与 siglongjmp

函数原型: 提供了非局部跳转时,信号屏蔽字的控制功能
#include <setjmp.h>

   int setjmp(jmp_buf env);

   int sigsetjmp(sigjmp_buf env, int savesigs);
   void siglongjmp(sigjmp_buf env, int val);
// An highlighted block
var foo = 'bar';

6.5 sigsuspend

功能:
使用mask临时替换现有信号屏蔽字,然后等待任意信号被捕获,信号处理程序执行并返回后,信号屏蔽字被恢复为原来默认值

   #include <signal.h>

   int sigsuspend(const sigset_t *mask);
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值