✨✨✨学习的道路很枯燥,希望我们能并肩走下来!
文章目录
目录
编辑 4.3.5 缺⻚中断?内存碎⽚处理?除零野指针错误?
前言
本篇详细介绍了进一步介绍Linux的进程间通信,让使用者有更加深刻的认知,而不是仅仅停留在表面,更好的模拟,为了更好的使用. 文章可能出现错误,如有请在评论区指正,让我们一起交流,共同进步!
一 信号快速认识
1.1 生活角度的信号
• 你在⽹上买了很多件商品,再等待不同商品快递的到来。但即便快递没有到来,你也知道快递来临 时,你该怎么处理快递。也就是你能“识别快递”
• 当快递员到了你楼下,你也收到快递到来的通知,但是你正在打游戏,需5min之后才能去取快递。 那么在在这5min之内,你并没有下去去取快递,但是你是知道有快递到来了。也就是取快递的⾏为 并不是⼀定要⽴即执⾏,可以理解成“在合适的时候去取”。
• 在收到通知,再到你拿到快递期间,是有⼀个时间窗⼝的,在这段时间,你并没有拿到快递,但是 你知道有⼀个快递已经来了。本质上是你“记住了有⼀个快递要去取”
• 当你时间合适,顺利拿到快递之后,就要开始处理快递了。⽽处理快递⼀般⽅式有三种:1.执⾏默 认动作(幸福的打开快递,使⽤商品)2.执⾏⾃定义动作(快递是零⻝,你要送给你你的⼥朋友) 3. 忽略快递(快递拿上来之后,扔掉床头,继续开⼀把游戏)
• 快递到来的整个过程,对你来讲是异步的,你不能准确断定快递员什么时候给你打电话
1.2 技术应用角度的信号
1.2.1 样例代码
// 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获取,解释成信号,发送给目标前台进程
• 前台进程因为收到信号,进⽽引起进程退出(退出前台进程)
1.2.2 系统函数signal
#include <iostream>
#include <unistd.h>
#include <signal.h>
void handler(int signumber)
{
std::cout << "我是: " << getpid() << ",我获得了⼀个信号: " << signumber << 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);
}
}
注意:
• 要注意的是,signal函数仅仅是设置了特定信号的捕捉⾏为处理⽅式,并不是直接调⽤处 理动作。如果后续特定信号没有产⽣,设置的捕捉函数永远也不会被调⽤!!
• Ctrl+C 产⽣的信号只能发给前台进程。⼀个命令后⾯加个&可以放到后台运⾏,这样 Shell不必等待进程结束就可以接受新的命令,启动新的进程。
• Shell可以同时运⾏⼀个前台进程和任意多个后台进程,只有前台进程才能接到像 Ctrl+C 这种控制键产⽣的信号。
• 前台进程在运⾏过程中⽤⼾随时可能按下 Ctrl-C Ctrl-C ⽽产⽣⼀个信号,也就是说该进程的⽤⼾空间代码执⾏到任何地⽅都有可能收到 SIGINT 信号⽽终⽌,所以信号相对于进程的控制流程来说是异步(Asynchronous)的。
• 关于进程间关系,我们在⽹络部分会专⻔来讲,现在就了解即可。
• 可以渗透&和nohup
1.3 信号概念
信号是进程之间事件异步通知的⼀种⽅式,属于软中断。
1.3.1 查看信号
每个信号都有⼀个编号和⼀个宏定义名称,这些宏定义可以在signal.h中找到,例如其中有定义
#define SIGINT 2
编号34以上的是实时信号,本章只讨论编号34以下的信号,不讨论实时信号。这些信号各⾃在什么条件 下产⽣,默认的处理动作是什么,在signal(7)中都有详细说明: man 7 signal
1.3.2 信号处理
(sigaction函数稍后详细介绍),可选的处理动作有以下三种:
1. 忽略此信号
2. 执⾏该信号的默认处理动作
3. 提供⼀个信号处理函数,要求内核在处理该信号时切换到⽤⼾态执⾏这个处理函数,这种⽅式称为⾃定义捕捉(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.1 通过终端按键产⽣信号
2.1.1 基本操作
• Ctrl+C(SIGINT) 已经验证过,这⾥不再重复
• Ctrl+\(SIGQUIT)可以发送终⽌信号 并⽣成coredump⽂件,⽤于事后调试(后⾯详谈)
• Ctrl+Z(SIGTSTP)可以发送停⽌信号,将当前前台进程挂起到后台等。
2.1.2 理解OS如何得知键盘有数据
2.1.3 初步理解信号起源
注意:
• 信号其实是从纯软件⻆度,模拟硬件中断的⾏为
• 只不过硬件中断是发给CPU,⽽信号是发给进程
• 两者有相似性,但是层级不同,这点我们后⾯的感觉会更加明显
2.2 调⽤系统命令向进程发信号
#include <iostream>
#include <unistd.h>
#include <signal.h>
int main()
{
while (true) {
sleep(1);
}
}
首先在后台执⾏死循环程序,然后⽤kill命令给它发SIGSEGV信号。
2.3 使⽤函数产⽣信号
2.3.1 kill
kill命令是调用kill函数执行的,kill函数可以给⼀个指定的进程发送指定的信号。