百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

Linux进程间通信——信号

gudong366 2025-04-15 12:12 126 浏览

概念

什么是信号?

  • 信号是linux系统中一种常用的通信机制,A给B发送信号,B在收到信号之前执行自己的代码,收到信号后,不管执行什么程序,都暂停运行,去处理信号,处理完毕后再继续执行原来的程序,是一种软中断。

特点

  • 由于信号是通过软件方法实现的,具有很强的延时性,对用户来讲,时间非常短,不易察觉
  • 每个进程收到的所有信号,都是由内核负责发送,内核处理

与信号相关的事件或者名词

产生信号的基本方法

  • 系统调用当前进程的某些函数
  • 通过命令产生,如kill指令
  • 硬件异常、段错误、内存出错、总线错误
  • 软件条件产生,如alarm定时器
  • 硬件产生,如ctrl+c按键

信号分类及信号一览表

1.可靠信号与不可靠信号

  • 不可靠信号
    Linux信号机制基本上是从Unix系统中继承过来的。早期Unix系统中的信号机制比较简单和原始,信号值小于SIGRTMIN的信号都是不可靠信号,它的主要问题是信号可能丢失。
  • 可靠信号
    随着时间的发展,实践证明了有必要对信号的原始机制加以改进和扩充。由于原来定义的信号已有许多应用,不好再做改动,最终只好又新增加了一些信号,并在一开始就把它们定义为可靠信号,这些信号支持排队,不会丢失

2.指令kill -l 查看所有信号

  • 信号的名称是在头文件 里定义的
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN
+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
  • Linux提供两个特殊信号:9号和19号信号,无法修改,提供终止进程的手段
  • 写信号时最好使用信号宏名称,因为不同系统下宏的数值可能是不同的,如下有些信号宏可能有多个数值

递达

从信号产生,会先发送给内核,通过内核处理再发送到进程,进程再处理信号这一过程称为递达

未决信号集

从信号产生,还未递达至进程,信号没有被处理掉,这过程中会有一个未决信号集存贮这些信号

阻塞信号集

也叫做信号屏蔽字,每个进程都有一个用来描述哪些信号递送到进程时将被阻塞的信号集,该信号集中的所有信号在递送到进程后都将被阻塞。


假设发送第三个信号,若阻塞信号集第三位为1,那么进程就不会收到该信号,因此未决信号集中第三位将保持状态为1

Linuxc/c++服务器开发高阶视频,电子书学习资料后台私信【架构】获取

内容包括C/C++,Linux,Nginx,ZeroMQ,MySQL,Redis,MongoDB,ZK,流媒体,P2P,K8S,Docker,TCP/IP,协程,DPDK多个高级知识点

信号处理方式

  • 系统默认动作,例如ctrl+c将进程杀死
  • 忽略(丢弃)该信号,就象没有收到该信号似的继续运行
  • 捕捉信号,自定义动作

信号相关函数

头文件

  • signal函数
    函数原型:
    typedef void (*sighandler_t)(int);//返回类型为空的函数指针,整型参数
    sighandler_t signal(int signum, sighandler_t handler)
    功能:接收某个信号sig(第一个参数),使程序接收到信号时执行对应函数func(第二个参数),func这个函数必须有一个int类型的参数(即接收到的信号)
    func也可以是下面两个特殊值:
    SIG_IGN 屏蔽该信号
    SIG_DFL 恢复默认行为
  • int kill(pid_t pid, int sig);
    功能:给指定进程发送信号(不一定杀死),第一个参数是进程ID,第二个参数是发送信号的类别,
    返回值:成功返回0, 失败返回-1
  • int sigemptyset(sigset_t *set);
    功能:将某个信号集清0
    返回值:成功返回0, 失败返回-1
  • int sigfillset(sigset_t *set);
    功能:将某个信号置1
    返回值:成功返回0, 失败返回-1
  • int sigaddset(sigset_t *set, int signo);
    功能:将某个信号加入到信号集中
    返回值:成功返回0, 失败返回-1
  • int sigdelset(sigset_t *set, int signo);
    功能:用来将参数signo信号从参数set信号集里删除。
    返回值:成功则返回0,如果有错误则为-1。
  • int sigismember(const sigset_t *set, int signo);
    功能:用来测试参数signo信号是否已加入至参数set信号集里。
    返回值:如果信号集里已有该信号则返回1,否则返回0。错误则为-1
  • int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
    功能:读取或更改进程的信号屏蔽字。
    返回值:若成功则为0,若出错则为-1


参数how的含义

  • int sigpending(sigset_t *set);
    功能: sigpending读取当前进程的未决信号集,通过set参数传出。
    返回值:若成功则为0,若出错则为-1
  • void abort(void);
    头文件:
    功能:向进程发送sigabort信号,默认情况下进程会异常退出,当然可定义自己的信号处理函数。
    说明:即使sigabort被进程设置为阻塞信号,调用abort()后,sigabort仍然能被进程接收。该函数无返回值

讲了这么多,做个例子,主要实现的功能是:打印当前进程未决信号集,并测试屏蔽信号

#include 
#include 
#include 
#include 
#include <sys/time.h>

void printf_ped(sigset_t* ped)
{
	int i;
	for (i = 0; i < 32; i++)
	{
		if (sigismember(ped, i) == 1)
		{
			putchar('1');	
		}
		else
		{
			putchar('0');
		}
	}
	printf("\n");
}

int main(void)
{
	sigset_t myset, oldset, ped_set;

	sigemptyset(&myset);//清空信号集为0
	sigaddset(&myset, SIGQUIT);//添加信号到信号集中

	sigprocmask(SIG_BLOCK, &myset, &oldset); //设置进程的信号屏蔽字

	while (1)
	{
		sigpending(&ped_set);//读取未决信号集到ped_set中
		printf_ped(&ped_set);//打印打印未决信号集
		sleep(3);
	}
	return 0;
}

运行结果如下


一开始没有产生信号 未决信号集都为0,当我们按下crtl+\时产生3号信号,但是由于我们屏蔽了3号信号,未决信号集中第三位一直为1,表示该信号还未被处理

定时器相关函数

头文件 #include <sys/time.h>

alarm函数

  • 每个进程只有一个alarm
  • 无论进程处于何种状态下,定时器都在计时

函数原型:
unsigned int alarm(unsigned int seconds);

功能:专门为sigalrm信号而设,在指定的时间seconds秒后,将向进程本身发送sigalrm信号

返回值:如果调用alarm()前,进程中已经设置了闹钟时间,则返回上一个闹钟时间的剩余时间,否则返回0

说明:进程调用alarm后,任何以前的alarm()调用都将无效

setitimer函数

函数原型:
int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);

参数:
第一个参数 which指定定时器类型
第二个参数 结构itimerval的一个实例
第三个参数 可不做处理,old_value一般设为NULL

结构体类型如下

struct itimerval
 {
        struct timeval it_interval; /* 下一次的取值 */
        struct timeval it_value; /* 本次的设定值 */
};

struct timeval 
{
        long tv_sec; /* 秒 */
        long tv_usec; /* 微秒,1秒 = 1000000 微秒*/
};

功能:这个函数可以周期性计时,定时器将it_value递减到0时,产生一个信号,并将it_value的值设定为it_interval的值,然后重新开始计时,如此往复,若it_interval为0则定时器停止。参数ovalue如果不为空,则其中保留的是上次调用设定的值

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

定时器类型:

itimer_real: 按实际时间计时(系统时间+用户时间+等待时间),经过指定的时间后,内核将发送SIGALRM信号给本进程
itimer_virtual :只计算进程占用cpu的时间,经过指定的时间后,内核将发送SIGVTALRM信号给本进程
itimer_prof :计算占用cpu及执行系统调用的时间,经过指定的时间后,内核将发送SIGPROF信号给本进程

写个例子,每3秒打印一次hello world

#include 
#include 
#include <sys/time.h>
#include 


void myfun(int signo)
{
	printf("hello world\n");
}

int main()
{
	struct itimerval it, oldit;
	int ret;

	signal(SIGALRM, myfun);

	//本次设定值
	it.it_value.tv_sec = 3;
	it.it_value.tv_usec = 0;

	//下次设定值
	it.it_interval.tv_sec = 3;
	it.it_interval.tv_usec = 0;

	ret = setitimer(ITIMER_REAL, &it, &oldit);
	if (ret == -1)
	{
		printf("error\n");
		exit(1);
	}

	while (1);
	return 0;
}

运行结果

相关推荐

理解Linux进程和线程(linux的进程和线程的区别)

#进程-进程是一个执行中的程序,它拥有自己独立的内存空间,不同进程的地址空间是相互隔离的。-进程有自身的代码段,数据段,堆,栈等。进程需要耗费资源创建和销毁。-进程之间的通信需要借助IPC(I...

Linux进程上下文切换过程context_switch详解

1前言1.1Linux的调度器组成2个调度器可以用两种方法来激活调度一种是直接的,比如进程打算睡眠或出于其他原因放弃CPU另一种是通过周期性的机制,以固定的频率运行,不时的检测是否有必要因此...

linux init进程(linux init 1)

一.init是Linux系统操作中不可缺少的程序之一。所谓的init进程,它是一个由内核启动的用户级进程。内核自行启动(已经被载入内存,开始运行,并已初始化所有的设备驱动程序和数据结构等)之后,就通...

【Linux系统编程】特殊进程之守护进程

01.守护进程概述守护进程(DaemonProcess),也就是通常说的Daemon进程(精灵进程),是Linux中的后台服务进程。它是一个生存期较长的进程,通常独立于控制终端并且周期性地...

在 Linux 中如何强制停止进程?kill 和 killall 命令有什么区别?

在日常工作中,您会遇到两个用于在Linux中强制结束程序的命令;kill和killall。虽然许多Linux用户都知道kill命令,但知道并使用killall命令的人并不多。这两个命令...

嵌入式Linux系统编程——连进程间通信都不懂,还自称linux大神?

所有学嵌入式Linux系统的看过来了,以下内容是每一位想学习Linux嵌入式系统想要了解的内容,真的很想要分享给大家!本文分享的内容主要如下几个方面:(绝对的精品资料,不收藏可惜了)6.1共享内存...

Linux基础运维篇:Linux进程与服务管理(第010课)

在Linux系统里,进程和服务管理就像是一个大管家的工作,得把各种程序的运行安排得明明白白,这样系统才能稳稳当当地干活。进程就是程序跑起来的一个实例,服务呢,是那种一直在后台默默工作的进程,咱下面...

深度剖析Linux内核《如何唤醒线程》

linux内核如何唤醒线程//本文代码片段出自linux内核版本:4.1.15linux内核唤醒线程主要使用wake_up_process()。一、wake_up_process()分析在linux内...

字节因它而跳动!顶级资深大牛整理的“深入理解Linux内核”

如果你对Linux如何工作。其性能又为什么会如此之高怀有强烈的好奇心。你将会从这里找到答案.阅读本文之后,你会通过上千行代码找到自己的方式来区别重要数据结构和次要数据结构的不同,简而言之,你蒋成为一名...

都说Linux内核很吊,它到底是个啥玩意儿?

了解完基本信息之后,我们来看一看,为什么说它吊?吊在哪里?甚至我觉得不仅仅是c/c++Linux开发的可以学习,Java、Python等方面的都可以学习提升一下。linux内核有什么用?linux内核...

77% 的 Linux 运维都不懂的内核问题,这篇全告诉你了

前言之前在实习时,听了OOM的分享之后,就对Linux内核内存管理充满兴趣,但是这块知识非常庞大,没有一定积累,不敢写下,担心误人子弟,所以经过一个一段时间的积累,对内核内存有一定了解之后,今...

Linux 内核开发流程的一个典型例子

>authorLinusTorvalds<torvalds@linux-foundation.org>2025-07-0813:31:29-0700>committ...

Vold原理介绍(volte基本原理)

一、Vold简介Android中Vold是volumeDaemon,即Volume守护进程,用来管理Android中存储类的热拔插事件。这里的热插拔涉及的场景如:手机usb以MTP或者传输照片方式...

2-剖析Linux内核源码分析《中断处理》

一、中断向量及汇编指令1、中断向量Intelx86系列机器共支持256种向量中断,Intel用一个8位无符号整数叫做一个向量,因此也叫中断向量。所有256种中断可分为两大类:异常和中断,异常又称为故...

剖析linux内核(一文看懂linux内核)

PASmm_struct详解malloc()函数是用户态常用的分配内存接口,mmap()函数是用户态常用创建文件映射或匿名映射。进程地址空间在linux内核当中使用structvm_area...