目录
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向进程发送信号?能否描述⼀下完整的发送处理过程?
1294



被折叠的 条评论
为什么被折叠?



