🎁个人主页:我们的五年****
🔍系列专栏:Linux课程学习****
🌷追光的人,终会万丈光芒
🎉欢迎大家点赞👍评论📝收藏⭐文章
Linux学习笔记:
https://blog.csdn.net/djdjiejsn/category_12669243.html
前言:
信号这专题也是很重要的,对之前学过的知识再次关联起来,形成闭环,对理解Linux操作系统有很大的帮助。信号的学习包含:1.信号的产生,2.信号的保存,3.信号的处理这三方面。关于信号的保存,在没学信号之前可能不太理解,学了以后,这步是必不可少的,因为在收到信号以后,因为block,可能不会马上处理。下面我们进行详细的讲解。
目录
[4.1kill -l查看所有的信号:](#4.1kill -l查看所有的信号:)
一.信号入门-生活角度的信号:
对于日常生活来说,平常的闹钟,语言,铃声......这些都是信号,对于如何从生活中的信号去和Linux操作系统中的信号进行联系,我引入了下面这一个例子:
在我们点好外卖以后,外卖小哥会接单,商家会处理。但是我们点好外卖以后就可以去等外卖送到门口了,我们不需要关心怎么送到的,其他等问题,我们就和外卖小哥形成了异步。等外卖到了以后,会有电话或者门铃等信号告知我们外卖来了。但是我们不会马上处理这个信号,也就是说,但是我们心中是知道有这一个信号来了的。多久处理就是看其他各种情况了。在过了一会以后,我们处理这个信号,我们可以把外卖拿进去吃,也可以拿进去不吃,也可以干脆不去拿,忽略这一个信号了(啥都不干)。所以这就是处理信号。然后处理信号之前就是信号的保存等问题了。
二.技术角度的信号:
2.1键盘Ctrl+c产生信号:
在我们启动一个shell进程,然后运行一个死循环的程序。如果我们想要这个进程(前台进程)退出,我们就可以在键盘下按下Ctrl+c(Ctrl+c其实就是2号信号,SIGINT),就可以杀死进程了。它的逻辑先是这样的:
1.键盘按Ctrl+c组合键,键盘产生一个中断。
2.这个终端被操作系统获取,其实CPU的针脚能察觉到,然后OS操作系统可以拿到这个信息。
3.OS就可以去给一个进程发信号。
发信号的本质:去改进程PCB中的pending位图。
Ctrl+c:不能终止后台进程。
三.前台进程和后台进程
|------|-----------------------|
| 前台进程 | 进程在终端或用户界面中运行 |
| 后台进程 | 不占用用户的终端 |
3.1前台进程:
🍟前台进程的定义:
前台进程是用户启动并且需要保持与用户交互的进程。这些进程在终端或用户界面中运行,用户可以直接控制和管理它们。
🍟产生方式:
一般./test运行的就是前段进程。
🍟特点:
1.占用终端:前台进程会一直占用终端,直到它运行结束或者被暂停(如通过Ctrl+Z组合键)。在它运行期间,终端不能用于其他操作,除非暂停或终止这个前台进程。
**2.对终端信号敏感:**前台进程会接收并处理终端发送的信号。例如,当用户在终端中按下Ctrl+C组合键时,前台进程会收到SIGINT(中断信号)并通常会终止运行,除非它对这个信号进行了特殊的处理(如信号捕获和忽略)。
3.状态显示:当前台进程正常运行时,在ps命令的输出中(如ps -ef)显示为R状态,表示正在运行并占用CPU资源。如果通过Ctrl+Z组合键暂停了前台进程,它的状态会变为T,表示停止运行。
3.2后台进程:
🍟后台进程的定义:
后台进程是在后台运行的进程,它们不占用用户的终端,用户也不需要直接与之交互。后台进程通常用于执行一些不需要用户立即关注的任务,比如长时间的计算任务、数据备份任务、服务器的守护进程等。
🍟产生方式:
在前台进程后面加上**&符号**就是后台进程。
🍟特点:
1.不占用终端I/O:后台进程不会阻止用户在终端进行其他操作,它的输出信息(标准输出和标准错误输出)可以通过重定向的方式保存到文件中,这样就不会在终端显示,干扰用户的其他操作。例如,可以使用">output.log 2>error.log"来分别将标准输出和标准错误输出重定向到output.log和error.log文件中。
2.对信号处理方式不同:后台进程也会接收信号,但对一些信号的默认处理方式可能和前台进程不同。例如,后台进程一般不会因为Ctrl+C而终止,除非它专门对SIGINT信号进行了处理。
3.状态显示:后台进程在正常运行时也显示为R状态,不过它不会占用终端的I/O设备。和前台进程类似,后台进程也可以被暂停,状态变为T。可以使用bg命令让它在后台继续运行,或者用fg命令将其恢复到前台运行。
3.3关于前台进程和后台进程的命令:
fg //把后台进程放到前台。
bg //让暂停的后台进程继续运行。
jobs //查看前后台进程的信息
四.信号的分类
4.1kill -l查看所有的信号:
如果要查看更详细的信号信息,可以在man 7 signal中查看
下面就表示信号的分类,其实这些都是定义的宏,用数字也是一样的。
对于34号以后的信号是实时信号,我们这里不理解。主要研究的是34号以下的,1到31号。这里有31个信号,用一个int位图就能表示出来。
刚刚我们使用的信号:
1.Ctrl+c:SIGINT(2号信号)
2.Ctrl+\:SIGQUIT(3号信号)
4.2信号的终止:
信号终止一般有core和term终止。在man 7 signal中就会有详细的介绍,里面就会有是core终止还是term终止进程。
core和term终止进程基本差不多,唯一不同的就是:
如果进程发生core终止,可能会生成core文件。
但是必须是我们先把core file的大小调成大于0,这样才能生成core文件!!!
ulimit -c 1024:把core file文件改成1024字节。
ulimit -a:查看相关信息。
五.信号处理的常见方式
5.1信号处理的方式有三种:
1.忽略此信号。
2.执行该信号的默认处理动作。
3.提供一个信息处理函数,要求内核在处理这个信号的时候,切换到用户态执行这个函数,这种方式成为捕捉。
5.2对信号进行自定义处理:
对信号进行自定义处理的函数是:
头文件:
#include <signal.h>
函数原型:
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
函数解释:
1.signum表示的是要对几号信号进行处理。
2.handler是一个函数指针,表示的是自定义的方式。
typedef void (*sighandler_t)(int);
它的类型是一个返回值为void,参数为int的函数指针。
也可以传这些参数:
SIG_IGN:表示的是处理信号的方式是忽略此信号。
SIG_DFL:执行该信号的默认行为。
5.3对signal的应用
对signal的应用,我们可以通过命令行参数,就可以在shell命令行中对指定的信号进行指定的修改。
我们还可以通过for循环对31个信号的执行方式进行修改。那么以后OS给我们写的程序的进程发的终止信号就不起作用了。
那是不是我们的进程就一直不会被杀掉呢?
答案:9号信号是不运行我们进行修改的,它的行为会一直是终止进程。所以我们的进程是肯定能被杀死的。
下面是关于signal的应用:
#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
void Handler(int signo)
{
std::cout << "l love xy!signo:" << signo << std::endl;
}
void Uuage(std::string s)
{
std::cout << "please use:" << std::endl;
std::cout << s << " signo" << " pid" << std::endl;
}
int cout = 0;
void die(int signo)
{
std::cout << " cout:" << cout << std::endl;
abort();
}
int main(int argc, char *argv[])
{
// 服务器,一秒可以累加多少次
alarm(100);
signal(SIGINT, die);
sleep(5);
unsigned int n = alarm(5);
std::cout << n << std::endl;
// abort();
// std::cerr << "kill" << std::endl;
// // raise(9);
// if (argc != 3)
// {
// Uuage(argv[0]);
// exit(1);
// }
// //-signum 信号的类型
// int signum = std::stoi(argv[1] + 1);
// // std::cout << signum << std::endl;
// // pid
// pid_t id = std::stoi(argv[2]);
// int n = ::kill(id, signum);
// if (n < 0)
// {
// std::cerr << "kill" << std::endl;
// }
// 如果一直没有调用,那么Handler就一直不会被调用!
// 不要放在循环中,因为只要进行说明一次,就会进行保存!
// 九号信号是不能被捕捉的!!!
// 不然如果把终止进程的信号全部捕捉,一个进程就不能被杀死
// for (int i = 1; i <= 31; i++)
// {
// signal(i, Handler);
// }
// while (1)
// {
// std::cout << "pid:" << getpid() << std::endl;
// sleep(1);
// }
// int num = 0;
// while (1)
// {
// std::cout << "num:" << num << std::endl;
// sleep(1);
// ++num;
// }
return 0;
}
到这里文章就基本结束了,作为C++程序员,请保持持续的学习,每天的算法练习!在这里欢迎大家和我一起交流问题,共同进步。
🌷🌷🌷🌷🌷🌷🌷🌷🌷🌷🌷🌷🌷🌷🌷