基于VS2019 C++的跨平台(Linux)开发(1.4)——信号

一、目标

  • 信号概念
  • signal、sigaction函数
  • 信号集操作函数
  • 时钟

本章先来学习不带参数的信号以及kill、signal函数

二、信号

1、概念

  • 信号是系统响应某些状况而产生的事件,进程在接收到信号时会采取相应的行动。可以是因为某些错误条件而产生的,比如内存段冲突、浮点处理器错误或者非法指令等
  • 进程可以生成信号、捕捉并响应信号或屏蔽信号

2、用途

进程间通信,之前提到进程之间的数据是不能进行互通的,所以就要用到进程间通信技术,这个信号就是其中之一 ,就类似Qt里面的信号和槽(在界面切换的时候可以用到),(开发Qt的时候实际他就是参照linux内核编程进行开发)

3、回顾kill命令

kill不是杀死而是发送:我们要突破之前的一个错误认知,单纯的认为kill -9 pid就是杀死进程,实际上并不是这样,要把他理解为发送第九号信号(SIGKILL),去结束后面的pid所对应的进程。查询kill支持的信号,如下图所示

 从上图仔细观察会发现,中间少了32号、33号,这其实是做一个分界线,1-31被归为不可靠信号,43到之后的被归为可靠信号(会在以下代码中进行验证)。其中10、12供程序员开发使用

4、信号名称

信号的名称是在头文件 signal.h里定义的,部分名称如图

5、进程与信号(signal库函数)

如果一个进程想要通知另一个进程做一些事情,就可以利用信号,所以涉及到signal库函数,如下

头文件<signal.h>

函数原型:

void (*signal(int sig, void (*func)(int))) (int);

signal是一个带sig和func两个参数的函数,准备捕捉或屏蔽的信号由参数sig给出,接收到指定信号时将要调用的函数由func给出。

func这个函数必须有一个int类型的参数(即接收到的信号代码),它本身的类型是void func也可以是下面两个特殊值:         

  • SIG_IGN    屏蔽该信号         
  • SIG_DFL    恢复默认行为 

linux中查看signal库函数如下图

其中signum是信号的编号,如第九号信号kill(停止进程),sighandler表示信号到达的时候进程要做什么事情;

typedef void (*sighandler_t)(int); 函数名前带一个*,表示函数指针,即做什么函数的逻辑要利用函数指针(一个信号对应一个函数,就类似Qt中将信号和槽绑定一起的connect)

回顾  int kill(pid_t pid, int sig);

进程可以通过调用kill向包括它本身在内的另一个进程发送信号。如果程序没有发送该信号的权限,对 kill的调用就将失败。

kill函数的作用是把参数sig给定的信号发送给标识号为pid的进程。 要想发送一个信号,发送者进程必须拥有相应的权限。这通常意味着两个进程必须拥有同样的用户ID

三、代码及实现效果

1、示例代码

#include <unistd.h>//
#include <string.h>
#include <sys/types.h>
#include<iostream>
#include <signal.h>

using namespace std;

void signalFunc(int i);
int main(){
	
	int pid = 0;
	//将信号和函数进行绑定,这个signal再开启子进程的时候会给拷贝走(这样子进程才能认识这个信号以及中断处理的函数)
	signal(SIGUSR1, signalFunc);
	signal(SIGUSR2, signalFunc);
	pid = fork();

	if (pid == 0)
	{
		//信号产生进程中断正在执行的业务
		//立刻执行信号对应的函数逻辑
		//执行以后回到原来的业务继续执行

		while (true)
		{
			cout << "子进程运行中 ...pid =" << getpid()<< endl;
			sleep(1);
		}
	}
	else if(pid >0)
	{
		//睡一会,等孩子运行起来的时候再发送信号(创造比子进程晚一点 )
		sleep(5);

		//父亲给孩子发送信号
		kill(pid,SIGUSR1);
		sleep(5);
		kill(pid, SIGUSR2);
		while (true)
		{
			cout << "父进程运行中 ...pid =" << getpid() << endl;
			sleep(1);
		}
	}
	return 0;
}
//信号对应的函数,参数对应的就是信号编码
void signalFunc(int i)
{
	cout << "函数被调用了signalFunc i=  " << i << endl;
}
	

因为每个进程都会有对应的PCB,代码段,数据段和堆栈段等,fork ()创建的子进程自然也有代码段,通过拷贝一份父进程代码段(即整个cpp文件的内容,自然fork ()之前的signal也会给拷贝走,这样子进程才能认识这个信号以及中断处理的函数),所以就能进行信号的接收和发送的动作

2、效果

①一个信号对应一个函数

为什么i打印出来是10呢?因为信号对应的函数,参数对应的就是信号编码,传递的是SIGUSR1对应10

② 两个信号对应一个函数(signal两次)

 ③父进程连续发送5次信号

两个不同的信号可以绑定同一个函数,多个信号也一样   

④ 删去sleep,连续发送5次信号,就收到一次 i=10

没有sleep则一下子发送五次信号,但是就收到了一次。区别就在于这个sleep,有sleep表示发送一次信号休息一会,五次都能收到。

⑤同样没有sleep的情况,此时我们换成发送34号信号(可靠信号),如下图

 没有sleep,却连续接收到5次,这也就是可靠信号和不可靠信号的差别

总结:

可靠信号一定会被发送成功,且一定被收到。不可靠信号在连续不中断的情况下(没有sleep)发送下会丢失

原创不易,转载请注明出处:

基于VS2019 C++的跨平台(Linux)开发(1.4)——信号

下面进入信号第二部分的学习:

基于VS2019 C++的跨平台(Linux)开发(1.4.2)——信号

猜你喜欢

转载自blog.csdn.net/hml111666/article/details/123561522