信号(一)

[TOC]

##1.信号基本概念
信号是事件发生时堆进程的通知机制,也称为软件中断。信号与硬件中断的相似之处在于打断了程序执行的正常流程。

##2.信号产生的原因及分类
1. 产生的诸多信号通常都来源于内核。引发内核为进程产生信号的各类事件:
- 硬件发生异常
- 用户键入能够产生信号中断的特殊字符,如ctrl+c
- 发生了软件事件,如该进程的某个子进程退出

2. 信号分为两大类
- 标准信号:内核向进程通知事件,编号范围:1~31
- 实时信号(注:与标准信号的差异后来补。。。)

可以输入 \`kill -l` 命令查看linux内核支持的信号

输入 `man 7 signal` 命令查看信号的默认动作、信号的含义

##3.信号在Linux中的传递过程

我们从左至右的看上面这张图片。
task_struct代表的是进程控制块(PCB)的结构;每个进程在内核中都有一个进程控制     块来维护进程的相关信息。
在task_struct中存储着信号屏蔽状态字(block),信号未决状态字(pending)和handler信息。

综述:向进程发送某个信号,内核首先判断其对应的PCB中 `block`的标志,
- 0代表不阻塞
- 1代表阻塞
若为阻塞状态,则将其对应的未决状态也置为1;若不为阻塞,相应的未决状态赋为0,代表信号可以抵达了。
信号抵达后,也有着不同的处理方式,如 `SIG_DFL` :执行默认的方式; `SIG_IGN` :忽略;或者执行信号处理器程序(即程序员自己编写的程序)。

###①如何获取或操作某进程block信息
| int sigprocmask(int how, const sigset_t *set, sigset_t *oset); |
| -------- | :-----: |
| 功能 | 读取或更改进程的信号屏蔽字(pending)
| 返回值: | 若成功则为0,若出错则为-1 |


参数
how SIG_BLOCK 参数set包含了希望添加到block中屏蔽字
SIG_UNBLOCK 参数set包含了希望解除的屏蔽字
SIG_SETMASK 参数set设置为当前信号屏蔽字
set
oset 原来的信号屏蔽字

###②如何获取某进程pending信息
| int sigpending(sigset_t *set) | |
| -------- | :-----: |
| 功能 | 获取信号未决状态字(pending)信息 |
| 返回值 | 若成功则为0,若出错则为-1 |

###③补充知识(信号集操作函数)
| 函数 | 作用 |
| -------- | :-----: |
| int sigemptyset(sigset_t *set) | 把信号集清零 |
| int sigfillset(sigset_t *set) | 把信号集制成1 |
| int sigaddset(sigset_t *set, int signo) | 根据signo,把信号集中的对应为置成1 |
| int sigdelset(sigset_t *set, int signo) | 根据signo,把信号集中的对应为置成0 |
| int sigismember(const sigset_t *set, int signo) | 判断signo是否在信号集中 |

###④小demo
SIGINT信号设置阻塞,查看未决关键字
SIGINT信号解除阻塞,查看未决关键字

void handler(int sig)
{
	if (sig == SIGINT)
	{
		printf("recv a sig=%d\n", sig);
		printf("\n\n\n");
		//fflush(stdout);
	}
	else if (sig == SIGQUIT)
	{
		sigset_t uset;
		sigemptyset(&uset);
		sigaddset(&uset, SIGINT);
		//ctr + \ 用来接触  SIGINT 信号
		//解除阻塞
		sigprocmask(SIG_UNBLOCK, &uset, NULL);
		
		//signal(SIGINT, SIG_DFL) ;
	}
}

void printsigset(sigset_t *set)
{
	int i;
	for (i=1; i<NSIG; ++i)
	{
		//printf("%d\n",NSIG);
		if (sigismember(set, i))
			putchar('1');
		else
			putchar('0');
	}
	printf("\n");
}

int main(int argc, char *argv[])
{
	sigset_t pset; //用来打印的信号集
	sigset_t bset; //用来设置阻塞的信号集
	sigset_t bbset;
	sigset_t tmpset;
	
	sigemptyset(&bset);
	sigaddset(&bset, SIGINT);
	
	if (signal(SIGINT, handler) == SIG_ERR)
		ERR_EXIT("signal error");
		
	if (signal(SIGQUIT, handler ) == SIG_ERR)
		ERR_EXIT("signal error");

	//读取或更改进程的信号屏蔽字 这里用来阻塞ctrl+c信号
	//ctrl+c信号被设置成阻塞,即使用户按下ctl+c键盘,也不会抵达
	sigprocmask(SIG_BLOCK, &bset, NULL);
	
	for (;;)
	{
		//获取未决字信息
		sigpending(&pset);

		printf("block......\n\n\n");
		sigemptyset(&tmpset);
		sigprocmask(SIG_BLOCK,&tmpset,&bbset);
		printsigset(&bbset);
		
		//打印信号未决字
		printf("pending......\n");
		printsigset(&pset);
		
		sleep(1);
	}
	return 0;
}
posted @ 2018-07-08 15:55  神秘的火柴人  阅读(148)  评论(1)    收藏  举报