进程间通信--信号

news2024/10/5 23:28:29

1:信号

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

信号由谁产生?

1)由shell终端根据当前发生的错误(段错误、非法指令等)Ctrl+c而产生相应的信号
比如:
socket通信或者管道通信,如果读端都已经关闭,执行写操作(或者发送数据),
将导致执行写操作的进程收到SIGPIPE信号(表示管道破裂)
该信号的默认行为:终止该进程。
2) 在shell终端,使用kill或killall命令产生信号
./demo & //选择在后台运行该程序
kill -HUP 13733 /* 向PID为13733的进程发送SIGHUP */
kill命令详解

3 ) 在程序代码中,调用kill系统调用产生信号

2:有哪些信号?

常用信号

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

int main()
{
  printf("SIGABRT:%d \n",SIGABRT);
  printf("SIGALRM:%d \n",SIGALRM);
  printf("SIGFPE:%d \n",SIGFPE);
  printf("SIGHUP:%d \n",SIGHUP);
  printf("SIGILL:%d \n",SIGILL);
  printf("SIGINT:%d \n",SIGINT);
  printf("SIGKILL:%d \n",SIGKILL);
  printf("SIGPIPE:%d \n",SIGPIPE);
  printf("SIGQUIT:%d \n",SIGQUIT);
  printf("SIGSEGV:%d \n",SIGSEGV);
  printf("SIGTERM:%d \n",SIGTERM);
  printf("SIGUSR1:%d \n",SIGUSR1);
  printf("SIGUSR2:%d \n",SIGUSR2);
  printf("SIGCHLD:%d \n",SIGCHLD);
  printf("SIGCONT:%d \n",SIGCONT);
  printf("SIGSTOP:%d \n",SIGSTOP);
  printf("SIGTSTP:%d \n",SIGTSTP);
  printf("SIGTTIN:%d \n",SIGTTIN);
  printf("SIGTTOU:%d \n",SIGTTOU);
  return 0;
}
~ 

结果如下:
在这里插入图片描述

3:信号的处理方式?

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

3.1 信号的捕获

信号的捕获,是指,指定接受到某种信号后,去执行指定的函数。
注意:SIGKILL和SIGSTOP不能被捕获,即,这两种信号的响应动作不能被改变。
信号的安装
1) 使用signal
用法:man 2 signal

     typedef void (*sighandler_t)(int);
     sighandler_t signal(int signum, sighandler_t handler);
     
    注:signal的返回类型,和它的第二个参数,都是函数指针类型
          
    signal的参数2可去以下特殊值:
    SIG_IGN     忽略信号
    SIG_DFL     恢复默认行为
//程序运行后 按ctrl c会产生SIGINT的信号 我们自定义回调函数去响应它
#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;
}

上面cpp 捕捉函数 用myhandle处理函数去处理对应的信号SIGINT 也就是ctrl+c

    此时就不能结束该进程了!
    只能通过其他终端,给该进程发送一个其他信号,使它终止
         #ps -ef | grep ./demo      //查询进程号  demo是指自己编译出来的exe文件
         #kill  -HUP  进程号      

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

void myhandle(int sig)
{
   printf("catch a signal:%d \n",sig);
   signal(SIGINT,SIG_DFL);
}
int main(void)
{
  signal(SIGINT,myhandle);
  while(1)
  {
    sleep(1);
  }
  return 0;
}
~     

上面代码是响应函数myhandle之后 信号就恢复其默认行为 即在此直接终止掉该程序

2 ) 使用sigaction (项目实战强烈推荐使用)
sigaction与signal的区别: sigaction比signal更“健壮”,建议使用sigaction

   用法:man 2 sigaction
   
   结构struct 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;

	sigaction(SIGINT, &act, 0);

	while (1) {
        
	}

	return 0;
}
//这段代码sigaction运行后信号以默认的行为运行
#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;
	act.sa_flags = SA_RESETHAND;

	sigaction(SIGINT, &act, 0);

	while (1) {

	}

	return 0;
}

3.2信号的发送

信号的发送方式:

(1) 在shell终端用快捷键产生信号
(2) 使用kill,killall命令。
(3) 使用kill函数和alarm函数

使用kill函数
给指定的进程发送指定信号
用法:man 2 kill
注意:
给指定的进程发送信号需要“权限”:
普通用户的进程只能给该用户的其他进程发送信号
root用户可以给所有用户的进程发送信号
kill失败
失败时返回-1
失败原因:
权限不够
信号不存在
指定的进程不存在
实例1:
创建一个子进程,子进程每秒中输出字符串“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;

		//注意fork函数 子进程返回0 父进程返回子进程的pid
        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();
                        printf("my pid:%d \n",pd);
                        printf("i Get c:%c \n",c);
                        if (c == 'A') {
                                kill(pd, SIGUSR1);
                        } else if (c == 'a') {
                                kill(pd, SIGUSR2);
                        }
                }
        }


        return 0;
}

实例:2
创建一个子进程,子进程在5秒钟之后给父进程发送一个SIGALR,父进程收到SIGALRM信号之后,“闹铃”(用打印模拟)

#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) {
		sleep(5);
		kill(getppid(), SIGALRM);
	} else {
		struct sigaction act; 
		act.sa_handler = wake_handle;
		act.sa_flags = 0;
		sigemptyset(&act.sa_mask);

		sigaction(SIGALRM,  &act, 0);

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

		if (wakeflag) {
			printf("Alarm clock work!!!\n");
		}
	}

	return 0;
}

上面的实现方法还可以改写成alarm的方式
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;
}

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

3.3发送多个信号

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

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

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

static int cut=1;
void myhandle(int sig)
{
   printf("catch a signal:%d \n",sig);
   cut++;
   for(int i=0;i<2e6-1;i++)
   {
     printf("cut:%d \n",cut);
   }
   printf("over!!!\n");
}
int main(void)
{
  struct sigaction act;
  act.sa_handler = myhandle;
  sigemptyset(&act.sa_mask);
  //如果没有下面这句 那么执行SIGINT的时候 接收到SIGUSR1的时候 会立刻执行对于的回调函数 把当前执行的给中止 
  //有下面一句 对于的就会等待SIGINT执行完再执行一次
  sigaddset(&act.sa_mask, SIGUSR1);
  act.sa_flags = 0;

  sigaction(SIGINT,&act,0);
  sigaction(SIGUSR1,&act,0);
  while(1)
  {
    sleep(1);
  }
  return 0;
}

3.4信号集?

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;
}

该程序最开始 不响应 SIGINT函数 阻塞住了 后面5s后放开 继续响应SIGINT

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1011943.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

途虎养车IPO:飞轮效应下的汽车后市场巨头

汽车已经成为了家家户户必不可少的存在。作为消费品来说&#xff0c;汽车更新换代快&#xff0c;日常使用磨损大&#xff0c;随着智能汽车和新能源汽车市场的不断扩大&#xff0c;也给汽车售后服务产线带来了巨大的发展市场。保养以及维修市场的缺口越来越大&#xff0c;也为汽…

简明 SQL 子查询指南:掌握 EXISTS 实现数据筛选

子查询是一种在查询语句内部嵌套另一个完整查询的方式&#xff0c;用于获取更复杂的查询结果或数据过滤。在执行包含子查询的查询时&#xff0c;数据库引擎首先执行子查询&#xff0c;然后将其结果用作外层查询的条件或数据源。 以下两表作为后续SQL语句所用 table1 …

Jmeter系列-并发线程Concurrency Thread Group的介绍(7)

简介 Concurrency Thread Group提供了用于配置多个线程计划的简化方法&#xff0c;该线程组目的是为了保持并发水平&#xff0c;意味着如果并发线程不够&#xff0c;则在运行线程中启动额外的线程Concurrency Thread Group提供了更好的用户行为模拟&#xff0c;因为它使您可以…

AeDet:方位不变的多视图3D物体检测

代码&#xff1a;https://github.com/fcjian/AeDet 项目地址&#xff1a;https://fcjian.github.io/aedet/ 导读&动机 本篇论文探讨了自动驾驶领域中的多视图3D目标检测问题&#xff0c;特别关注了LSS&#xff08;Lift-Splat-Shoot&#xff09;方法的发展&#xff0c;该方法…

模拟实现C语言--strcat函数

模拟实现C语言–strcat函数 文章目录 模拟实现C语言--strcat函数一、strcat函数是什么&#xff1f;二、使用示例二、模拟实现 一、strcat函数是什么&#xff1f; 作用是把源数据追加到目标空间 char * strcat ( char * destination, const char * source );源字符串必须以 ‘…

Springboot整合规则引擎

Springboot整合Drools 规则引擎 1.添加maven 依赖坐标&#xff0c;并创建springboot项目 <!-- drools规则引擎 --> <dependency><groupId>org.drools</groupId><artifactId>drools-compiler</artifactId><version>7.6.0.Final<…

maven-resources-production:trunk-auth: java.lang.NegativeArraySizeException

ijdea启动auth项目报错&#xff1a; maven-resources-production:trunk-auth: java.lang.NegativeArraySizeException 一直在本地启动不起来&#xff0c;记录下操作步骤 第一步&#xff1a;将ijdea中的缓存都清理掉了&#xff0c;不行 第二步&#xff1a;将maven中的对应项目…

如何将内网ip映射到外网?快解析内网穿透

关于内网ip映射到外网的问题&#xff0c;就是网络地址转换&#xff0c;私网借公网。要实现这个&#xff0c;看起来说得不错&#xff0c;实际上是有前提条件的。要实现内网ip映射到外网&#xff0c;首先要有一个固定的公网IP&#xff0c;可以从运营商那里得到。当你得到公网IP后…

Mybatis动态数据源及其原理

一、引言 作者最近的平台项目需要一个功能&#xff0c;数据库是动态的&#xff0c;sql也是动态的&#xff0c;所以需要动态注入数据源&#xff0c;并且能够在运行过程中进行切换数据库。作者在这里分享一下做法&#xff0c;以及Mybatis这样做的原理。 二、分析 接下来分析一下…

【案例教学】华为云API图引擎服务 GES的便捷性—AI帮助快速处理图片小助手

云服务、API、SDK&#xff0c;调试&#xff0c;查看&#xff0c;我都行 阅读短文您可以学习到&#xff1a;人工智能AI快速处理图片 1 IntelliJ IDEA 之API插件介绍 API插件支持 VS Code IDE、IntelliJ IDEA等平台、以及华为云自研 CodeArts IDE&#xff0c;基于华为云服务提供…

LeetCode //C - 637. Average of Levels in Binary Tree

637. Average of Levels in Binary Tree Given the root of a binary tree, return the average value of the nodes on each level in the form of an array. Answers within 1 0 − 5 10^{-5} 10−5 of the actual answer will be accepted. Example 1: Input: root [3…

学Python的漫画漫步进阶 -- 第十三步

学Python的漫画漫步进阶 -- 第十三步 十三、图形用户界面13.1 Python中的图形用户界面开发库13.2 安装wxPython13.3 第一个wxPython程序13.4 自定义窗口类13.5 在窗口中添加控件13.6 事件处理13.7 布局管理13.7.1 盒子布局管理器13.7.2 动动手——重构事件处理示例13.7.3 动动手…

赴日开发工程师工作怎么找?

想要做赴日开发工作的途径有一下几种&#xff1a;一个是自己去联系日本的企业&#xff0c;当然这种的前提是你日语完全没有问题&#xff0c;但是现在很少有企业愿意直接与求职人员沟通。第二个就是你有IT技术&#xff0c;但是不会日语&#xff0c;年纪不大的可以来日本读个学校…

单目标追踪——【工具】汉明窗(Hamming window)

目录 汉明窗&#xff08;Hamming window&#xff09;原理作用代码实例可视化总结 汉明窗&#xff08;Hamming window&#xff09; 原理 汉明&#xff08;Hanning&#xff09;窗可以看成是升余弦窗的一个特例&#xff0c;汉宁窗可以看作是3个矩形时间窗的频谱之和&#xff0c;…

webpack实战:最新QQ音乐sign参数加密分析

文章目录 1. 写在前面2. 接口抓包分析3. 扣webpack代码4. 补浏览器环境5. 验证加密结果 1. 写在前面 现在&#xff01;很多的网站使用Webpack加载和处理JS文件。所以对于使用了Webpack加载的JS代码&#xff0c;一旦它们被打包并在浏览器中执行&#xff0c;通常是难以直接阅读和…

选择正确的开发框架:构建高效、可维护的应用程序

&#x1f482; 个人网站:【工具大全】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 引言 在现代软件开发中…

python如何打包成应用

前几天有学生问&#xff0c;开发好的python代码如何打包给对方使用&#xff1f;对方没有python的安装执行环境。 python是一个强大的编程开发工具&#xff0c;它不仅仅是做一些命令行或脚本运行工具&#xff0c;还可以开发桌面、web等应用。 本文就介绍使用pyinstall如何把py…

GIS前端编程-地理事件动态模拟

GIS前端编程-地理事件动态模拟 动画特效功能图形闪烁要素轨迹移动 动画特效功能 目前&#xff0c;GIS应用除了涉及地理位置信息&#xff0c;还要结合时间维度&#xff0c;这样才能更加真实地模拟现实世界的事物。因此在实际项目应用中&#xff0c;静态的&#xff08;位置固定不…

Redis混合模式持久化原理

前言 前面文章中我们也介绍过Redis的持久化方式有两种&#xff1a;rdb持久化和aof持久化&#xff0c;具体详情可查看之前文章redis持久化。rdb持久化还是aof持久化它们都有各自的缺点。 rdb和aof缺点 rdb持久化&#xff1a;由于是定期对内存数据快照进行持久化&#xff0c;因此…

【智能家居-大模型】行业专题报告:ChatGPT等大模型催化智能家居行业发展

&#xff08;报告出品方/作者&#xff1a;华安证券&#xff0c;马远方&#xff09; 1 智能家居&#xff1a;ChatGPT 等大模型为行业发展带来新机遇 1.1 现状&#xff1a;智能家居产品的用户体验&#xff08;交互能力、智能化水 平&#xff09;及安全性待提升 智能家居&#…