进程间通信之信号

这篇具有很好参考价值的文章主要介绍了进程间通信之信号。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. 信号

什么是信号?
信号是给程序提供一种可以处理异步事件的方法,它利用软件中断来实现。不能自定义信号,所有信号都是系统预定义的。

2. 信号由谁产生?

  • 由shell终端根据当前发生的错误(段错误、非法指令等)Ctrl+c而产生相应的信号

比如:socket通信或者管道通信,如果读端都已经关闭,执行写操作(或者发送数据),将导致执行写操作的进程收到SIGPIPE信号(表示管道破裂)
该信号的默认行为:终止该进程。

  • 在shell终端,使用kill或killall命令产生信号
eg:
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

void myhandle(int sig)
{
    printf("Catch a signal : %d\n", sig);
}

int main(void)
{
    // ctrl+c 触发信号 SIGINT
    signal(SIGINT, myhandle);
    while (1)
    {
        sleep(1);
        printf("sleep 1 second.\n");
    }
    return 0;
}
kill  -HUP  13733     /* 向PID为13733的进程发送SIGHUP */ 

3. 有哪些信号


-------------------------------------------
信号名称 			说明
-------------------------------------------
SIGABORT		进程异常终止
SIGALRM 	    超时告警
SIGFPE 			浮点运算异常
SIGHUP 			连接挂断
SIGILL 		    非法指令
SIGINT 			终端中断  (Ctrl+C将产生该信号)
SIGKILL 	    *终止进程                             
SIGPIPE         向没有读进程的管道写数据
SIGQUIT 		终端退出(Ctrl+\将产生该信号)
SIGSEGV 		无效内存段访问
SIGTERM 		终止
SIGUSR1         *用户自定义信号1
SIGUSR2 	    *用户自定义信号2 
-------------------------------------->以上信号如果不被捕获,则进程接受到后都会终止!
SIGCHLD 		子进程已停止或退出
SIGCONT 	    *让暂停的进程继续执行
SIGSTOP 		*停止执行(即“暂停")
SIGTSTP 		中断挂起
SIGTTIN 		后台进程尝试读操作
SIGTTOU 		后台进程尝试写
-------------------------------------------
  • 信号的处理

    • 忽略此信号
    • 捕捉信号,指定信号处理函数进行处理
    • 执行系统默认动作,大多数都是终止进程
  • 信号的捕获

    • 信号的捕获,是指,指定接受到某种信号后,去执行指定的函数。
    • 注意:SIGKILL和SIGSTOP不能被捕获,即,这两种信号的响应动作不能被改变。

4. 信号的安装

  1. signal
signal的参数2可去以下特殊值:
        SIG_IGN     忽略信号
        SIG_DFL     恢复默认行为
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

void myhandle(int sig) 
{
	printf("Catch a signal : %d\n", sig);
}

int main(void) 
{
	signal(SIGINT, myhandle);
	while (1) {
        sleep(1);
	}

	return 0;
}
  1. 使用sigaction (项目实战强烈推荐使用)
    sigaction与signal的区别: sigaction比signal更“健壮”,建议使用sigaction
struct sigaction {
   void (*sa_handler)(int);   /* 信号的响应函数 */
   sigset_t   sa_mask;          /* 屏蔽信号集 */                         
   int sa_flags;                /* 当sa_flags中包含 SA_RESETHAND时,接受到该信号并调用指定的信号处理函数执行之后,把该信号的响应行为重置为默认行为SIG_DFL */
   ...
}
补充:
        当sa_mask包含某个信号A时,则在信号处理函数执行期间,如果发生了该信号A,
         则阻塞该信号A(即暂时不响应该信号),直到信号处理函数执行结束。
         即,信号处理函数执行完之后,再响应该信号A
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

void myhandle(int sig) 
{
	printf("Catch a signal : %d\n", sig);
}

int main(void) 
{
	struct sigaction act;

	act.sa_handler = myhandle;
	sigemptyset(&act.sa_mask);
    //act.sa_flags = 0;//可以接收无数次ctrl+c
    act.sa_flags = SA_RESETHAND;//只能接收一次ctrl+c

	sigaction(SIGINT, &act, 0);

	while (1) {
        sleep(1);
        printf("sleep 1 second.\n");
	}

	return 0;
}

5. 信号的发送

信号的发送方式:
	      在shell终端用快捷键产生信号
	      使用kill,killall命令。
	      使用kill函数和alarm函数

1) 使用kill函数

main6.c创建一个子进程,子进程每秒中输出字符串“child process work!",父
进程等待用户输入,如果用户按下字符A, 则向子进程发信号SIGUSR1, 子进程
的输出字符串改为大写; 如果用户按下字符a, 则向子进程发信号SIGUSR2, 
子进程的输出字符串改为小写
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

int workflag = 0;

void work_up_handle(int sig) 
{
	workflag = 1;
}

void work_down_handle(int sig) 
{
	workflag = 0;
}



int main(void) 
{
	pid_t pd;
	char c;


	pd = fork();
	if (pd == -1) {
		printf("fork error!\n");
		exit(1);
	} else if (pd == 0) {
		char *msg;
		struct sigaction act; 
		act.sa_flags = 0;
		act.sa_handler = work_up_handle;
		sigemptyset(&act.sa_mask);		
		sigaction(SIGUSR1, &act, 0);
		
		act.sa_handler = work_down_handle;
		sigaction(SIGUSR2, &act, 0);
		
		while (1) {
			if (!workflag) {
				msg = "child process work!";
			} else {
				msg = "CHILD PROCESS WORK!";
			}
			printf("%s\n", msg);
			sleep(1);
		}
		
	} else {
		while(1) {
			c = getchar();
			if (c == 'A') {
				kill(pd, SIGUSR1);
			} else if (c == 'a') {
				kill(pd, SIGUSR2);
			}
		}
	}
	

	return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

int wakeflag = 0;

void wake_handle(int sig) 
{
	wakeflag = 1;
}

int main(void) 
{
	pid_t pd;
	char c;


	pd = fork();
	if (pd == -1) {
		printf("fork error!\n");
		exit(1);
	} else if (pd == 0) {//子进程
		while(1){
			sleep(5);
			kill(getppid(), SIGALRM);//getppid()得到父进程的id号,SIGALRM闹钟信号
		 }
	} else {
		struct sigaction act; 
		act.sa_handler = wake_handle;
		act.sa_flags = 0;
		sigemptyset(&act.sa_mask);

		sigaction(SIGALRM,  &act, 0);
		while(1){
			pause(); //把该进程挂起,直到收到任意一个信号
	
			if (wakeflag) {
				printf("Alarm clock work!!!\n");
			}
		}
	}

	return 0;
}

2)使用alarm函数

作用:在指定时间之内给该<进程本身>发送一个SIGALRM信号。
用法:man 2 alarm
注意:时间的单位是“秒” 
         实际闹钟时间比指定的时间要大一点。  
         如果参数为0,则取消已设置的闹钟。
         如果闹钟时间还没有到,再次调用alarm,则闹钟将重新定时
         每个进程最多只能使用一个闹钟。

返回值:
         失败:返回-1
         成功:返回上次闹钟的剩余时间(秒)
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>

int wakeflag = 0;

void wake_handle(int sig) 
{
	wakeflag = 1;
}

int main(void) 
{
	int ret;
	
	struct sigaction act;
	act.sa_flags = 0;
	act.sa_handler = wake_handle;
	sigemptyset(&act.sa_mask);
	sigaction(SIGALRM, &act, 0);
	
	printf("time =%ld\n", time((time_t*)0));

	ret = alarm(5);
	if (ret == -1) {
		printf("alarm error!\n");
		exit(1);
	}

	//挂起当前进程,直到收到任意一个信号
	pause();

	if (wakeflag) {
		printf("wake up, time =%ld\n", time((time_t*)0));
	}

	return 0;
}

3) 使用raise

    给本进程自身发送信号。
    原型: int  raise (int sig)

6.发送多个信号

某进程正在执行某个信号对应的操作函数期间(该信号的安装函数),如果此时,该进程又多次收到同一个信号(同一种信号值的信号),
则:如果该信号是不可靠信号(<32),则只能再响应一次。
如果该信号是可靠信号(>32),则能再响应多次(不会遗漏)。但是,都是都必须等该次响应函数执行完之后,才能响应下一次。

某进程正在执行某个信号对应的操作函数期间(该信号的安装函数),如果此时,该进程收到另一个信号(不同信号值的信号),则:
如果该信号被包含在当前信号的signaction的sa_mask(信号屏蔽集)中,则不会立即处理该信号。直到当前的信号处理函数执行完之后,才去执行该信号的处理函数。
否则:
则立即中断当前执行过程(如果处于睡眠,比如sleep, 则立即被唤醒)而去执行这个新的信号响应。新的响应执行完之后,再在返回至原来的信号处理函数继续执行。文章来源地址https://www.toymoban.com/news/detail-462406.html

7. 信号集

1). 什么是信号集
    信号集,用sigset_t类型表示,实质是一个无符号长整形。
    用来表示包含多个信号的集合。
     
   
2). 信号集的基本操作
    sigemptyset       把信号集清空
    sigfillset          把所有已定义的信号填充到指定信号集
    sigdelset         从指定的信号集中删除指定的信号
    sigaddset        从指定的信号集中添加指定的信号
    
    sigismember   判断指定的信号是否在指定的信号集中
                              如果是,    返回 1
                              如果不是, 返回 0
                              信号无效, 返回-1
            
    详细用法见  man 
    
3) 进程的“信号屏蔽字”
    进程的“信号屏蔽字”是一个信号集
    想目标进程发送某信号时,如果这个信号在目标进程的信号屏蔽字中,
    则目标进程将不会捕获到该信号,即不会执行该信号的处理函数。
    当该进程的信号屏蔽字不再包含该信号时,则会捕获这个早已收到的信号(执行对应的函数)
            
    修改进程的“信号屏蔽字”
    使用sigprocmask
    int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
    
    参数:
         how:
               SIG_BLOCK      把参数set中的信号添加到信号屏蔽字中
               SIG_UNBLOCK  把参数set中的信号从信号屏蔽字中删除
               SIG_SETMASK  把参数set中的信号设置为信号屏蔽字
         
         oldset
              返回原来的信号屏蔽字
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

void myhandle(int sig) 
{
	printf("Catch a signal : %d\n", sig);
	printf("Catch end.%d\n", sig);
}

int main(void) 
{
	struct sigaction act, act2;

	act.sa_handler = myhandle;
	sigemptyset(&act.sa_mask);
	act.sa_flags = 0;
	sigaction(SIGINT, &act, 0);

	sigset_t proc_sig_msk, old_mask;
	sigemptyset(&proc_sig_msk);
	sigaddset(&proc_sig_msk, SIGINT);

	sigprocmask(SIG_BLOCK, &proc_sig_msk, &old_mask);
	sleep(5);
	printf("had delete SIGINT from process sig mask\n");
	sigprocmask(SIG_UNBLOCK, &proc_sig_msk, &old_mask);
		
	while (1) {
		
	}

	return 0;
}
4) 获取未处理的信号
   当进程的信号屏蔽字中信号发生时,这些信号不会被该进程响应,
   可通过sigpending函数获取这些已经发生了但是没有被处理的信号
   
   用法: man sigpending
   返回值:成功则返回0
               失败则返回-1

5) 阻塞式等待信号
   (1) pause
        阻塞进程,直到发生任一信号后
   
   (2) sigsuspend
        用指定的参数设置信号屏蔽字,然后阻塞时等待信号的发生。
        即,只等待信号屏蔽字之外的信号

到了这里,关于进程间通信之信号的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Linux】进程间通信 -- 信号量

    信号量是什么? 本质是一个计数器,通常用来表示公共资源中,资源数量多少的问题 公共资源:能被多个进程同时可以访问的资源 访问没有保护的公共资源:数据不一致问题(比如我想写abc123,但是我123还没有写入,就读取了abc,可能数据分开会导致数据无意义) 为什么要

    2024年02月16日
    浏览(47)
  • linux进程间通信(信号量)

    信号量是一个特殊的变量,程序对其访问都是原子操作,且只允许对它进行等待(即 P(信号变量))和发 送(即 V(信号变量))信息操作。最简单的信号量是只能取 0 和 1 的变量,这也是信号量最常见的一种形式, 叫做二进制信号量。而可以取多个正整数的信号量被称为通用信号

    2024年02月07日
    浏览(36)
  • Linux进程间通信【消息队列、信号量】

    ✨个人主页: 北 海 🎉所属专栏: Linux学习之旅 🎃操作环境: CentOS 7.6 阿里云远程服务器 在 System V 通信标准中,还有一种通信方式: 消息队列 ,以及一种实现互斥的工具: 信号量 ;随着时代的发展,这些陈旧的标准都已经较少使用了,但作为 IPC 中的经典知识,我们可

    2024年02月08日
    浏览(51)
  • 【Linux】进程间通信——System V信号量

    目录 写在前面的话 一些概念的理解 信号量的引入 信号量的概念及使用            System V信号量是一种较低级的IPC机制 ,使用的时候需要手动进行操作和同步。在现代操作系统中,更常用的是 POSIX信号量 (通过 sem_* 系列的函数进行操作)或更高级的同步原语(如互斥锁

    2024年02月11日
    浏览(47)
  • 【Linux】进程间通信之共享内存/消息队列/信号量

    共享内存是通过让不同的进程看到同一个内存块的方式。 我们知道,每一个进程都会有对应的PCB-task_struct ,独立的进程地址空间,然后通过页表将地址映射到物理内存中。此时我们就可以让OS在内存中申请一块空间,然后将创建好的内存空间映射到进程的地址空间中,两个需

    2024年02月05日
    浏览(46)
  • 【Linux】进程间通信 --- 管道 共享内存 消息队列 信号量

    等明年国庆去西藏洗涤灵魂,laozi不伺候这无聊的生活了 1. 通过之前的学习我们知道,每个进程都有自己独立的内核数据结构,例如PCB,页表,物理内存块,mm_struct,所以具有独立性的进程之间如果想要通信的话,成本一定是不低的。 2. a.数据传输:一个进程需要将它的数据

    2023年04月17日
    浏览(46)
  • 【linux 多线程并发】多线程模型下的信号通信处理,与多进程处理的比较,属于相同进程的线程信号分发机制

    ​ 专栏内容 : 参天引擎内核架构 本专栏一起来聊聊参天引擎内核架构,以及如何实现多机的数据库节点的多读多写,与传统主备,MPP的区别,技术难点的分析,数据元数据同步,多主节点的情况下对故障容灾的支持。 手写数据库toadb 本专栏主要介绍如何从零开发,开发的

    2024年01月17日
    浏览(46)
  • Linux进程间通信 - 信号(signal) 与 管道(pipe) 与 消息队列

    什么是进程间通信,就是进程与进程之间进行通信,互相发送消息;可以通过 信号 或者 管道 或者 消息队列 或者 信号量 去通信! 目录 一、信号 1. 信号简介  2. 都有那些信号? 3. 注册信号的函数 1). signal 2). sigaction (项目中强烈推荐使用) 4. 信号发送 1). kill 函数 2). alarm 函

    2024年02月01日
    浏览(38)
  • 【C++】Windows下共享内存加信号量实现进程间同步通信

    目录 一,函数清单 1.CreateFileMapping 方法 2.OpenFileMapping 方法 3.MapViewOfFile 方法 4.UnmapViewOfFile 方法 5.CreateSemaphore 方法 6. OpenSemaphore 方法 7.WaitForSingleObject 方法 8.ReleaseSemaphore 方法 9.CloseHandle 方法 10.GetLastError 方法 二,单共享内存单信号量-进程间单向通信 共享内存管理文

    2024年02月08日
    浏览(40)
  • 标准C库之raise,abort函数(进程通信函数,发送信号类函数)

    如果,想要深入的学习标准C库中的raise,abort函数,还是需要去自己阅读Linux系统中的帮助文档。 具体输入命令: 即可查阅到完整的资料信息。 raise 是一个C语言标准库函数,它的作用是给当前进程发送信号。它属于信号处理库(signal.h),允许程序员通过代码控制信号的发送

    2023年04月26日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包