信号的产生与使用

信号

信号的基础

信号就是软中断,
软件实现中断的一种机制
机器指令 检查是否有中断到来 ------》 (没有)机器指令
有中断到来 —> 调用中断处理函数—>回到刚刚机器指令
信号的处理是异步的
信号的到来有不确定性
信号的目的地是一个进程
信号到来后,有信号处理 程序

系统提供的信号

系统一共提供了 62 个信号
2) SIGINT 3) SIGQUIT
9) SIGKIL 11) SIGSEGV
4) SIGALRM 17) SIGCHLD(子进程终止的时候,给父进程发送sigchld 信号 ,父进程回收子进程资源)
每个进程都有自己的信号处理函数
进程初始的时候,所有的信号的信号处理 程序采用默认的
默认处理大部分是终止当前进程。

信号的产生 信号递达接收 信号的处理
信号的未决状态 : 信号产生 但是信号还没有处理,为未决状态
未决状态的信号 成为未决信号

进程可以设置对信号的阻塞 阻塞信号

修改进程的信号处理函数

#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
功能:将signum 的信号处理函数指定为handler

参数:
signum : 信号值
handler : 指定的信号处理函数(signum to handler, which is either SIG_IGN, SIG_DFL)
SIG_IGN : 忽略处理
SIG_DFL : 默认处理
The signals SIGKILL and SIGSTOP cannot be caught or ignored.

返回值:
错误 : SIG_ERR errno
成功 : 返回原来的值 (原来的信号处理函数)

sighandler_t 是什么类型的别名?
typedef void (*sighandler_t)(int);

sighandler_t* 的别名 
对 sighandler_t 类型变量访问的时候,遵循  typedef void  (int); 的规则

子进程继承父进程的信号处理函数。

#include <stdio.h>
#include <signal.h>

void handler(int n)
{
	printf("recv... %d  \n",n);
	return ;
}

int main()
{
	int ret = signal(2, handler);
	signal(3, SIG_IGN  );
	signal(9, SIG_DFL );
	printf("ret = %d\n",ret);
	while(1);
}

include <stdio.h>
#include <signal.h>
#include <unistd.h>
void handler(int n)
{
	printf("recv... %d  \n",n);
	return ;
}

int main()
{
	int ret = signal(2, handler);
	signal(3, SIG_IGN  );
	signal(9, SIG_DFL );
	printf("ret = %d\n",ret);
	fork();
	while(1);
}

  1. 9号信号不可以被捕获或者修改。
  2. 子进程可以继承父进程的信号处理函数和方法。直接输入2好进程的快捷建 (ctrl + c), 父子进程都会调用信号处理函数。

信号的产生

1. 硬件产生信号

ctrl + c 2 号信号
ctrl + \ 3号信号

2. 使用kill(1) 给进程发送信号

kill -信号编号 进程的pid

3. 使用库函数或者系统调用产生信号

kill (2) raise(2) alarm(2)
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
功能:用于发送信号给进程
参数:
pid : 目标进程的pid
sig : 信号的编号
返回值:
成功 0
错误 -1 errno
使用kill 函数实现kill 命令

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

int main(int argc,char *argv[])
{
	int pid = atoi(argv[1]);
	int sig = atoi(argv[2]);
	int ret = kill( pid,sig);
	if (ret < 0)
	{
		perror("kill");
		return -1;
	}
}

kill(getpid(), sig); =============> raise(sig)
raise 就是给自己发送信号

#include <unistd.h>

unsigned int alarm(unsigned int seconds);
功能 : 产生一个SIGALRM在seconds 内将信号递达当前进程

参数
seconds : 秒数
如果seconds 为0 ,所有的未决alarm 被取消

返回值:
原来的alarm 未决时间值

比如
alarm(3)
… 1
alarm(0) : 会返回2

在这里插入代码片
```#include <stdio.h>
#include <unistd.h>

int main(void)
{
	alarm(5);  // 设置时钟5s
	for(int i =0; i<30000; i++)
	{
		printf("%d\n",i);
	}
	int ret = alarm(6);  //  运行到这时,还有4s 会执行上面的alarm(5),  但是现在会重新设置时钟,alarm 信号重新计时,6s 后产生alarm 信号,进程结束

	printf("ret = %d\n", ret);
	while(1);

}

运行结果

```c
29999
ret = 4
Alarm clock

#include <unistd.h>
int pause(void);
功能 : 等待信号
参数:
返回值 :
-1 errno 被设置 只有信号到达,调用完信号处理函数后,再返回。

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void handler(int n)
{
	printf("recv... %d  \n",n);
	return ;
}

int main()
{
	signal(2, handler);
	int ret = pause();
	if (ret < 0)
	{
		printf("ret = %d \n", ret);
		return -1;
	}
}

运行结果
在获得信号2 后,才会停止 ,返回 值为-1

^Crecv... 2  
ret = -1 

使用 alarm 和pause 实现sleep

unsigned int sleep(unsigned int seconds);

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
void handler(int n)
{
//	printf("recv... %d  \n",n);
//	return ;
}

int main(int argc, char* argv[])
{
	int ret = 0;
	signal(SIGALRM, handler);
	while (1)
	{
		alarm(atoi(argv[1]));
		ret = pause();
		ret = alarm(0);
		printf("sleep....%d \n", ret);  //  ret 返回值为0 表示alarm 已经执行完毕
		printf("sleep....\n");
	}
	if (ret < 0)
	{
		printf("ret = %d \n", ret);
		return -1;
	}
}

信号阻塞的设置

信号的阻塞和未决
信号掩码集表示信号的阻塞 1 表示阻塞该信号 0 表示不阻塞该信号
未决信号集 为1 表示该信号产生了,但是还未执行。0表示该信号还未产生。
比如第一个信号 1 1 表示该信号已经产生,但未执行,对该信号阻塞
1 0 表示对该信号 阻塞 但是该信号未产生
0 1 表示不阻塞该信号 该信号已经产生,处于未决状态

信号集类型 sigset_t
信号集类型的相关操作
#include <signal.h>
int sigemptyset(sigset_t *set);

功能 : 将信号集初始化为空, 将所有的信号从信号集中移除
参数 : 指定要初始化的信号集的地址
返回值 :
成功 0
失败 -1 errno

int sigfillset(sigset_t *set);
功能 : 将信号集初始化为满, 包含所有的信号
参数 : 指定要初始化的信号集的地址
返回值 :
成功 0
失败 -1 errno

int sigaddset(sigset_t *set, int signum);
功能 : 将信号添加到set (信号集)中
参数 : 指定要初始化的信号集的地址
signum : 信号编号
返回值 :
成功 0
失败 -1 errno

int sigdelset(sigset_t *set, int signum);
功能 : 将信号从set (信号集)中移除
参数 : 指定要初始化的信号集的地址
signum : 信号编号
返回值 :
成功 0
失败 -1 errno

int sigismember(const sigset_t *set, int signum);
功能 : 测试信号是不是 set 信号集中的一员
参数 : 指定要初始化的信号集的地址
signum : 信号编号
返回值 :
0 表示 signum 不是set 中的一员
1 表示 signum 是set 中的一员

信号集的操作示例:

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
	sigset_t *set = malloc (sizeof (sigset_t));
	int ret = sigemptyset(set);
	if (ret < 0){
		perror("sig init");
		return -1;
	}
	sigaddset(set, 2);
	sigaddset(set, 3);
	ret = sigismember(set, 3);
	if (ret == 1) printf("3 is in set \n");
	else  printf("3 is not in set\n ");
	sigdelset(set, 3);

	ret = sigismember(set, 3);
	if (ret == 1) printf("3 is in set \n");
	else  printf("3 is not in set\n ");

	free(set);
}

执行结果

3 is in set 
3 is not in set

sigprocmask (2)

#include <signal.h>

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
功能 : 检查和改变阻塞信号集
参数 :
how :
SIG_BLOCK : 将set 和当前进程的信号掩码集的并集设置为当前信号集的掩码集
SIG_UNBLOCK : 将set 集合中的信号从当前进程的信号的掩码集中移除

SIG_SETMASK : 将set 设置为信号的掩码集

set : 用户自己定义的信号集
oldset :
如果不为空,将进程原来的信号掩码集保存早这里。
如果为空,则原来的信号掩码集不要了

返回值
成功 0
失败 -1 errno

#include <stdio.h>
#include <signal.h>

void handler (int n)
{
	printf("rec... %d\n",n);
	return ;

}
int main()
{
	sigset_t set, old_set;
	signal(2,handler);
	signal(9,handler);
	signal(45,handler);
	sigemptyset(&set);
	sigaddset(&set, 2);
	sigaddset(&set, 9);
	sigaddset(&set, 45);
	sigprocmask(SIG_SETMASK, &set, &old_set);
	for(int i = 0;i< 1500000; i ++)
		printf("i= %d\n",i);
	sigprocmask(SIG_SETMASK, &old_set,NULL);
	while(1);
	return 0;
}

运行结果:

i= 1499999
rec... 45
rec... 45
rec... 45
rec... 45
rec... 45
rec... 2

注意

  1. 信号的阻塞和信号的忽略是两个不同的概念
    信号的阻塞是信号被挡在进程中,进程中没有调用信号处理函数处理这个信号。
    信号的忽略是信号已经到达,但是视而不见。
    信号忽略是对信号处理的一种方式
  2. 信号分为可靠信号和不可靠信号
    有信号的丢失,这些信号成为不可靠信号 0~31
    可靠信号的编号是从34~64 也叫做实时信号
    所以上面代码中,kill -45 pid 5此后,会执行5次信号处理函数。但是发送多次2的信号,只会执行一个 rec… 2

未决信号集的获取

#include <signal.h>
int sigpending(sigset_t *set);
功能 : 获取进程的未决信号集
参数 :
set 将进程的未决信号集填充到set 中
返回值 :
成功 0
错误 -1 errno 被设置

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void handler (int n)
{
	printf("rec... %d\n",n);
	return ;

}
int main()
{
	sigset_t set, old_set,p_set;
	signal(2,handler);
	signal(9,handler);
	signal(45,handler);
	sigemptyset(&set);
	sigaddset(&set, 2);
	sigaddset(&set, 9);
	sigaddset(&set, 45);
	sigprocmask(SIG_SETMASK, &set, &old_set);
	while(1)
	{
		sigpending(&p_set);
		sigismember(&p_set,2)?printf("pending\n"):printf("not pending\n");
		sleep(2);
	
	}
	return 0;
}

执行结果:
开始 2 号信号不是未决信号,因此阻塞信号集中没有2号,
但是发送2号信号给当前继承后,发现2号信号为pending .

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值