Linux 详谈Linux进程信号(一)

【Linux】进程信号详解 介绍了信号的种类、属性、信号的产生、信号在内核中的存储形式、信号的捕捉、信号的处理流程等 阅读详情

目录

1. 信号快速认识

⽣活⻆度的信号

技术应⽤⻆度的信号

⼀个样例

⼀个系统函数

信号概念

查看信号

信号处理

2. 产⽣信号

通过终端按键产⽣信号

基本操作

理解OS如何得知键盘有数据

初步理解信号起源

调⽤系统命令向进程发信号

使⽤函数产⽣信号

kill

raise

abort

由软件条件产⽣信号

基本alarm验证-体会IO效率问题

设置重复闹钟

如何理解软件条件

如何简单快速理解系统闹钟

硬件异常产⽣信号

模拟除0

模拟野指针

⼦进程退出core dump

Core Dump

总结思考⼀下


1. 信号快速认识

⽣活⻆度的信号

你在⽹上买了很多件商品,再等待不同商品快递的到来。但即便快递没有到来,你也知道快递来临时,你该怎么处理快递。也就是你能“识别快递”

当快递员到了你楼下,你也收到快递到来的通知,但是你正在打游戏,需5min之后才能去取快递。

那么在在这5min之内,你并没有下去去取快递,但是你是知道有快递到来了。也就是取快递的⾏为并不是⼀定要⽴即执⾏,可以理解成“在合适的时候去取”。

在收到通知,再到你拿到快递期间,是有⼀个时间窗⼝的,在这段时间,你并没有拿到快递,但是你知道有⼀个快递已经来了。本质上是你“记住了有⼀个快递要去取”当你时间合适,顺利拿到快递之后,就要开始处理快递了。⽽处理快递⼀般⽅式有三种:1. 执⾏默认动作(幸福的打开快递,使⽤商品)2. 执⾏⾃定义动作(快递是零⻝,你要送给你你的⼥朋友)3. 忽略快递(快递拿上来之后,扔掉床头,继续开⼀把游戏)

快递到来的整个过程,对你来讲是异步的,你不能准确断定快递员什么时候给你打电话

基本结论:
你怎么能识别信号呢?识别信号是内置的,进程识别信号,是内核程序员写的内置特性。
信号产⽣之后,你知道怎么处理吗?知道。如果信号没有产⽣,你知道怎么处理信号吗?知道。所以,信号的处理⽅法,在信号产⽣之前,已经准备好了。
处理信号,⽴即处理吗?我可能正在做优先级更⾼的事情,不会⽴即处理?什么时候?合
适的时候。
信号到来 | 信号保存 | 信号处理
怎么进⾏信号处理啊?a.默认 b.忽略 c.⾃定义, 后续都叫做信号捕捉。

技术应⽤⻆度的信号

⼀个样例

// sig.cc
#include <iostream>
#include <unistd.h>
int main()
{
	while (true) {
		std::cout << "I am a process, I am waiting signal!" << std::endl;
		sleep(1);
	}
}
$ g++ sig.cc -o sig
$ ./sig
I am a process, I am waiting signal!
I am a process, I am waiting signal!
^C

⽤⼾输⼊命令,在Shell下启动⼀个前台进程

⽤⼾按下 Ctrl+C ,这个键盘输⼊产⽣⼀个硬件中断,被OS获取,解释成信号,发送给⽬标前台进程

前台进程因为收到信号,进⽽引起进程退出

⼀个系统函数

NAME
    signal - ANSI C signal handling
SYNOPSIS
    #include <signal.h>
    typedef void (*sighandler_t)(int);
    sighandler_t signal(int signum, sighandler_t handler);
参数说明:
signum:信号编号[后⾯解释,只需要知道是数字即可]
handler:函数指针,表⽰更改信号的处理动作,当收到对应的信号,就回调执⾏handler⽅法

⽽其实, Ctrl+C 的本质是向前台进程发送 SIGINT 即 2 号信号,我们证明⼀下,这⾥需要引⼊⼀个系统调⽤函数

开始测试

#include <iostream>
#include <unistd.h>
#include <signal.h>
void handler(int signumber)
{
	std::cout << "我是: " << getpid() << ", 我获得了⼀个信号: " << signumber <<
		std::endl;
}
int main()
{
	std::cout << "我是进程: " << getpid() << std::endl;
	signal(SIGINT/*2*/, handler);
	while (true) {
		std::cout << "I am a process, I am waiting signal!" << std::endl;
		sleep(1);
	}
}
$ g++ sig.cc -o sig
$ ./sig
我是进程: 212569
I am a process, I am waiting signal!
I am a process, I am waiting signal!
^C我是: 212569, 我获得了⼀个信号: 2
I am a process, I am waiting signal!
I am a process, I am waiting signal!
^C我是: 212569, 我获得了⼀个信号: 2
I am a process, I am waiting signal!
I am a process, I am waiting signal!
思考:
这⾥进程为什么不退出?
这个例⼦能说明哪些问题?信号处理,是⾃⼰处理
请将⽣活例⼦和 Ctrl-C 信号处理过程相结合,解释⼀下信号处理过程?进程就是你,操作系统就是快递员,信号就是快递,发信号的过程就类似给你打电话
注意
要注意的是,signal函数仅仅是设置了特定信号的捕捉⾏为处理⽅式,并不是直接调⽤处理动作。如果后续特定信号没有产⽣,设置的捕捉函数永远也不会被调⽤!!
Ctrl-C 产⽣的信号只能发给前台进程。⼀个命令后⾯加个&可以放到后台运⾏,这样Shell不必等待进程结束就可以接受新的命令,启动新的进程。
Shell可以同时运⾏⼀个前台进程和任意多个后台进程,只有前台进程才能接到像 Ctrl-C这种控制键产⽣的信号。
前台进程在运⾏过程中⽤⼾随时可能按下 Ctrl-C ⽽产⽣⼀个信号,也就是说该进程的⽤⼾空间代码执⾏到任何地⽅都有可能收到 SIGINT 信号⽽终⽌,所以信号相对于进程的控制流程来说是异步(Asynchronous)的。
关于进程间关系,我们在⽹络部分会专⻔来讲,现在就了解即可。
可以渗透 & 和 nohup

信号概念

信号是进程之间事件异步通知的⼀种⽅式,属于软中断。

查看信号

kill -l

每个信号都有⼀个编号和⼀个宏定义名称,这些宏定义可以在signal.h中找到,例如其中有定义#define SIGINT 2

编号34以上的是实时信号,本章只讨论编号34以下的信号,不讨论实时信号。这些信号各⾃在什么条件下产⽣,默认的处理动作是什么,在signal(7)中都有详细说明: man 7 signal

信号处理

( sigaction 函数稍后详细介绍),可选的处理动作有以下三种:

忽略此信号。

#include <iostream>
#include <unistd.h>
#include <signal.h>
void handler(int signumber)
{
	std::cout << "我是: " << getpid() << ", 我获得了⼀个信号: " << signumber
		<< std::endl;
}
int main()
{
	std::cout << "我是进程: " << getpid() << std::endl;
	signal(SIGINT/*2*/, SIG_IGN); // 设置忽略信号的宏
	while (true) {
		std::cout << "I am a process, I am waiting signal!" << std::endl;
		sleep(1);
	}
}
$ g++ sig.cc -o sig
$ ./sig
我是进程: 212681
I am a process, I am waiting signal!
I am a process, I am waiting signal!
^C^C^C^C^C^CI am a process, I am waiting signal! // 输⼊ctrl+c毫⽆反应
I am a process, I am waiting signal!

执⾏该信号的默认处理动作。

#include <iostream>
#include <unistd.h>
#include <signal.h>
void handler(int signumber)
{
	std::cout << "我是: " << getpid() << ", 我获得了⼀个信号: " << signumber
		<< std::endl;
}
int main()
{
	std::cout << "我是进程: " << getpid() << std::endl;
	signal(SIGINT/*2*/, SIG_DFL);
	while (true) {
		std::cout << "I am a process, I am waiting signal!" << std::endl;
		sleep(1);
	}
}
$ g++ sig.cc -o sig
$ ./sig
我是进程: 212791
I am a process, I am waiting signal!
I am a process, I am waiting signal!
^C // 输⼊ctrl+c,进程退出,就是默认动作

提供⼀个信号处理函数,要求内核在处理该信号时切换到⽤⼾态执⾏这个处理函数,这种⽅式称为⾃定义捕捉(Catch)⼀个信号。

// 就是开始的样例

注意看源码:

#define SIG_DFL ((__sighandler_t) 0) /* Default action. */
#define SIG_IGN ((__sighandler_t) 1) /* Ignore signal. */
/* Type of a signal handler. */
typedef void (*__sighandler_t) (int);
// 其实SIG_DFL和SIG_IGN就是把0,1强转为函数指针类型

上⾯的所有内容,我们都没有做⾮常多的解释,主要是先⽤起来,然后渗透部分概念和共识,下⾯我们从理论和实操两个层⾯,来进⾏对信号的详细学习、论证和理解。为了保证条理,我们采⽤如下思路来进⾏阐述:

2. 产⽣信号

当前阶段:

通过终端按键产⽣信号

基本操作

Ctrl+C (SIGINT) 已经验证过,这⾥不再重复

Ctrl+\(SIGQUIT)可以发送终⽌信号并⽣成core dump⽂件,⽤于事后调试(后⾯详谈)

#include <iostream>
#include <unistd.h>
#include <signal.h>
void handler(int signumber)
{
	std::cout << "我是: " << getpid() << ", 我获得了⼀个信号: " << signumber <<
		std::endl;
}
int main()
{
	std::cout << "我是进程: " << getpid() << std::endl;
	signal(SIGQUIT/*3*/, handler);
	while (true) {
		std::cout << "I am a process, I am waiting signal!" << std::endl;
		sleep(1);
	}
}
$ g++ sig.cc -o sig
$ ./sig
我是进程: 213056
I am a process, I am waiting signal!
I am a process, I am waiting signal!
I am a process, I am waiting signal!
^\我是: 213056, 我获得了⼀个信号: 3
// 注释掉13⾏代码
$ ./sig
我是进程: 213146
I am a process, I am waiting signal!
I am a process, I am waiting signal!
^\Quit

Ctrl+Z(SIGTSTP)可以发送停⽌信号,将当前前台进程挂起到后台等。

#include <iostream>
#include <unistd.h>
#include <signal.h>
void handler(int signumber)
{
	std::cout << "我是: " << getpid() << ", 我获得了⼀个信号: " << signumber <<
		std::endl;
}
int main()
{
	std::cout << "我是进程: " << getpid() << std::endl;
	signal(SIGTSTP/*20*/, handler);
	while (true) {
		std::cout << "I am a process, I am waiting signal!" << std::endl;
		sleep(1);
	}
}
$ ./sig
我是进程: 213552
I am a process, I am waiting signal!
I am a process, I am waiting signal!
^Z我是: 213552, 我获得了⼀个信号: 20
// 注释掉13⾏代码
$ ./sig
我是进程: 213627
I am a process, I am waiting signal!
I am a process, I am waiting signal!
I am a process, I am waiting signal!
^Z
[1]+ Stopped ./sig
whb@bite:~/code/test$ jobs
[1]+ Stopped ./sig

理解OS如何得知键盘有数据

初步理解信号起源

注意:
信号其实是从纯软件⻆度,模拟硬件中断的⾏为
只不过硬件中断是发给CPU,⽽信号是发给进程
两者有相似性,但是层级不同,这点我们后⾯的感觉会更加明显

调⽤系统命令向进程发信号

⽰例代码

#include <iostream>
#include <unistd.h>
#include <signal.h>
int main()
{
	while (true) {
		sleep(1);
	}
}
$ g++ sig.cc -o sig // step 1
$ ./sig & // step 2
$ ps ajx |head -1 && ps ajx | grep sig // step 3
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
211805 213784 213784 211805 pts/0 213792 S 1002 0:00 ./sig

⾸先在后台执⾏死循环程序,然后⽤kill命令给它发SIGSEGV信号。

$ kill -SIGSEGV 213784
$ // 多按⼀次回⻋
[1]+ Segmentation fault ./sig

213784 是 sig 进程的pid。之所以要再次回⻋才显⽰ Segmentation fault ,是因为在213784 进程终⽌掉之前已经回到了Shell提⽰符等待⽤⼾输⼊下⼀条命令, Shell 不希望Segmentation fault 信息和⽤⼾的输⼊交错在⼀起,所以等⽤⼾输⼊命令之后才显⽰。

指定发送某种信号的 kill 命令可以有多种写法,上⾯的命令还可以写成 kill -11 213784 , 11 是信号 SIGSEGV 的编号。以往遇到的段错误都是由⾮法内存访问产⽣的,⽽这个程序本⾝没错,给它发SIGSEGV也能产⽣段错误。

使⽤函数产⽣信号

kill

kill 命令是调⽤ kill 函数实现的。 kill 函数可以给⼀个指定的进程发送指定的信号。

NAME
    kill - send signal to a process
SYNOPSIS
    #include <sys/types.h>
    #include <signal.h>
    int kill(pid_t pid, int sig);
RETURN VALUE
    On success (at least one signal was sent), zero is returned. On error,
    -1 is returned, and errno is set appropriately.

样例:实现⾃⼰的 kill 命令

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
// mykill -signumber pid
int main(int argc, char* argv[])
{
	if (argc != 3)
	{
		std::cerr << "Usage: " << argv[0] << " -signumber pid" << std::endl;
		return 1;
	}
	int number = std::stoi(argv[1] + 1); // 去掉-
	pid_t pid = std::stoi(argv[2]);
	int n = kill(pid, number);
	return n;
}

raise

raise 函数可以给当前进程发送指定的信号(⾃⼰给⾃⼰发信号)。

NAME
    raise - send a signal to the caller
SYNOPSIS
    #include <signal.h>
    int raise(int sig);
RETURN VALUE
    raise() returns 0 on success, and nonzero for failure.

样例:

#include <iostream>
#include <unistd.h>
#include <signal.h>
void handler(int signumber)
{
	// 整个代码就只有这⼀处打印
	std::cout << "获取了⼀个信号: " << signumber << std::endl;
}
// mykill -signumber pid
int main()
{
	signal(2, handler); // 先对2号信号进⾏捕捉
	// 每隔1S,⾃⼰给⾃⼰发送2号信号
	while (true)
	{
		sleep(1);
		raise(2);
	}
}
$ g++ raise.cc -o raise
$ ./raise
获取了⼀个信号: 2
获取了⼀个信号: 2
获取了⼀个信号: 2

abort

abort 函数使当前进程接收到信号⽽异常终⽌。

NAME
    abort - cause abnormal process termination
SYNOPSIS
    #include <stdlib.h>
    void abort(void);
RETURN VALUE
    The abort() function never returns.
// 就像exit函数⼀样,abort函数总是会成功的,所以没有返回值。
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
void handler(int signumber)
{
	// 整个代码就只有这⼀处打印
	std::cout << "获取了⼀个信号: " << signumber << std::endl;
}
// mykill -signumber pid
int main()
{
	signal(SIGABRT, handler);
	while (true)
	{
		sleep(1);
		abort();
	}
}
$ g++ Abort.cc -o Abort
$ ./Abort
获取了⼀个信号: 6 // 实验可以得知,abort给⾃⼰发送的是固定6号信号,虽然捕捉了,但是
还是要退出
Aborted
// 注释掉15⾏代码
$ ./Abort
Aborted

由软件条件产⽣信号

SIGPIPE 是⼀种由软件条件产⽣的信号,在“管道”中已经介绍过了。本节主要介绍 alarm 函数和 SIGALRM 信号。

NAME
    alarm - set an alarm clock for delivery of a signal
SYNOPSIS
    #include <unistd.h>
    unsigned int alarm(unsigned int seconds);
RETURN VALUE
    alarm() returns the number of seconds remaining until any previously
    scheduled alarm was due to be delivered, or zero if there was no previ‐
    ously scheduled alarm.

调⽤ alarm 函数可以设定⼀个闹钟,也就是告诉内核在 seconds 秒之后给当前进程发SIGALRM 信号,该信号的默认处理动作是终⽌当前进程。

这个函数的返回值是0或者是以前设定的闹钟时间还余下的秒数。打个⽐⽅,某⼈要⼩睡⼀觉,设定闹钟为30分钟之后响,20分钟后被⼈吵醒了,还想多睡⼀会⼉,于是重新设定闹钟为15分钟之后响,“以前设定的闹钟时间还余下的时间”就是10分钟。如果seconds值为0,表⽰取消以前设定的闹钟,函数的返回值仍然是以前设定的闹钟时间还余下的秒数。

基本alarm验证-体会IO效率问题

程序的作⽤是1秒钟之内不停地数数,1秒

// IO 多
#include <iostream>
#include <unistd.h>
#include <signal.h>
int main()
{
	int count = 0;
	alarm(1);
	while (true)
	{
		std::cout << "count : "
			<< count << std::endl;
		count++;
	}
	return 0;
}
... ...
count : 107148
count : 107149
Alarm clock
// IO少
#include <iostream>
#include <unistd.h>
#include <signal.h>
int count = 0;
void handler(int signumber)
{
	std::cout << "count : " <<
		count << std::endl;
	exit(0);
}
int main()
{
	signal(SIGALRM, handler);
	alarm(1);
	while (true)
	{
		count++;
	}
	return 0;
}
$ g++ alarm.cc -o alarm
whb@bite:~/code/test$ ./alarm
count : 492333713
结论:
闹钟会响⼀次,默认终⽌进程
有IO效率低

设置重复闹钟

代码样例

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <vector>
#include <functional>
using func_t = std::function<void()>;
int gcount = 0;
std::vector<func_t> gfuncs;
// 把信号 更换 成为 硬件中断
void hanlder(int signo)
{
	for (auto& f : gfuncs)
	{
		f();
	}
	std::cout << "gcount : " << gcount << std::endl;
	int n = alarm(1); // 重设闹钟,会返回上⼀次闹钟的剩余时间
	std::cout << "剩余时间 : " << n << std::endl;
}
int main()
{
	//gfuncs.push_back([](){ std::cout << "我是⼀个内核刷新操作" << std::endl; });
	//gfuncs.push_back([](){ std::cout << "我是⼀个检测进程时间⽚的操作,如果时间⽚到了,我会切换进程" << std::endl; });
	//gfuncs.push_back([](){ std::cout << "我是⼀个内存管理操作,定期清理操作系统内部的内存碎⽚" << std::endl; });
	alarm(1); // ⼀次性的闹钟,超时alarm会⾃动被取消
	signal(SIGALRM, hanlder);
	while (true)
	{
		pause();
		std::cout << "我醒来了..." << std::endl;
		gcount++;
	}
}
NAME
    pause - wait for signal
SYNOPSIS
    #include <unistd.h>
    int pause(void);
DESCRIPTION
    pause() causes the calling process (or thread) to sleep until a signal
    is delivered that either terminates the process or causes the invoca‐
    tion of a signal-catching function.
RETURN VALUE
    pause() returns only when a signal was caught and the signal-catching
    function returned. In this case, pause() returns -1, and errno is set
    to EINTR
// 窗⼝ 1
yzk@yzk:~/code/test$ ./alarm
我的进程pid是: 216982
剩余时间 : 13 // 提前唤醒它,剩余时间
剩余时间 : 0
剩余时间 : 0
剩余时间 : 0
// 窗⼝ 2
$ kill -14 216982
结论:
闹钟设置⼀次,起效⼀次
重复设置的⽅法
如果时间允许,可以测试⼀下 alarm(0)

如何理解软件条件

在操作系统中,信号的软件条件指的是由软件内部状态或特定软件操作触发的信号产⽣机制。
这些条件包括但不限于定时器超时(如alarm函数设定的时间到达)、软件异常(如向已关闭的管道写数据产⽣的SIGPIPE信号)等。
当这些软件条件满⾜时,操作系统会向相关进程发送相应的信号,以通知进程进⾏相应的处理。
简⽽⾔之,软件条件是因操作系统内部或外部软件操作⽽触发的信号产⽣。

如何简单快速理解系统闹钟

系统闹钟,其实本质是OS必须⾃⾝具有定时功能,并能让⽤⼾设置这种定时功能,才可能实现闹钟这样的技术。

现代Linux是提供了定时功能的,定时器也要被管理:先描述,在组织。内核中的定时器数据结构是:

struct timer_list {
	struct list_head entry;
	unsigned long expires;
	void (*function)(unsigned long);
	unsigned long data;
	struct tvec_t_base_s* base;
};

我们不在这部分进⾏深究,为了理解它,我们可以看到:定时器超时时间expires和处理⽅法 function。

操作系统管理定时器,采⽤的是时间轮的做法,但是我们为了简单理解,可以把它在组织成为"堆结构"。

硬件异常产⽣信号

硬件异常被硬件以某种⽅式被硬件检测到并通知内核,然后内核向当前进程发送适当的信号。例如当前进程执⾏了除以0的指令, CPU的运算单元会产⽣异常, 内核将这个异常解释为SIGFPE信号发送给进程。再⽐如当前进程访问了⾮法内存地址, MMU会产⽣异常,内核将这个异常解释为SIGSEGV信号发送给进程。

模拟除0

#include <stdio.h>
#include <signal.h>
void handler(int sig)
{
	printf("catch a sig : %d\n", sig);
}
// v1
int main()
{
	//signal(SIGFPE, handler); // 8) SIGFPE
	sleep(1);
	int a = 10;
	a /= 0;
	while (1);
	return 0;
}

模拟野指针

[yzk@localhost code_test]$ cat sig.c
#include <stdio.h>
#include <signal.h>
void handler(int sig)
{
	printf("catch a sig : %d\n", sig);
}
int main()
{
	//signal(SIGSEGV, handler);
	sleep(1);
	int* p = NULL;
	*p = 100;
	while (1);
	return 0;
}

[yzk@localhost code_test]$ ./sig
Segmentation fault (core dumped)
[yzk@localhost code_test]$
//捕捉⾏为

[yzk@localhost code_test]$ cat sig.c
#include <stdio.h>
#include <signal.h>
void handler(int sig)
{
	printf("catch a sig : %d\n", sig);
}
int main()
{
	//signal(SIGSEGV, handler);
	sleep(1);
	int* p = NULL;
	*p = 100;
	while (1);
	return 0;
}
[yzk@localhost code_test]$ . / sig
[yzk@localhost code_test]$ . / sig
catch a sig : 11
catch a sig : 11
catch a sig : 11

由此可以确认,我们在C/C++当中除零,内存越界等异常,在系统层⾯上,是被当成信号处理的。

注意:
通过上⾯的实验,我们可能发现:
发现⼀直有8号信号产⽣被我们捕获,这是为什么呢?上⾯我们只提到CPU运算异常后,如何处理后续的流程,实际上 OS 会检查应⽤程序的异常情况,其实在CPU中有⼀些控制和状态寄存器,主要⽤于控制处理器的操作,通常由操作系统代码使⽤。状态寄存器可以简单理解为⼀个位图,对应着⼀些状态标记位、溢出标记位。OS 会检测是否存在异常状态,有异常存在就会调⽤对应的异常处理⽅法。
除零异常后,我们并没有清理内存,关闭进程打开的⽂件,切换进程等操作,所以CPU中还
保留上下⽂数据以及寄存器内容,除零异常会⼀直存在,就有了我们看到的⼀直发出异常信
号的现象。访问⾮法内存其实也是如此,⼤家可以⾃⾏实验。

⼦进程退出core dump

#include <iostream>
#include <string>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/wait.h>
int main()
{
	if (fork() == 0)
	{
		sleep(1);
		int a = 10;
		a /= 0;
		exit(0);
	}
	int status = 0;
	waitpid(-1, &status, 0);
	printf("exit signal: %d, core dump: %d\n", status & 0x7F, (status >> 7) & 1);
	return 0;
}
$ man 7 signal
SIGABRT P1990 Core Abort signal from abort(3)
SIGALRM P1990 Term Timer signal from alarm(2)
SIGBUS P2001 Core Bus error (bad memory access)
SIGCHLD P1990 Ign Child stopped or terminated
SIGCLD - Ign A synonym for SIGCHLD
SIGCONT P1990 Cont Continue if stopped
SIGEMT - Term Emulator trap
SIGFPE P1990 Core Floating-point exception
SIGHUP P1990 Term Hangup detected on controlling terminal
or death of controlling process
SIGILL P1990 Core Illegal Instruction
SIGINFO - A synonym for SIGPWR
SIGINT P1990 Term Interrupt from keyboard
SIGIO - Term I/O now possible (4.2BSD)
SIGIOT - Core IOT trap. A synonym for SIGABRT
SIGKILL P1990 Term Kill signal
SIGLOST - Term File lock lost (unused)
SIGPIPE P1990 Term Broken pipe: write to pipe with no
readers; see pipe(7)
SIGPOLL P2001 Term Pollable event (Sys V).
$ ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 7643
max locked memory (kbytes, -l) 65536
max memory size (kbytes, -m) unlimited
open files (-n) 65535
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 7643
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited

Core Dump

SIGINT的默认处理动作是终⽌进程,SIGQUIT的默认处理动作是终⽌进程并且Core Dump,现在我们来验证⼀下。

⾸先解释什么是Core Dump。当⼀个进程要异常终⽌时,可以选择把进程的⽤⼾空间内存数据全部保存到磁盘上,⽂件名通常是core,这叫做Core Dump。

进程异常终⽌通常是因为有Bug,⽐如⾮法内存访问导致段错误,事后可以⽤调试器检查core⽂件以查清错误原因,这叫做 Post-mortem Debug (事后调试)。

⼀个进程允许 产⽣多⼤的 core ⽂件取决于进程的 Resource Limit (这个信息保存 在PCB中)。默认是不允许产⽣ core ⽂件的, 因为 core ⽂件中可能包含⽤⼾密码等敏感信息,不安全。

在开发调试阶段可以⽤ ulimit 命令改变这个限制,允许产⽣ core ⽂件。 ⾸先⽤ ulimit 命令改变 Shell 进程的 Resource Limit ,如允许 core ⽂件最⼤为 1024K: $ ulimit -c 1024

$ ulimit -c 1024
$ ulimit -a
core file size (blocks, -c) 1024
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 7643
max locked memory (kbytes, -l) 65536
max memory size (kbytes, -m) unlimited
open files (-n) 65535
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 7643
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited

然后写⼀个死循环程序:

前台运⾏这个程序,然后在终端键⼊Ctrl-C(貌似不⾏)或Ctrl-\(介个可以):

ulimit命令改变了Shell进程的Resource Limit,test进程的PCB由Shell进程复制⽽来,所以也具 有和Shell进程相同的Resource Limit值,这样就可以产⽣Core Dump了。 使⽤core⽂件:

总结思考⼀下

上⾯所说的所有信号产⽣,最终都要有OS来进⾏执⾏,为什么?OS是进程的管理者

信号的处理是否是⽴即处理的?在合适的时候

信号如果不是被⽴即处理,那么信号是否需要暂时被进程记录下来?记录在哪⾥最合适呢?

⼀个进程在没有收到信号的时候,能否能知道,⾃⼰应该对合法信号作何处理呢?

如何理解OS向进程发送信号?能否描述⼀下完整的发送处理过程?

【Linux】进程信号,一看就懂!! 进程信号,未决 信号概念 信号的产生 信号注册 信号注销 捕捉处理信号 信号未决 自定义信号处理函数 1)信号概念 查看信号列表:在bash中输入kill -l 一共62种信号,其中方框里的是一些常见的信号 2号信号:SIGINT(interrupt),bash中输入的ctrl+c 3号信号:SIGQUIT,bash中输入的ctrl+|,会产生coredump文件 6号信号:SIGABRT,代码中调用到abort()函数时,使当前进程接收到6号信号而异常终止,double free也会产生6号信号 9 阅读详情

相关推荐

【SAP中的框架协议】

在SAP中,框架协议(Framework Agreement) 是企业与供应商签订的长期采购约束文件,用于简化重复性采购流程、锁定交易条款并降低管理成本。

m0_73548864的博客 1294

【Linux进程#6】:进程信号(详谈信号捕捉 & OS 运行)

✨🌏​​​​​​​1. 信号捕捉的流程 💦​​​​​​​​​​​​​​还记得我们之前说过的吗,处理信号时并不一定立即去处理,有可能此时我们在做一个优先级很高的事情, 此时信号处理就会等到一个合适的时候去处理。现在我们就要来理解一下,什么叫作合适的时候 ❓言归正传,让我们开始讲信号捕捉的具体流程如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为由于信号处理函数的代码是在用户空间的,处理过程比较复杂,举例如下;2. 操作系统是怎么运行的 📚2.1 🍉 硬件中断。

island1314的博客 2626

Win11系统下Abaqus2022与VS2022+oneAPI2023环境配置全攻略(附VUMAT子程序避坑指南)

本文详细指导在Windows 11系统下,为Abaqus 2022配置Visual Studio 2022与Intel oneAPI 2023的Fortran编译环境,以支持VUMAT等用户子程序开发。内容涵盖软件安装顺序、环境变量关联、快捷方式修改及关键验证步骤,并提供了首个VUMAT子程序的实战编写与模型验证方法,帮助用户高效搭建稳定可靠的仿真开发平台。

pytorch8learner的博客 314

【Linux】进程信号

信号就像是一个突然的电话铃声,它会打断正在进行的程序并引起其注意。在Linux系统中,信号是一种软件中断,它通常是异步发生的,用来通知进程某个事件已经发生。每个信号都有一个唯一的编号和一个宏定义名称,这些宏定义可以在signal.h中找到

Renswc的博客 3378

Design Compiler时序约束实战:如何用set_clock_uncertainty解决时钟抖动问题?

本文深入探讨了在Design Compiler时序约束实战中,如何精准运用set_clock_uncertainty命令解决时钟抖动等非理想性问题。文章详细解析了该命令在时钟树综合前后的不同应用策略、建立时间与保持时间的差异化设置,并对比了其与set_clock_jitter的适用场景,旨在帮助数字IC设计工程师制定稳健的时序约束,为后端物理实现预留合理的安全裕量,从而高效达成时序收敛。

weixin_29196891的博客 299

【Altium designer】如何添加过孔阵列

按下图1指示,执行【Tool】菜单栏下【Via Stitching/Shielding】选项卡中的【Add Stitching to Net...】选项 图1 “添加过孔阵列”选项 点击【Add Stitching to Net...】选项后,会跳出一个窗口,让我们设置过孔阵列的参数信息,如下图2所示。 图2 过孔阵列参数设置 过孔阵列区域:勾选该选项框,即可绘制多边形区域; 过孔内径与焊盘直径:比如内径 【Hole Size】设为12mil,焊盘直径【Diameter】设为24mil; .

夜半少年的博客 2万+

【Linux-进程信号】详谈信号捕捉

在中断处理完毕后要返回用户态的main函数之前检测到有信号SIGQUIT递达,内核决定返回用户态后不是恢复main函数的上下文继续执行,而是执行SigHandler函数,SigHandler函数使用户态的堆栈空间,它们之间不存在调用和被调用的关系,是两个独立的控制流程。当某个信号的处理函数被调用时,内核自动将当前信号加入进程的信号屏蔽字,当信号处理函数返回时自动恢复原来的信号屏蔽字,这样就保证了在处理某个信号时,如果这种信号再次产生.,那么它会被阻塞到当前处理结束为止。:指向信号处理函数的指针。

2202_75331338的博客 1352

SAP:SAP的内存管理详解

SAP 的内存管理是一个复杂的多层机制,涉及应用服务器、数据库和操作系统的协同工作。通过合理配置和优化,可以显著提高 SAP 系统的性能和稳定性。

baidu_35680696的博客 1212

【Linux】进程信号(一):信号的产生与信号的保存

【Linux】进程信号(一):信号的产生与信号的保存

tan_run的博客 2589

Bean named‘ddlApplicationRunner‘is expected to be of type ‘org.springframework.boot.Runner‘

springboot3.1.7整合mybatisplus3.5.3.2版本报错,sringboot3.x整合myabtis报错

雾林小妖的博客 1万+

【2026年华为暑期实习(非AI方向(通软&嵌软&测试&算法&数据科学))-4月8日-第一题- 包裹的编码】(题目+思路+Java&C++&Python解析+在线测试)

摘要:该问题要求将给定的十进制整数N转换为混合进制编码字符串,并处理回文检测。首先根据N的符号位确定编码首位(0或1),然后按输入的进制序列依次对N的绝对值进行取余和整除操作,得到编码数组。将数组元素转换为字母后生成字符串,若字符串为回文则添加"(palindrome)"后缀;否则输出最长回文子串(字典序最小)。算法采用中心扩展法检测回文,时间复杂度为O(L²),空间复杂度为O(L),其中L为字符串长度。

m0_73659489的博客 129

【Linux】八、Linux进程信号详解(一)_ 认识信号 | 产生信号

认识信号|产生信号详解

枫叶先生的博客 2669

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

【Linux 17】进程信号

1. 进程的运行。2. 信号的产生。3.信号的捕捉。4. 信号的阻塞 (保存)

shangguanxiu的博客 2395

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

Linux进程信号

信号的产生 1.通过终端按键产生信号 如下代码: 1 #include <stdio.h> 2 #include <unistd.h> 3 int main(){ 4 while(1){ 5 printf("i am test process\n"); 6 sleep(1); 7

Enthusiastic_boy的博客 3761

Linux 详谈Linux进程信号(二)

本文主要探讨操作系统中的信号处理机制和中断系统。信号捕捉部分详细解释了用户自定义信号处理函数的执行流程,包括内核态与用户态的切换过程,并介绍了sigaction函数的使用方法。中断系统部分阐述了硬件中断、时钟中断和软中断的工作原理,揭示了操作系统通过中断驱动的本质,分析了系统调用通过软中断实现的机制。文章还讨论了可重入函数的概念及条件,并通过实例展示了volatile关键字在防止编译器优化、确保内存可见性方面的重要作用。整体上,文章从底层角度剖析了操作系统处理信号和中断的核心机制,帮助理解系统调用的实现原理

yuzhaokangcode的博客 971

【Linux】实验四 进程信号通信

1、了解什么是信号 2、了解和熟悉 LINUX 支持的信号量机制 3、熟悉 LINUX 系统中进程之间软中断通信的基本原理

m0_55251293的博客 2973

Linux的进程信号

主要是关于进程信号的接口使用和信号的深层理解...

limou3434的博客 2185

<Linux> 进程信号

关于进程信号的概念,分产生、保存、处理三个阶段

weixin_61522065的博客 1354

Linux——进程信号(一)

什么是信号?结合实际红绿灯、闹钟、游戏中的"!"等等这些都是信号。以红绿灯为例子:一看到红绿灯我们就知道:红灯停、绿灯行;我们不仅知道它是一个红绿灯而且知道当其出现不同的状况时我们应该做出怎么样的行为去应对。对于红绿灯:我们之前受过的教育,让我们能够识别这个红绿灯(即使它还未出现在我们眼前),也就是说你能识别"红绿灯"当绿灯量了后我们不一定要直接走,也可以等一会再走(也就是说接收信号后我们不一定要立刻产生对应的行为)当红灯亮了之后,我们可以玩手机,要记得这个时候还是红灯。

m0_74515566的博客 1629
上一篇: Linux 详谈进程间通信(二)
下一篇: Linux 详谈Linux进程信号(二)
万我.
博客等级 码龄1年 460粉丝 93原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值