linux--------------进程信号(上)

发布于:2025-07-04 ⋅ 阅读:(14) ⋅ 点赞:(0)

1.信号的快速认识

1-1 生活的角度来看待信号

就比如我们在网上买东西,在等待不同的快递到来的时候,即使快递没有到来我们也能够识别到快递再比如在我们日常生活中我们在等待红绿灯的时候就已经知道了信号

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

1-2 技术应用角度的信号

下面大家看一个实例

#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

在运行的时候我们按下ctrl+c是不是向操作系统发送了一个信号只不过这个信号是用来终止进程的

⽽其实, 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

1-3 信号概念

大家可以输入 kill -l来查看信号

每个信号都有⼀个编号和⼀个宏定义名称,这些宏定义可以在signal.h中找到,例如其中有定义
#define SIGINT 2
编号34以上的是实时信号,本章只讨论编号34以下的信号,不讨论实时信号。这些信号各⾃在什么条件
下产⽣,默认的处理动作是什么,在signal(7)中都有详细说明
1-3-2忽略信号
( sigaction 函数稍后详细介绍),可选的处理动作有以下三种:
signal (SIGINT /*2*/ , SIG_IGN);
#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);
}
}

2 产生信号

2-1 信号通过终端按键来产生

2-1-1 基本操作

就如同上面我们按ctrl+c就是向操作系统发送信号

Ctrl+\(SIGQUIT)可以发送终⽌信号并⽣成core dump⽂件,⽤于事后调试(后⾯详谈)
Ctrl+Z(SIGTSTP)可以发送停⽌信号,将当前前台进程挂起到后台等。

2-1-3 初步理解信号起源

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

2-2 使函数产生信号

2-2-1 kill

kill命令是调用kill函数来实现的 kill可以给指定一个进程发送指定信号
样例:实现⾃⼰的 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;
}

2-2-2  raise

raise 函数可以给当前进程发送指定的信号(⾃⼰给⾃⼰发信号)。
样例:
#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);
}
}

2-2-3 模拟野指针

我们平常在写代码的时候出现了野指针程序是不是也会崩溃崩溃也是一种信号我们来模拟一下

#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;
}
[hb@localhost code_test]$ ./sig
Segmentation fault (core dumped)
[hb@localhost code_test]$
//捕捉⾏为
[hb@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;
}
[hb@localhost code_test]$ ./sig
[hb@localhost code_test]$ ./sig
catch a sig : 11
catch a sig : 11
catch a sig : 11
由此可以确认,我们在C/C++当中除零,内存越界等异常,在系统层⾯上,是被当成信号处理的。

网站公告

今日签到

点亮在社区的每一天
去签到