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

接上一篇文章本章先来学习带参数的信号以及sigqueue、sigaction等函数

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

一、引言

上篇文章我们提到的kill函数发送信号只是针对某一个进程,并没有体现带参数。之前也提到进程中不能进行数据共享,即使定义一个全局变量,在子进程间进行修改,在父进程里并不知道修改了没有。所以这种情况就需要IPC通信来解决,而kill不能传递数据,那么就需要使用带参数的信号

二、函数原型

1、sigaction库函数——绑定信号

功能:用于改变进程接收到特定信号后的行为。

头文件 <signal.h>

原型: int  sigaction(int signum,const struct sigac        tion *act,const struct sigaction *old);

参数

  • 第一个参数为信号的值,可以为除sigkill及sigstop外的任何一 个特定有效的信号(为这两个信号定义自己的处理函数,将导致信号安装错误)
  • 第二个参数是指向结构sigaction的一个实例的指针,在结构 sigaction的实例中,指定了对特定信号的处理,可以为空,进程会以缺省方式对信号处理
  • 第三个参数oldact指向的对象用来保存原来对相应信号的处理,通常为null

注意:参数二、三都是结构体指针。参数二最为重要,在这个结构体中 包含了信号处理函数,但是在使用的时候不能直接把函数赋值给第二个参数

返回值:函数成功返回0,失败返回-1

linux中查看sigaction函数如下

对应的第二个参数sigaction的结构体如下

        c结构体中不能有函数,但这里只是保留函数指针,并没有破坏结构体中不能有函数的规定,对于结构体而言,它只是一个指针变量(比较特殊,指向函数)

        其中第一个类似于之前提到的不带参数的函数指针,所以我们使用的是第二个函数实现带参数的信号,然后由sa_flags来判断是否带参数

2、 sigqueue库函数——发送信号

功能:新的发送信号系统调用,主要是针对实时信号提出的支持信号带有参数,与函数sigaction()成对出现使用。

原型:     int sigqueue(pid_t pid, int sig, const union sigval value);

参数  

  • 第一个参数是指定接收信号的进程id
  • 第二个参数确定即将发送的信号
  • 第三个参数是一个联合数据结构union sigval(类似结构体 ,很少用),指定了信号传递的参数,即通常所说的4字节值。

返回值:成功返回0,失败返回-1

linux中查看sigqueue函数如下 

对应的第二个参数的sigval联合体如下 

 其中 sigval_int就是要发送的整形数据

三、实现

1、示例代码

该例子实现父进程发送信号并传递数据给子进程

①之前的void signalFunc(int i) 只适用于无参信号的使用,所以重新定义的函数如下

void sigaction_func(int num, siginfo_t* info, void* vo)

第一个参数num就是收到的信号编码,收到的数据存在第二个参数info中

②sigqueue发送的数据必须是联合体的形式

#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);
int main(){
    int pid = 0;
	//准备结构体
	struct sigaction  act;
	act.sa_sigaction = sigaction_func;
	act.sa_flags = SA_SIGINFO;//带参数的信号
	//绑定信号
	sigaction(SIGUSR1,&act,NULL);  //发送是sigqueue
	
	pid = fork();

	if (pid == 0)
	{
		while (true)
		{
			cout << "子进程运行中 ...pid =" << getpid() << endl;
			sleep(1);
		}
	}
	else if (pid > 0)
	{
		sleep(2);
		//准备要传递的数据
		union sigval value;
		value.sival_int = 111;
		//带参数发送信号
		sigqueue(pid, SIGUSR1, value);//第三个参数跟之前不一样,是一个联合体
		//卡住父进程
		while (1) {}
	}

	return 0;
}

void sigaction_func(int num, siginfo_t* info, void* vo)
{
	int x = info->si_int;
	cout <<"当前进程pid = "<<getpid()<<"  sigaction_func函数被调用了,num =  " << num << "  信号传递过来的数据是 x =  " << x<< endl;
}

2、运行结果

四、总结

前面例子中传递的数据只是一个整形,那么有的人可能注意到sigval联合体中的第二个参数void *(如下图),以为可以利用它来实现其他类型的数据,但并非如此。

1、在最早开发者开发信号的时候只支持int类型的数据传递,就想到预留一个void * 以便后续使用,但是遗憾的是到现在ubuntu20版本中还是不支持void *的数据传递,还没有进行更新,所以复杂点的数据(数组、结构体)就没法传递 ,就显得非常鸡肋。

2、因为IPC有五种通信方式,并不是只能用信号来实现进程间传递数据,因为信号麻烦的操作导致它只是通信的基础使用

3、当子进程接收到一个不认识的信号(比如把上述代码中sigqueue(pid, SIGUSR1, value);中的SIGUSR1改成SIGUSR2),而且没有进行绑定函数,运行结果如下,子进程不知道做什么事情就会被卡死

linux中再进行编译

此时,查看进程状态为Z+(僵尸状态——子进程先于父进程结束。子进程中有死循环,本不应该先结束,但因为子进程收到了一个不认识的信号就会被打断,子进程卡死在那,即原来本的事情不做,接下来也不懂做什么,就进入了僵尸状态,占用资源,应该避免的)

4、当我只有一个进程的时候,在linux中用kill给他发送信号

 进程收到一个不认识的信号(10号信号),打印出用户自定义信号1,查看进程状态,发现找不到这个pid,即被中断销毁,就像起到了杀死进程的作用(前面的父进程给子进程发送不认识的信号,只是卡死)。所以要保证进程的正常执行,就需要使用信号屏蔽
 

小试牛刀

 

装载请注明出处

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

学习信号屏蔽如下

https://blog.csdn.net/hml111666/article/details/123683046

猜你喜欢

转载自blog.csdn.net/hml111666/article/details/123564522
今日推荐