字节一面,操作系统这题没答好,可惜了

news2024/12/23 5:13:20

问题引入:

在曾经我们学习Linux的经历中,我们也是多次使用信号的。比如:当我们在使用xshell时,在命令行中按Ctrl+c,这个键盘输入产生了一个硬件中断,被操作系统获取,解释成信号,发送给目标前台进程。前台进程因为收到了信号,进而引起进程退出。

注意:Ctrl+C 产生的信号只能发给前台进程。一个命令后面加个&可以放到后台运行,这样Shell不必等待进程结束就可以接受新的命令,启动新的进程。Shell可以同时运行一个前台进程和任意多个后台进程,只有前台进程才能接到像Ctrl+C这样控制键产生的信号。前台进程在运行过程中用户随时可能按下Ctrl+C而产生一个信号,也就是说该进程的用户空间代码执行到任何地方都有可能收到SIGINT信号而终止,所以信号相对于进程的控制流程来说是异步(Asynchronous)的。

将进程设置为后台进程

./进程名 &

查看后台进程并将后台进程提至前台

我们发现进程一旦被设置成为后台进程是无法杀掉的,此时在一直死循环的打印hello myproc.那么我们怎么杀掉这个进程呢?有一种方法是将后台进程设置成为前台进程,再使用ctrl +c 关闭。这种方法也比较好理解。因此哦我们在此处将使用这种方法关闭后台进程。

首先我们要查看此时都有什么任务在执行,我们可以输入 jobs 来查看当前任务列表

其次我们将这个进程提至提至前台,使用 fg 1 ,其中1是任务编号,也就是 [ ]+ 中的数字,注意fg 和 1之间需要带空格。

将前台进程设置为后台进程

我们刚刚知道了将后台进程提至前台使用 fg 任务号,f是front的意思。因此我们如果想把前台进程设置为后台进程,可以使用bg 任务号,我们可以来测试一下

1.信号的概念

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

2.查看信号列表

使用kill -l 命令可以查看系统定义的信号列表

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

  2. 编号34以上的是实时信号。在此我们只讨论编号34以下的信号,不讨论实时信号。这些信号各自在什么条件下产生默认动作是什么,在signal(7)中都有详细说明:man 7 signal

3.信号处理的常见方式

由于信号产生时是异步的,当产生信号的时候,对应的进程可能正在做着更重要的事情,因此这个进程可以暂时不处理这个信号!进程正在做着更重要的事情说明进程可能不需要理解处理这个信号!但是不代表这个信号不会被处理。进程是一定要记住这个信号已经来了(信号有吗?什么信号?)。因此信号处理的常见方式有以下三种:

  1. 忽略此信号

  2. 执行该信号的默认处理动作

  3. 提供一个信号处理函数,要求内核在处理该信号时切换到用户态执行这个处理函数,这种方式成为捕捉信号(Catch)。

4.信号的产生

我们在前面提到了我们现在只谈论1-31号信号。那么进程要处理一个信号,肯定是先描述,再组织。那么进程是如何记住这个信号的呢?当然是保存在进程的PCB中。由于我们只考虑1-31号进程,因此在进程的tast_struct中有一个uint32_t sig来表示信号。这里使用了位图的思想。什么信号产生我们使用的是比特位的位置。那么怎么判断有没有比特位的产生我们通过比特位的内容,1表示产生,0表示没有产生。因此一个uint_32足以表示1-31个信号了。

而tast_struct是内核的数据结构,因此只有操作系统有权利获得进程的所有属性。所以进程的整个生命周期,无论信号怎么产生,只能是操作系统帮我们进行信号的设置。

用户层产生信号的方式

通过终端按键产生信号

产生信号的第一种方式是通过终端按键产生信号,也就是键盘。系统也为我们提供了一个signal函数,可以捕捉我们产生的信号,接下来我们将使用signal这个函数验证以下终端按键是可以产生信号的。

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

我们可以使用函数产生信号。我们可以使用signal函数向进程发送信号。

其中handler是函数指针(回调方法),是可以让我们用户自定义处理信号的接口(处理信号的第三种方式)

我们在C++代码中看看如何使用signal函数,在下面这段代码中,我们设置了如果进程接受到了SIGINT信号,就会调用handler函数从而输出指定内容。我们都是SIGINT是2号信号,键盘上按Ctrl +C 本质就是给前台进程发送2号信号,因此当进程跑起来的时候,我们按Ctrl + C时,程序会调用hanlder方法打印指定内容,我们来看看结果吧。

#include <iostream>
#include <signal.h>
#include <unistd.h>
 
using namespace std;
 
void handler(int signo)
{
    cout<<"我是一个进程,刚刚获取了一个信号,信号编号是: "<<signo <<endl;
}
 
int main()
{
    signal(SIGINT,handler);
 
    sleep(3);
    cout<<"进程已经设置完了"<<endl;
    sleep(3);
    while(true)
    {
        cout<<"我是一个运行中的进程,我的pid: "<<getpid()<<endl;
        sleep(2);
    }
    
    return 0;
}

注意:这里当SIGINT产生的时候,才会调用hanlder方法。

在2号信号被我们自定义设置的时候,进程按2号信号是不会执行默认动作的,因此此时我们向终止进程不能使用ctrl + c了,我们可以按ctrl + \ , ctrl + \ 是给进程产生3号信号,我们也可以自定义3好信号。按这样的道理,我们可以将31个信号都自定义,那么这个进程是不是就可以不被kill 掉呢?其实不是的!其中9号进程是不能被自定义设置的。因为操作系统要保护自己所以肯定不能将所有的信号都自定义。

#include <iostream>
#include <signal.h>
#include <unistd.h>
 
using namespace std;
 
void handler(int signo)
{
    cout<<"我是一个进程,刚刚获取了一个信号,信号编号是: "<<signo <<endl;
}
 
int main()
{
    for(int sig=1;sig<=31;sig++)
    {
        signal(sig,handler);
    }
 
    //signal(SIGINT,handler);
 
    sleep(3);
    cout<<"进程已经设置完了"<<endl;
    sleep(3);
    while(true)
    {
        cout<<"我是一个运行中的进程,我的pid: "<<getpid()<<endl;
        sleep(2);
    }
 
    return 0;
}

那么我们知道了键盘可以产生信号,那么是谁给进程发送的信号呢?答案当然是OS操作系统

调用系统函数向进程发信号(kill,raise,abort)

使用kill函数发送信号

我们知道kill命令可以给一个进程发信号,例如我们之前经常使用kill -9 pid 来杀死进程编号为pid的进程。同时kill也是一个系统函数。我们也可以通过软件的方式,调用kill函数从而给指定进程发信号。

我们使用一段C++代码来演示一下kill函数是如何使用的:我们的计划是通过调用kill函数给指定进程发信号,模拟实现kill命令。因此当程序跑完是,如果输入./mykill 9 pid时候可以杀掉指定pid进程。

#include <iostream>
#include <cstring>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
 
#include <unistd.h>
 
using namespace std;
 
void handler(int signo)
{
    cout<<"我是一个进程,刚刚获取了一个信号,信号编号是: "<<signo <<endl;
}
 
 
static void Usage(const std::string &proc)
{
    cerr<<"Usage:\n\t" << proc << "signo pid"<<endl;
}
 
//我想写一个kill命令
// ./mykill 9 pid
int main(int argc,char* argv[])
{
    if(argc != 3) 
    {
        Usage(argv[0]);
        exit(1);
    }
 
    if(kill(static_cast<pid_t>(atoi(argv[2])),atoi(argv[1])) == -1)
    {
        cerr<<"kill :" <<strerror(errno) << endl;
    }
    return 0;
}

使用raise函数

raise是一个自取函数,是进程自己给自己发送信号。因此我们在代码中使用一下,加入我们要给自己发送2号信号。

#include <iostream>
#include <cstring>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
 
#include <unistd.h>
 
using namespace std;
 
void handler(int signo)
{
    cout<<"我是一个进程,刚刚获取了一个信号,信号编号是: "<<signo <<endl;
}
 
 
static void Usage(const std::string &proc)
{
    cerr<<"Usage:\n\t" << proc << "signo pid"<<endl;
}
 
//我想写一个kill命令
// ./mykill 9 pid
int main(int argc,char* argv[])
{
    signal(2,handler);//没有调用对应的handler方法,仅仅是注册
 
 
    while(true)
    {
        sleep(1);
        raise(2);//自己给自己发送2号信号
    }
    return 0;
}

使用abort函数

abort是想自己发送SIGABRT信号 -- 6号信号 我们也在代码中调用使用abort函数,并且捕捉以下该信号。

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
 
#include <unistd.h>
 
using namespace std;
 
void handler(int signo)
{
    cout<<"我是一个进程,刚刚获取了一个信号,信号编号是: "<<signo <<endl;
}
 
 
static void Usage(const std::string &proc)
{
    cerr<<"Usage:\n\t" << proc << "signo pid"<<endl;
}
 
//我想写一个kill命令
// ./mykill 9 pid
int main(int argc,char* argv[])
{
    signal(6,handler);//没有调用对应的handler方法,仅仅是注册
 
 
    while(true)
    {
        sleep(1);
        abort();
        //raise(2);//自己给自己发送2号信号
    }
    return 0;
}

通过结果发现:我们刚刚确实捕捉到了6号信号,但是进程依然退出了。因此除了9号信号可以让进程退出。6号信号也可以。

由软件条件产生信号

alarm

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

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
 
#include <unistd.h>
 
using namespace std;
int main(int argc,char* argv[])
{
    alarm(3);//3秒后alarm
    int cnt = 0;
    while(true)
    {
        cout<< "我是一个进程 cnt : "<<cnt++<<endl;
        sleep(1);
    }
    return 0;
}

我们调用了alarm函数,经过3秒后闹钟会响,我们没有改变alarm的动作,因此当闹钟响起时,alarm函数会执行默认的终止进程的操作,因此经过3秒后,进程会自动退出。

[Lxy@VM-20-12-centos 11-28]$ ./mykill 
我是一个进程 cnt : 0
我是一个进程 cnt : 1
我是一个进程 cnt : 2
Alarm clock

由硬件异常产生信号

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

当我们日常在写代码的时候,如果写了一个/0错误,进程会怎么样呢?

    int a = 10;
    cout<< a/0<<endl;

当我们运行时,直接会告诉我们浮点数异常!因此我们之前所写的C/C++代码,我们说程序崩溃了,现在我们站在系统的角度上面我们可以知道,所谓的程序崩溃说法不是很准确,其实是进程崩溃,进程崩溃的本质是该进程收到了异常信号!我们写两个进程崩溃的例子。

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
 
#include <unistd.h>
 
using namespace std;
int cnt = 0;
void handler(int signo)
{
    cout<<"我是一个进程,刚刚获取了一个信号,信号编号是: "<<signo << endl;
}
 
 
static void Usage(const std::string &proc)
{
    cerr<<"Usage:\n\t" << proc << "signo pid"<<endl;
}
 
//我想写一个kill命令
// ./mykill 9 pid
int main(int argc,char* argv[])
{
    for(int sig = 1;sig<=31;sig++) signal(sig,handler);
    int a = 10;
    cout<< a/0<<endl;
    return 0;
}

我们再来写一个数组的越界访问,看看效果

    int a[100];
    a[10000000] = 100;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/77615.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

两百行C++代码实现yolov5车辆计数部署(通俗易懂版)

这周用opencv简单实现了一下基于yolov5检测器的车辆计数功能&#xff0c;方法是撞线计数&#xff0c;代码很简单一共就两百多行&#xff0c;测试视频是在b站随便下载的。注&#xff1a;该代码只能演示视频demo效果&#xff0c;一些功能未完善&#xff0c;离实际工程应用还有距离…

JMeter整体综述

JMeter整体综述1. JMeter体系结构及运行原理1.1 主要的组件1.2 运行原理2. 元件执行顺序和作用域2.1 元件执行顺序2.2 元件执行作用域3. 参考1. JMeter体系结构及运行原理 负载模拟&#xff1a;负责模拟用户请求。如取样器有参数化的需求&#xff0c;可通过配置元件或前置处理器…

15.JavaScript 01

文章目录一、概念二、功能三、 JavaScript发展史四、 ECMAScript&#xff1a;客户端脚本语言的标准1、基本语法1. 与html结合方式2. 注释3. 数据类型4. 变量5. 运算符6. 流程控制语句7. JS特殊语法8. 练习&#xff1a;99乘法表2、基本对象1. Function&#xff1a;函数(方法)对象…

上线3天,下载4万,ChatGPT 中文版VSCode插件来了

ChatGPT 的 Debug 功能&#xff0c;有人应用化了。 ChatGPT 这几天可谓是风头无两。作为一个问答语言模型&#xff0c;它最大的优点就是可以回答与编程相关的问题&#xff0c;甚至回复一段代码。 尽管有人指出 ChatGPT 生成的代码有错误&#xff0c;但程序员们还是对它写代码、…

图像配准开源数据集资源汇总

Brown 数据集 数据集下载链接&#xff1a;http://suo.nz/3042bh 数据集由 1024 x 1024 位图 (.bmp) 图像组成&#xff0c;每个图像包含一个 16 x 16 图像块阵列。每个补丁都被采样为 64 x 64 灰度&#xff0c;具有规范的比例和方向。 ETHZ Toys 数据集下载链接&#xff1a…

Java中的语法糖(真甜)

什么是语法糖&#xff08;Syntactic sugar&#xff09; 语法糖是一个计算机数据&#xff0c;特指在编程语言中添加的某种语法&#xff0c;这种语法对语言的功能没有影响&#xff0c;但是更方便程序员使用。语法糖让程序更加简洁&#xff0c;有更高的可读性。 糖嘛&#xff0c…

08.DashBoard流监控配置

08.DashBoard流监控配置 每个服务提供者都需要实现actuator&#xff0c;才可以实现流量监控。 导入Maven依赖 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-hystrix</artifactId><…

【图像去噪】空域+频域滤波图像去噪【含GUI Matlab源码 914期】

⛄一、获取代码方式 获取代码方式1&#xff1a; 完整代码已上传我的资源&#xff1a;【图像去噪】基于matlab GUI空域频域滤波图像去噪【含Matlab源码 914期】 获取代码方式2&#xff1a; 通过订阅紫极神光博客付费专栏&#xff0c;凭支付凭证&#xff0c;私信博主&#xff0…

移动端防抓包实践

目录介绍 01.整体概述介绍 1.1 项目背景1.2 思考问题1.3 设计目标1.4 收益分析 02.市面抓包的分析 2.1 Https三要素2.2 抓包核心原理2.3 搞定CA证书2.4 突破CA证书校验2.5 如何搞定加解密2.6 Charles原理2.7 抓包原理图2.8 抓包核心流程 03.防止抓包思路 3.1 先看如何抓包3.2 …

C++计算机视觉库OpenCV在Visual Studio 2022的配置方法

本文介绍在Visual Studio 2022中配置、编译C 计算机视觉库OpenCV的方法。 1 OpenCV库配置 首先&#xff0c;我们进行OpenCV库的下载与安装。作为一个开源的库&#xff0c;我们直接在其官方下载网站&#xff08;https://opencv.org/releases/&#xff09;中进行下载即可&#x…

实现无入侵式C++代码mock工具

为了实现真正无侵入式的mock&#xff0c;我们基于开源Hook框架Frida-gum提供的API&#xff0c;利用C模板进行封装&#xff0c;作者编写了一个简单实用的mock工具&#xff0c;在此开源分享&#xff08;代码详见附录&#xff09;。背景在单元测试中&#xff0c;往往需要减少被测函…

如何实现高性能网络编程-ChatGPT怎么看

hi ,大家好&#xff0c;我是大师兄。听说最近chatgpt特别火&#xff0c;那我们邀请一下chatgpt如何实现&#xff1a;我们先来小试牛刀&#xff1a;刚开始用先用英文交流一下&#xff0c;然后试一下中文&#xff1a;元芳你怎么看&#xff1f;下期直播主题--网络编程 (如何实现高…

用 AWTK 和 AWPLC 快速开发嵌入式应用程序 (7)- 用状态机实现红绿灯

AWPLC 目前还处于开发阶段的早期&#xff0c;写这个系列文章的目的&#xff0c;除了用来验证目前所做的工作外&#xff0c;还希望得到大家的指点和反馈。如果您有任何疑问和建议&#xff0c;请在评论区留言。 1. 背景 AWTK 全称 Toolkit AnyWhere&#xff0c;是 ZLG 开发的开源…

kaggle实战:基于超市消费数据的用户个性化分析案例

大家好&#xff0c;今天给大家分享一篇 kaggle 数据集的新文章&#xff1a;基于一份超市消费数据集的用户个性化分析以及用户分群的实现。 更多详细内容参考原数据集地址&#xff1a; https://www.kaggle.com/code/sonalisingh1411/customer-personality-analysis-segmentati…

实验八 网络优化与正则化(3)不同优化算法比较

目录7.3 不同优化算法的比较分析7.3.1 优化算法的实验设定7.3.1.1 2D可视化实验7.3.1.2 简单拟合实验7.3.1.3 与Torch API对比&#xff0c;验证正确性7.3.2 学习率调整7.3.2.1 AdaGrad算法7.3.2.2 RMSprop算法7.3.3 梯度估计修正7.3.3.1 动量法7.3.3.2 Adam算法7.3.4 不同优化器…

java基于Springboot的简历系统-计算机毕业设计

项目介绍 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;简历系统当然也不能排除在外。简历系统是以实际运用为开发背景&#xff0c;运用软件工程原理和开发方法&#xff0c;…

SAP ABAP CDS view Association 引入的缘由

ABAP CDS view 支持三种 join 方式&#xff1a; Inner JoinLeft Outer joinRight outer join 我们使用 ABAP Development Tool 的 CDS view 向导创建一个 CDS view&#xff1a; 向导里包含的 $ 和大括号就是占位符&#xff0c;需要开发人员自己指定&#xff1a; 我们把占位符…

奇舞周刊475期:2022年 CSS 生态圈技术趋势!

记得点击文章末尾的“ 阅读原文 ”查看哟~下面先一起看下本期周刊 摘要 吧~奇舞推荐■ ■ ■2022年 CSS 生态圈技术趋势&#xff01;一年一度的 State of CSS 调查结果正式公布&#xff01;通过本文看看2022年 CSS 生态圈的技术趋势&#xff01;React Streaming SSR 原理解析Re…

BI技巧丨RANKX浮点运算

RANKX这个函数&#xff0c;白茶之前已经写过很多期了&#xff0c;本期是对RANKX函数一个细节问题的补充。 我们常见的数据类型有很多&#xff0c;用来聚合的主要有三种数据类型&#xff1a;文本、整数、小数。 在大部分场合&#xff0c;小数是实际FACT数据中最为常见的数据类…

[1180]clickhouse查看数据库和表的容量大小

文章目录1.查看数据库容量、行数、压缩率2.查看数据表容量、行数、压缩率3.查看数据表分区信息4.查看数据表字段的信息5. 查看表的各个指标6.跟踪分区7.检查数据大小在mysql中information_schema这个数据库中保存了mysql服务器所有数据库的信息&#xff0c; 而在clickhouse&…