ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

【linux进程间通信】1.信号

2021-07-31 13:59:03  阅读:127  来源: 互联网

标签:set 函数 int 间通信 信号 linux 进程 include


一、何为进程间通信

1.进程是一个独立的资源分配单元,不同进程(用户进程)之间的资源是独立的,没有关联,不能再一个进程中访问别的进程的资源。而实际情况下不同进程间常常需要交互来传递状态等信息,所以需要进程间的通信。

2.进程间通信功能:

        数据传输:一个进程需要将它的数据发送给另一个进程。

        资源共享:多个进程之间共享同样的资源。

        通知事件:一个进程需要向另一个或一组进程发送消息,通知它们发生了某种事件。

        进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有操作,并能够及时知道它的状态改变。

 3.进程间通信的实质:系统只要创建一个进程,就会给当前进程分配4G的虚拟内存(32位),虚拟内存不是常说的内存条的空间(物理空间),虚拟内存和物理内存之间存在映射关系

4G的虚拟内存分为3G的用户空间(0~3G)和1G ( 3~4G)的内核空间,用户空间是进程所私有的,每一个进程的用户空间只能自己访问和使用,我们之前说的栈区、堆区、数据区、代码区等都是用户空间的区域

内核空间是所有进程所公有的,也就意味着绝大多数进程间通信方式,本质就是对内核空间的操作

4.

socket通信可以实现不同主机的进程间通信,其他六个只能在一台主机的多个进程间通信

信号通信是唯一的一种异步通信机制

共享内存是所有进程间通信方式中效率最高的,它是直接对物理内存进行操作

二、信号

1.信号是软件中断,它是在软件层次上对中断机制的一种模拟。信号可以导致一个正在运行的进程被另一个正在运行的异步进程中断,转而处理某一个突发事件。信号是一种异步通信方式。

进程不必等待信号的到达,进程也不知道信号什么时候到达。信号可以直接进行用户空间进程和内核空间进程的交互,内核进程可以利用它来通知用户空间进程发生了哪些系统事件。

2.每个信号的名字都以字符SIG开头。每个信号和一个数字编码相对应,在头文件signum.h中,这些信号都被定义为正整数。信号名定义路径:

          /usr/include/x86_64-linux-gnu/bits/signum.h

        /usr/include/x86_64-linux-gnu/bits/signum-generic.h

3.在linux可以使用kill -l 查看所有信号与编号的关系

4.信号是由当前系统已经定义好的一些标识,每一个标识都会在特定的场合使用并且都会对进程有一定的影响,当信号产生时,会让当前信号做出相应的操作

5.产生信号的方式

        5.1 某些终端键会发送信号

        5.2硬件异常会产生信号

        5.3软件异常产生信号

        5.4运行kill命令会产生信号

        5.5调用kill函数可以发送信号

6.信号的处理方式

6.1当进程中产生了一个信号,就会让当前进程做出一定的反应,默认的处理方式如下(其中一种):

  • 终止进程:当信号产生后,当前进程就会立即结束
  • 缺省处理:当信号产生后,当前进程不做任何处理
  • 停止进程∶当信号产生后,使得当前进程停止
  • 让停止的进程回复运行:当信号产生后,停止的进程会回复执行(后台进程

6.2进程接收到信号后的处理方式

  • 执行上述6.1的默认操作
  • 忽略该信号
  • 执行自定义的信号处理函数

注:SIGKILL和SIGSTOP两个信号只能默认处理,无法忽略也无法自定义

6.3常见的信号

信号性质默认处理方式
SIGKILL9当产生这个信号后,当前进程会退出,不能被缺省和捕捉退出进程
SIGSTOP19当产生这个信号后,当前进程会停止,不能被缺省和捕捉停止进程
SIGINT2键盘输入ctrl+c时产生信号退出进程
SIGQUIT3键盘输入ctrl+\时产生信号退出进程
SIGTSTP20键盘输入ctrl+z时产生信号停止进程
SIGCONT18当产生当前信号后,当前停止的进程会恢复运行停止的进程恢复运行
SIGALRM14当调用alarm函数设置的时间到达时会产生当前信号退出进程
SIGPIPE13当管道破裂时,会产生当前信号退出进程
SIGABRT6当调用abort函数时会产生当前信号退出进程
SIGCHLD17当使用fork创建一个子进程时,如果子进程状态改变(退出),会产生当前信号缺省
SIGUSR110用户自定义信号,不会自动产生,只能使用kill函数或者命令给指定的进程发送当前信号缺省
SIGUSR212用户自定义信号,不会自动产生,只能使用kill函数或者命令给指定的进程发送当前信号缺省

 6.4kill函数

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

      int kill(pid_t pid, int sig);

 6.4.1功能:给制定进程发送信号

6.4.2参数:

pid:

        pid>0∶将信号传送给进程ID为pid的进程。

        pid=0:将信号传送给当前进程所在进程组中的所有进程。

        pid=-1:将信号传送给系统内所有的进程。

        pid<-1:将信号传给指定进程组的所有进程。这个进程组号等于pid的绝对值。

signum:信号的编号

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

6.4.4实例

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
int main(){
	pid_t pid = fork();
	if(pid<0){
		perror("file  to fork");
		return -1;
	}
	if(pid==0){

		printf("*-son process-*\n");
		sleep(2);
		kill(getppid(),SIGKILL);
	}
	else{
		for(int i = 0;i<5;i++){
			printf("-****parent***-\n");
			sleep(1);
		}
	}
	return 0;
}

6.4.5kill命令用法

        kill -信号编号 进程号

 6.5alarm函数

       #include <unistd.h>

       unsigned int alarm(unsigned int seconds);

6.5.1 功能:在秒数到达后,向调用的进程发送一个SIGALARM信号

6.5.2参数seconds:设定的秒数

6.5.3返回值:

        若之前alarm函数没有alarm设置,则返回0

        如果有,则返回上一个alarm剩余时间

6.5.4实例

        int sec;
		sec = alarm(5);
		printf("sec = %d\n",sec);//此处sec=0
		for(int i = 0;i<4;i++){
			printf("*-son process-*\n");
			sleep(1);
		}
		sec = alarm(3);
		printf("sec=%d\n",sec);//此处sec=1
/*****************************************************
*之前设置过alarm且时间没有到再设置一次,alarm时间会重置,
*返回值是上次alarm剩余的秒数
****************************************************/

6.6.raise函数

       #include <signal.h>

       int raise(int sig);

6.6.1功能:给调用进程本身发送一个信号

6.6.2参数:signum信号编号

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

6.6.4raise(sig)<==>kill(getpid(),sig)

6.6.5这么简单就不放实例了

6.7abort函数

#include <stdlib.h>
void abort(void);

6.7.1功能:向进程发送一个SIGABRT信号,默认情况下进程会退出

6.7.2注意事项:及时SIGABRT信号被加入了阻塞集,一旦进程调用了abort函数,进程也还是会被终止,且在终止前会刷新缓冲区,关闭文件描述符

6.7.3调用方式:直接在代码上加上abort();就完事了

6.8 pause函数

#include <unistd.h>
int pause(void);

6.8.1功能:将调用进程挂起直到捕捉到信号位置,这个函数常用来判断信号是否接收到

6.8.2返回值:直到捕捉信号pause返回-1,且ERRNO为EINTR

6.8.3实例

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
int main(){
	pid_t pid = fork();
	if(pid<0){
		perror("file  to fork");
		return -1;
	}
	if(pid==0){
		for(int i = 0;i<4;i++){
			printf("*-son process-*\n");
			sleep(1);
		}
		kill(getppid(),SIGINT);
	}
	else{
			printf("-****parent***-\n");
			pause();
	}
	return 0;
}

6.9signal函数

#include <signal.h>

typedef void (*sighandler_t)(int);
sighandler_t signal(int signum,sighandler_t handler);

6.9.1功能:为信号设置处理函数,相当于中断中的中断处理函数

6.9.2参数

        signum:信号编号

        handler:

                       忽略信号:SIG_IGN

                       执行默认:SIG_DFL

                       自定义处理函数:函数入口地址(函数名) 

6.9.3返回值:

        成功:返回函数地址,该地址为上一次注册的信号处理函数的地址

        失败:返回SIG_ERR

6.9.4实例

注意示例中的注释

/********************************
*该例程主要演示signal在进程间通信间的使用
*以及自定义信号处理函数的使用方法
*若是不自定义第二个参数可直接代入另外两个宏
*此处不做这方面的演示
********************************/
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>

void func1(int sig){	//信号处理函数  返回值为空,参数sig为接收到信号的信号值
	if(sig == SIGUSR1)        //经过判断可以用同一个信号处理函数来处理多个信号
		printf("-*****using sigusr1****-\n");
	else
		printf("-******no sigusr1******-\n");
}
int main(){
	pid_t pid = fork();
	if(pid<0){
		perror("fail  to fork");
		return -1;
	}
	if(signal(SIGUSR1,func1)==SIG_ERR){//注册信号处理函数
		perror("fail to signal");
		return -1;
	}

	if(pid==0){
		pause();//挂起等待父进程给信号
		for(int i = 0;i<3;i++){
			printf("*-son process-*\n");
			sleep(1);
		}
		kill(getppid(),SIGUSR1);//给父进程发送信号
		sleep(2);
		exit(2);
	}
	else{
		printf("-****parent***-\n");
		sleep(1);//此处睡眠一秒是因为等待一下保证子进程已经挂起在等待信号,
                //然后再将信号发送过去
		kill(pid,SIGUSR1);//给子进程发送信号
		pause();//挂起
		printf("-*****parent****-\n");
		wait(NULL);//等待子进程退出
	}
	return 0;
}

/********************************
*该例程主要演示signal返回值的应用
********************************/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>

void *handler;
void func1(int sig){	
	if(sig == SIGINT){
		if(signal(SIGINT,handler)==SIG_ERR){
			perror("fail to signal");
			exit(3);
		}
		printf("-************************-\n");
		printf("-*****using sigusr1****-\n");
		printf("-************************-\n");
		sleep(1);
	}
	else
		printf("-******no sigusr1******-\n");
}
int main(){
	if((handler = signal(SIGINT,func1))==SIG_ERR){
		perror("fail to signal");
		return -1;
	}
	while(1){
		printf("*-----main------*\n");
		sleep(1);
	}
	return 0;
}

三、可重入函数

可重入函数是指函数可以由多个任务并发使用,而不必担心数据错误

可重入函数就是可以被中断的函数,当前函数可以再任何时刻中断它,并执行另一块代码,当执行完毕后,回到原本的代码还可以正常的执行。

编写可重入函数的注意点:

  1.  不使用(返回)静态数据、全局变量。
  2. 不调用动态分配内存、释放的函数
  3. 不调用任何不可重入的函数(如标准io函数)

进入处理函数时,首先要保存好errno的值,结束时在恢复原值。

常见的可重入函数

 应用就是在被信号中断后可以回到函数原位置继续执行

四、信号集

1.一个用户进程常常需要对多个信号作出处理,为了方便对多个信号进行处理,在linux系统中引入了信号集,信号集是用来表示多个信号的数据类型

2.信号集的数据类型 sigset_t

3.信号集操作函数

3.1sigemptyset函数

#include <signal.h>
int sigemptyset(sigset_t *set);

功能:初始化set指向的信号集,清除其中所有的信号

参数:set:信号集标识的地址

返回值:成功0失败-1

3.2sigfillset函数

#include<signal.h>
int sigfillset(sigset_t *set);

功能:初始化信号集set,将信号集设置为所有信号的集合

参数返回值同上

3.3sigismenber函数

#incldue <signal.h>
int sigismember(const sigset_t *set,int signum);

功能:查询signum表示的信号是否在信号集set中

参数:signum:信号的编号

返回值:找到了返回1,找不到返回0,错误返回-1

3.4sigaddset函数

#include <signal.h>
int sigaddset(sigset_t *set,int signum);

功能:将signum所指信号添加到信号集set中

返回值:成功0失败-1

3.5sigdelset函数

#include <signal.h>
int sigdelset(sigset_t *set,int signum);

功能:将signum所指信号从信号集set中删除

返回值:成功0失败-1

五、信号阻塞集

1.每个进程都有一个阻塞集,他用来描述那些信号递送到该进程的时候被阻塞(在信号发生时记住它 ,直到进程准备好了是再将信号通知进程)。所谓阻塞并不是禁止传送信号,而是暂缓信号的传送,若将被阻塞的信号从信号阻塞集中删除,且对应的信号在被阻塞时发生了,进程将会收到相应的信号。

2.信号阻塞集的相关函数:sigprocmask函数

#include <signal.h>
int sigprocmask(int how,const sigset_t *set,sigset_t *oldset);

功能:检查或修改信号阻塞集,根据how指定的方法对进程的阻塞集和进行修改,新的信号阻塞集由set指定,而原先的信号阻塞集由oldset保存

参数:

        how:信号阻塞集的修改方法,可选参数如下

                SIG_BLOCK:向信号阻塞集中添加set信号集

                SIG_UNBLOCK:向信号阻塞集中删除set信号集

                SIG_SETMASK:将信号阻塞集设为set集合

        set:要操作的信号集地址

        oldset:保存原来的信号阻塞集的地址

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

 3.实例

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

int main(){
	sigset_t myset;
	sigemptyset(&myset);
	sigaddset(&myset,SIGINT);
	
	sigprocmask(SIG_BLOCK,&myset,NULL);

	for(int i = 0;i<5;i++)
	{
		printf("hello world!\n");
		sleep(1);
	}
	/************************************************************
    *在前5秒ctrl+c的话不会马上中断打印,五秒后将信号从阻塞集中移除
    *就会马上处理之前被阻塞的信号,所以会马上退出进程
    *************************************************************/
	sigprocmask(SIG_UNBLOCK,&myset,NULL);
	for(int i = 0;i<5;i++){
		printf("hello unblock!\n");
		sleep(1);
	}
    /************************************************************
    *在后5秒ctrl+c的话会马停止进程,此时SIGINT信号已经移出阻塞集了
    *************************************************************/
	return 0;
}

标签:set,函数,int,间通信,信号,linux,进程,include
来源: https://blog.csdn.net/m0_47458160/article/details/119243757

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有