C++跨平台开发——带参数信号实现进程间数据传递

一、实现要求

二、实现思路

大致框架:

 信号绑定函数(触发函数)如下:

1、确定A\B\C\D四个进程的关系是父子关系:A fork B、B fork C、C fork D(或者A是父亲,BCD都是A的孩子;再或者main函数是父亲,ABCD是兄弟也可以)

2、此时D就是A的重孙子,可以看出B、C既有父亲也有孩子;A有孩子没有父亲;D有父亲没有孩子。那么怎么区分B、C呢?因为触发函数中有num(收到的信号编号),B、C收到的信号不同,所以可以根据num判断(num为10表示信号SIGUSER1判断是B进程、num为12判断是C进程,注意不能直接判断num是否等于12,因为可能是C也可能是A),这些都是放在信号绑定函数中操作

3、可以区分A\B\C\D四个进程,接下来就好办了。用两个变量存四个进程的父亲或者孩子,比如A有孩子没有父亲,只要存孩子的pid;B、C有孩子都要存孩子pid(因为发送都是针对孩子);D有父亲没有孩子存父亲pid。接着就能在信号绑定函数判断这两个变量,从而知道是哪个进程发送什么信号,但是D进程给A进程发送信号就不能直接利用父亲或者孩子变量,而是要使用getpgid函数返回一个进程组组长ID(A进程ID)。

4、绑定和发送信号

  • 绑定信号sigaction,在fork之前(注意不能放在进程开始之前,否则4个进程都会有4个信号,第一,会产生信号冲突,还要进行信号屏蔽;第二,会占用更多内存空间,即A进程也会认识信号SIGRTMIN等,没有意义)
  • A用sigqueue发送信号在main中,B、C、D发送信号在信号绑定函数中

经典错误:

①在子进程或者父进程中发送信号(可以直接在触发函数中发送 )

②认为有4个进程要转接4次,写了4个触发函数(函数中都是收到数据+1发送给下一个,他们的操作的逻辑一样,可以直接封装成一个信号绑定函数)

三、代码

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

//信号屏蔽
#include <signal.h>
#include<stdio.h>
#include <stdlib.h>
using namespace std;

//带参数的信号
void sigaction_func(int num, siginfo_t* info, void* vo);

//为了区分 保存父亲和孩子(fork之后也会拷贝走)
int father_flag = 0;
int child_flag = 0;
pid_t pid = 0;

int main()
{
	

	pid = fork();

	if (pid > 0)//A进程  绑定SIGUSR2  发送SIGUSR1
	{
		struct sigaction sig_act2;
		sig_act2.sa_sigaction = sigaction_func;//绑定函数
		sig_act2.sa_flags = SA_SIGINFO;//带参数
		sigaction(SIGUSR2, &sig_act2, NULL);

		cout << "A进程 pid = " << getpid() << endl;

		child_flag = pid;//father_flag = 0
		sleep(3);
		union sigval sig_val;
		sig_val.sival_int = 1001;
		sigqueue(child_flag, SIGUSR1, sig_val);
		while (true)
		{

		}
	}
	else if (pid == 0)//B进程 发送SIGUSR2
	{
		cout << "B进程 pid = " << getpid() << endl;

		pid = fork();
		if (pid > 0)
		{
			struct sigaction sig_act2;
			sig_act2.sa_sigaction = sigaction_func;//绑定函数
			sig_act2.sa_flags = SA_SIGINFO;//带参数
			sigaction(SIGUSR1, &sig_act2, NULL);

			child_flag = pid;
			father_flag = getppid();
			while (true)
			{

			}
		}
		else if (pid == 0)//C进程   发送SIGRTMIN
		{
			struct sigaction sig_act2;
			sig_act2.sa_sigaction = sigaction_func;//绑定函数
			sig_act2.sa_flags = SA_SIGINFO;//带参数
			sigaction(SIGUSR2, &sig_act2, NULL);

			cout << "C进程 pid = " << getpid() << endl;

			pid = fork();
			if (pid > 0)
			{
				child_flag = pid;
				father_flag = getppid();
				while (true)
				{

				}
			}
			else if (pid == 0)//D进程   绑定SIGRTMIN 发送SIGUSR2
			{
				struct sigaction sig_act2;
				sig_act2.sa_sigaction = sigaction_func;//绑定函数
				sig_act2.sa_flags = SA_SIGINFO;//带参数
				sigaction(SIGRTMIN, &sig_act2, NULL);

				cout << "D进程 pid = " << getpid() << endl;

				
				father_flag = getppid();
				while (true)//死循环避免孤儿进程或者僵尸进程  
				{

				}
			}
			
		}
	}

	

	return 0;

}

//信号对应的函数,参数对应的就是信号编码
void signalFunc(int i)
{
	cout << "函数被调用了signalFunc i=  " << i << endl;
}

void sigaction_func(int num, siginfo_t* info, void* vo)
{
	
	//cout << "触发函数被调用了sigaction_func num=  " << num << "info->si_int= " << info->si_int << endl;

	int x = info->si_int;//保存传递的数据
	
	if (father_flag != 0 && child_flag != 0)//B或者C进程
	{
		if (num == 10)
		{
			cout << "进程pid =  " << getpid() << "	数据x= " << x << endl;
			union sigval sig_val;
			sig_val.sival_int = x + 1;//加1操作
			sigqueue(child_flag,SIGUSR2, sig_val);//发送SIGUSR2信号
		}
		else if (num == 12)
		{
			cout << "进程pid =  " << getpid() << "	数据x= " << x << endl;
			union sigval sig_val;
			sig_val.sival_int = x + 1;//加1操作
			sigqueue(child_flag, SIGRTMIN, sig_val);//发送SIGRTMIN信号
		}
	}
	else if (father_flag == 0 && child_flag != 0)//A进程
	{
		//只要接收信号即可
		cout << "进程pid =  " << getpid() << "	数据x= " << x << endl;
	}
	else if (father_flag != 0 && child_flag == 0)//D进程
	{
		cout << "进程pid =  " << getpid() << "	数据x= " << x << endl;
		if (num == 34)
		{
			union sigval sig_val;
			sig_val.sival_int = x + 1;//加1操作
			//getpgid返回一个进程组组长ID ——A进程
			sigqueue(getpgid(father_flag), SIGUSR2, sig_val);//发送SIGUSR2信号
		}
		else
		{
			cout << "这里不会被执行 " << endl;
		}

	}

	
	
	
}

四、代码分析以及运行结果

1、进程A一定是先开启的,B、C、D进程都是A开启过程中才陆续开启,即使可以认为是同步执行,但是按毫秒级别上来看,还是有一定的差距。所以在A进程中做一个延时(slepp),目的是为了让B、C、D进程陆续创建完成并打开,信号全部绑定成功。否则在A进程里面马上发送信号就会失败(没绑上就发了,B进程根本接收不到)

2、每一个进程都要加一个死循环,让他们不会走到return退出,避免出现孤儿进程或者僵尸进程(A是最早发送信号也是最晚接收信号的,如果这个过程B、C、D进程其中一个进程结束就会导致A收不到信号)

3、注意,点击运行后出现如下图的异常(用户定义的信号有2个),控制台未打印出A进程收到的数据(即A进程未收到信号),这时候可能就懵逼了,因为A进程就绑定了一个信号,那么为什么会出现2个呢,该如何解决呢。

  • VS2019运行起来就是进程,VM虚拟机运行起来也是一个进程,这两个进程跨平台通信,如果在VS2019代码运行失败出现问题(比如这个异常),就会从虚拟机中抛出错误发送到VS2019,这个过程就叫做IPC通信(即两个进程同时运行的情况下还能传递数据)。
  • 因为我们使用的是gbd调试工具,这个gdb通过在VS中打断点去接收虚拟机发送的信号,也就是ubuntu虚拟机在编译c++之后,如果产生异常,通过信号的方式返回到VS。实质这个异常不是一个错误,因为同时有两个信号(gdb、SIGUSR2)从ubuntu中发给VS的A进程发送了冲突,导致A进程不知所措,不知道先处理哪个信号。
  • 这时你可以会想用信号屏蔽的方式解决信号冲突,但两个信号都是必须接收的,加上你也不知道gdb是哪种信号,就算你知道gdb是哪种信号,你如果把他屏蔽了就用不了gdb调试工具,就会导致工程在VS中运行不起来,所以不可行。
  • 解决:只要点击VS菜单栏上的“继续”,就会走过gdb、SIGUSR2两个信号。因为代码没有报错,不需要处理gdb信号,只会处理SIGUSR2信号。此时A进程就会收到SIGUSR2信号,在控制台打印出数据

 

 转载请注明出处

C++跨平台开发——带参数信号实现进程间数据传递

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ze言

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值