linux信号 | 学习信号三步走 | 全解析信号的产生方式

        前言:本节内容是信号, 主要讲解的是信号的产生。信号的产生是我们学习信号的第二个阶段。 我们已经学习过第一个阶段——信号的概念与预备知识(没有学过的友友可以查看我的前一篇文章)。 以及我们还没有学习信号的第三个阶段——信号的保存与处理。 

        ps:本节内容主要为信号的产生, 需要友友们了解信号的概念以及信号的预备知识后再进行学习。   

        信号的产生有很多种, 但是无论信号如何产生, 最终一定是由 OS 发出的。 ——为什么? 因为OS是进程的管理者。 就比如弟弟惹到了姐姐, 然后姐姐向爸爸告状, 爸爸收到姐姐的告状后就会去找弟弟, 然后教育一顿。 这里面爸爸就是执行者OS, 然后弟弟就是被管理者, 姐姐就是发送的那个信号。  

目录

键盘组合键

kill命令

系统调用

kill 系统调用

接口

应用

raise——发送一个信号给调用者

接口

应用

abort

接口

应用

异常

异常:为什么除零, 或者野指针会给进程发送信号呢? 

除零

 野指针

软件条件

alarm

dump


键盘组合键

        产生信号的第一种方法是键盘组合键, 信号组合健种最常见ctrl + C, 我们在运行一个程序,如果程序陷入死循环, 我们这个时候无论输入任何指令都是没有用的。 所以就要使用ctrl + C将程序退出这里我们点击ctrl + C, 本质其实就是给进程发送了一个信号。 这个信号是2号信号。

        同样的, 键盘组合键还有另外一种比较常见的组合键就是ctrl + \。 同样也是给进程发送信号, 不过信号序号是3号信号。 当进程识别到这些信号, 就会终止, 退出自己。 

        这里有一个函数, 用来捕捉我们的信号, 叫做signal

        这里面有两个参数, 第一个参数是signum,意思是要捕捉的信号编号。 第二个参数是handler, 意思是捕捉到信号后,重新定义的信号的处理方式。 

        如下是一个2号信号的捕捉操作:

kill命令

        kill 命令可以用来终止正在运行的进程。kill命令通过发送不同的信号给目标进程, 如我们经常使用的kill -9。 kill命令的使用方法是kill 命令序号 PID。

        这里博主利用信号的捕捉来验证我们的kill命令。(注意, 接下来实验过程中我们可以发现signal并不能捕捉9号和19号命令), 下面是代码:

#include<iostream>
using namespace std;
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<signal.h>void myhander(int x)
{cout << "signal is " << x << endl;
}int main()
{for (int i = 1; i <= 31; i++){signal(i, myhander);}while (true){cout << "hello signal , PID : " << getpid() << endl;sleep(1);}return 0;
}

接下来我们就完成编译。 并且打开两个终端, 一个终端用来发送信号即可:

        博主上面对于1 ~ 31个信号没有全部进行测试, 但是友友们可以全部测试一下。 然后就能发现, 除了9号和19号, 其他的信号都可以被捕捉。 并且也验证了我们的kill命令可以给进程发送信号。——那么, 为什么9号和19号信号要暴露出来, 不能被捕捉。 是因为这两个信号, 一个是杀掉进程, 一个是暂停进程如果我们今天想写一个恶意病毒, 恶意软件。那么我们把9号、19号信号一捕捉, 操作系统就杀不掉这个病毒了, 那怎么办? 所以, 必须要将9号和19号暴露出来。 

系统调用

kill 系统调用

接口

kill系统调用是发送一个信号给进程。这里的第一个参数进程pid, 意思是发给哪一个进程。第二个参数是sig, 表示发送哪一个信号。

kill调用的返回值是如果成功, 零被返回。 如果失败, -1被返回。

应用

这里我们定义一个proc程序, 用来循环打印一条语句:

#include<iostream>
using namespace std;
#include<unistd.h>
#include<sys/types.h>
#include<signal.h>void myhander(int signum)
{cout << "signal is : " << signum << endl;
}int main()
{for (int i = 1; i <= 31; i++){signal(i, myhander);}while (true){cout << "i am a process,  pid : " << getpid() << endl;sleep(1);}return 0;
}

        这里之所以要signal捕捉信号是因为要检测当前进程是否收到了信号, 正确的信号。  并且这个语句执行后, 会循环打印进程自己当前的pid。 

        然后, 我们创建另一个程序, mykill, 用来封装kill系统调用。

#include<iostream>
using namespace std;
#include<sys/types.h>
#include<signal.h>
#include<unistd.h>
#include<cstring>int main(int argc, char* argv[])
{if (argc == 3){kill(stoi(argv[0]), stoi(argv[1]));}else{cout << "please set mykill.exe + pid + sig" << endl;exit(1);}return 0;
}

运行结果: 

raise——发送一个信号给调用者

接口

        

这里面的参数只有一个sig, 意思是要给当前进程发送哪一个信号。 返回值int, 当成功时返回零, 失败时返回非零。 

应用

#include<iostream>
using namespace std;
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<signal.h>void myhander(int x)
{cout << "signal is " << x << endl;
}int main()
{signal(2, myhander);int cnt = 5;while (true){cout << "hello signal , PID : " << getpid() << endl;sleep(1);cnt--;if (cnt == 0) raise(2);}return 0;
}

运行结果(然后我们运行起来可以发现, 5秒之后, 确实给我们发送了一个2号信号。

abort

接口

这一个调用是引起一个正常的进程进行终止。 

应用

运行结果:

我们运行这个程序, 就会发现, 这个进程5秒之后, abort了。 这个abort其实就是6号信号, 所以我们这里可以直接捕捉他。

但是问题是, 我们捕捉了, 但是也退出了!!注意, 这个退出不是信号的问题, 这个是abort的问题。 abort不仅仅捕捉了我们的6号信号, 而且还做了一个工作——必须让我们这个进程终止。 这个abort在底层其实也相当于是有一个kill(getpid(), 6)。 但是还多做了一些工作, 比如exit之类的。

异常

        异常博主认为是最重要的信号的产生方式。 因为我们在运行程序的时候, 遇到的信号, 基本都是由异常引起的。 我们的野指针发送信号, 我们的除零错误要发送信号。 

        我们平时运行程序,退出的方式无非就是三种——代码跑完, 结果正确; 代码跑完,结果不正确; 代码没跑完, 出现异常。 

        这里我们先写一段代码, 看一下异常:

上面的运行结果就能看出发生了异常, 直接报错。 但是问题是, 上面的报错是31个信号的哪一种呢? ——这里其实是八号信号, 如下图:

我们可以使用自定义捕捉这个异常:

        然后运行, 就能看到下图的情况:

        这里我们会发现, 虽然我们知道了这个错误是八号信号。 但是问题是他怎么一直在死循环打印呢? 我们知道, 我们的捕捉函数里面, 没有退出进程, 只有一条语句, 那么也就是说, 我们的程序发生除零错误不会退出, 进程一直再跑, 所以OS一直发送信号, 进程也就一直都在打印信号。 

        我们换成下面的代码:        

void myhander(int signum)
{cout << "get a signum : " << signum << endl;
}int mian()
{signal(SIGFPE, myhander);cout << "pointer error before " << endl;sleep(1);int* p = nullptr;*p = 100;sleep(1);cout << "pointer error after " << endl;return 0;
}

这个进程我们会发现, 两秒之后也退出了!并且发生了段错误。 段错误在信号中是11信号,SIGSEGV, 如何验证和上面的八号信号的验证方式是一样的。这里直接贴运行结果。 同样会死循环打印语句, 不发生退出。 ——因为11号信号被捕捉。 

         上面捕捉信号也是在处理异常信号。 那么问题来了, 处理异常信号, 一定会退出吗? 答案是不一定, 一般情况下, 如果是默认动作, 异常会推出。 但是如果是自定义动作, 也就是信号被捕捉, 这个时候异常不一定会推出。 但是一旦异常推出, 一定是执行了某个信号的处理方法。 

        但是我们虽然可以捕捉信号, 不让它退出,只不过这个意义不大, 因为我们的进程已经发生了错误了。那么我们大概率还是要让这个异常终止。 

        

异常:为什么除零, 或者野指针会给进程发送信号呢? 

除零

        我们上面说过, 进程接收到信号, 一定是OS发出的, 一旦我们的计算机里面出现了除零错误或者野指针问题, 操作系统又识别到了这些问题, 那么操作系统就会给我们的进程发送信号收到信号后我们的进程对于信号的默认处理动作就是终止自己, 所以它就直接崩溃了。 但是这里的问题不是操作系统会检测到除零或者野指针,而是如何检测到除零或者野指针

        首先, 我们知道cpu中有着许许多多的寄存器。 其中, EIP、PC指针指向我们的进程的上下文。 

        其中, 状态寄存器有标记位, 是把寄存器按照比特位级别设计的。 状态寄存器里面的位数各自代表什么含义是由芯片制造商定好的。

        状态寄存器里面有一个标记位, 叫做一处标记位。 当我们除零的时候, 结果就会非常大那么就溢出了。 这个标记为就从0变成1了。

        而且,要知道, 我们cpu中的数据, 其实都是进程的上下文。 虽然我们修改的是cpu中的状态寄存器。 但是进程只影响他自己。 ——什么意思? 就是说,我们的进程是不断切换的, 所以寄存器里面的数据也是不断切换的。 当我们的进程切换的时候, 会把我们自己的上下文带走, 下一个进程将自己的上下文放上去。 所以进程之间是不会有影响的。 所以, 不要认为cpu发生了异常, 就说明操作系统出问题了。 因为我们的用户的各种行为, 都是被进程包裹的, 硬件异常是代表这个进程出现了异常, 并不会波及我们的操作系统。 也就是说, 引起出错的永远是进程, cpu出错, 我们的系统照跑不误。 

        那么问题来了, 我们的cpu这个时候发生溢出了, 我们的操作系统要不要知道呢? 为什么呢? ——必须要知道!操作系统必须得知道!因为操作系统是硬件的管理者!!!cpu也是硬件!!操作系统在运行我们的进程的时候, 会有类似检查或者中断的方法, 得知我们的cpu出现溢出了, 然后操作系统向进程发送信号, 进程收到信号后, 就崩溃了。 ——简而言之就是我们这个除零问题被转化为了硬件问题, 表现在硬件上面, 进而被操作系统识别到。 被操作系统识别, 操作系统就能对信息做处理。 操作系统的处理并不影响整个系统的稳定性, 影响的是当前进程, 因为cpu内的问题, 是属于进程的问题

 野指针

     上面的进程我们的进程在通过页表进行查表的时候, 这个查表不是操作系统直接来查的, 因为查表是很费时间的。 ——这个查表是由一个MMU, 内存的管理单元来完成的。

        在上面的图中, 我们的cpu读到的都是虚拟地址。 从虚拟地址到物理地址, 需要经过MMU的转化, 而野指针就是这个转化失败了——要么没有映射关系, 要么越界了, 越权了。 转化失败, MMU报错, 代表里面的硬件发生了报错, 转化失败后的地址会放到另一个寄存器里面, 这个报错, 也能被cpu识别到。 

        那么, 操作系统是怎么知道, 我们的cpu是溢出了, 还是野指针了呢? ——因为对应的是cpu内不同类型的寄存器的报错!!! 

        那么既然报错后, 操作系统会发送信号让进程崩溃, 但是如果我们将信号捕捉, 不让进程崩溃呢? ——那么就意味着我们的进程在异常的时候也要一直被调度运行!!!所以为什么我们上面会看到死循环呢? 就是因为我们的进程发生了问题, 但是我们没有修正这个问题, 硬件问题一直存在, 随着我们的调度, 那么我们的上下文的错误就一直存在。 所以操作系统就一直检测到这个错误, 它就会一直发信号, 我们就能一直捕捉这个信号。 !!!

        对于异常的进程来讲, 即便向后运行, 结果可能也是错误的, 所以进程发生异常, 就应该是终止掉, 所以大部分信号的默认动作都是终止掉进程。 ——但是为什么会有捕捉信号呢? 因为捕捉信号, 并不是用来解决问题, 而是用来告诉用户, 你是为什么挂掉的!!

        那么, 为什么操作系统这么温和? 当硬件报错的时候, 不直接将进程的pcb, 页表, 地址空间全部干掉呢? ——这是因为进程里面有可能保存着许多的重要的数据, 如果直接将这些东西干掉,这些数据会丢失, 所以要设置信号, 告诉上层哪里出错了, 为什么出错了, 然后让上层自己想办法解决。

软件条件

alarm

        上面我们讲述的异常也可以成为软件异常。现在有另外一种, 叫做软件条件。 ——这个软件条件叫做闹钟。 

        alarm是在我们的系统里面设置一个闹钟, 一旦闹钟响了, 那么就会给我们的进程传送一个信号。 这个参数就是多少秒之后会给我们发送这个信号。下面是这个接口的返回值:

        这个返回值是提前醒来, 距离闹钟响之间剩余的时间。  

下面我们写这么一串代码:

然后运行它

        我们会发现, 虽然闹钟到达事件后会发送信号, 但是仅仅只是发送一次!这是因为这个闹钟不是异常, 它只响一次。 那么问题来了, 当我们重复设置闹钟的时候, 加入设定了一个5秒的闹钟, 但是过了三秒又设置了一个闹钟, 这个时候会发生什么情况呢? ——答案是第一个闹钟剩余的时间会返回。 

        为了验证, 我们可以使用下面这串代码进行测试。

按照上面的结论, 我们猜测最终打印出来的是6:

        我们的闹钟是如何确定时间的呢? ——使用时间戳和参数, 我们的进程, 我们知道进程可以直接获取时间, 我们的闹钟里面一定有一个时间戳的成员变量的。 未来我们进程获取时间戳变量, 填到闹钟里, 闹钟再根据参数, 两者一加, 就是未来时间了!!!

        那么知道了未来时间, 我们操作系统里面又维护着当前时间, 当这个时间 >= 未来时间的时候, 就能知道闹钟到没到时间了。 

        那么我们未来系统中一定存在着大量的闹钟, 我们如果想要确定哪个闹钟响过了, 就把他去掉。 可以使用什么数据结构呢? ——堆

dump

        我们在进程控制的时候, 曾经说过wait的返回值status, 这个返回值当中的前八个比特位是代表的退出码第0 ~ 7是代表的终止信号第8个比特位我们说过叫做core dump标志。 

        这个core dump是什么当时我们并没有说过, 这里可以说了。 我们看下面的图:里面有term, 有core。 还有其他的动作, 但是其他的动作我们不考虑, 我们只看core和term。 这个dump比特位, 就是代表的是core或者term。为零是term, 为1是core。 

        现在我们来重新写一下代码, 看一下我们的dump比特位:

现在我们来看一下:

对于2号信号:

对于8号信号:

        两次都是dump0, 但是我们的8是core2是term。 这是为什么呢? 是什么呢 ? 怎么办? ——这是因为云服务器上面的core默认是关闭的

        我们可以看一下ulimit -a, 这个是查看系统当中的标准的配置。 

这里面有一个core file size的选项, -c就是用来查看。 当前它是设为0的。 我们可以使用 -c选项后边加一个数字, 可以用来设置core。

        这一次, 我们再来使用8号信号。 就能看到结果变了。  

        更重要的是, 这里生成了一个core.4017, 也就是刚刚进程的PID——打开系统的core dump功能, 一旦进程出现异常, 我们的OS会将进程在内存中的运行信息, 给我们dump(转储)到进程的当前目录(磁盘)形成core.pid文件——这个就叫做核心转储(core dump)

        上面的core.pid有什么用? ——我们的程序在运行时发生错误, 我们最想要知道的是什么呢? ——是不是定位错误的位置, 也就是在哪一行出错了。 我们可以使用core dump数据, 来定位原始代码当中, 在运行过程中哪方面出错了。

        我们使用gdb调试左边的代码, 然后进入core.pid文件里面, 就能看到错误信息了。 

        core文件能够让我们复现问题之后, 直接定位到出错行。 就是先运行, 再core-file。 ——这个叫做事后调试。 而我们边跑边调叫做事中调试。

        现在的问题是, 为什么云服务器是要把它进行关闭的。 我们知道core文件比较大。 在当代服务器上面, 如果一个服务器挂掉了, 是要让运维重新起来的。 可是大公司的后端服务器集群非常多。 如果运维手动去起, 那么太慢了。 所以大公司要做很多很多自动化运维的手段, 比如说我们的服务器挂掉了, 那么我们要自动的检测服务器出问题了, 第二点就是先将服务器启动起来, 最后才是根据日志排查问题。 

        所以, 一旦服务器挂掉了, 在很多系统当中会自动重启。 可是, 问题是一般人一年才挂一次, 半年挂一次, 结果呢你写的服务只要跑起来就挂。 虽然在大部分公司, 这样的程序员根本不会有, 但是可能会出现这样的问题。 要知道, 我们的计算机的速度是很快的, 我们的计算器一晚上的重启, 如果每一次重启都会生成一个core-pid文件那么就会导致本来是云服务器上面的一个服务挂掉了, 结果后来变成了磁盘空间被core-pid文件打满了。 那么就可能连操作系统都挂掉了。 所以, core dump的功能在线上一般都要禁掉, 保证我们的系统重启功能一直都要有效。 不要让core dump冲击磁盘, 影响服务。 

——————以上就是本节全部内容哦, 如果对友友们有帮助的话可以关注博主, 方便学习更多知识哦!!!

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

89个H5小游戏源码

下载地址&#xff1a;https://download.csdn.net/download/w2sft/89791650 亲测可用&#xff0c;代码完整&#xff0c;都是htmljs&#xff0c;保存到本地即可。 游戏截图&#xff1a;

Universal Link配置不再困扰,Xinstall来帮忙

在移动互联网时代&#xff0c;App的推广和运营至关重要。而Universal Link作为一种能够实现网页与App间无缝跳转的技术&#xff0c;对于提升用户体验、引流至App具有显著效果。今天&#xff0c;我们就来科普一下Universal Link的配置方法&#xff0c;并介绍如何通过Xinstall这款…

TypeScript 设计模式之【备忘录模式】

文章目录 备忘录模式&#xff1a;时光机器的魔法备忘录模式的奥秘备忘录模式有什么利与弊?如何使用备忘录模式来优化你的系统代码实现案例备忘录模式的主要优点备忘录模式的主要缺点备忘录模式的适用场景总结 备忘录模式&#xff1a;时光机器的魔法 想象一下&#xff0c;如果…

25 基于51单片机的温度电流电压检测系统(压力、电压、温度、电流、LCD1602)

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 基于51单片机&#xff0c;通过DS18B20检测温度&#xff0c;滑动变阻器连接数模转换器模拟电流、电压&#xff0c;通过LCD1602显示&#xff0c;程序里设置温度阈值为40&#xff0c;电流阈值为60&am…

万博智云CEO王嘉在华为全联接大会:以创新云应用场景,把握增长机遇

一、大会背景 2024年9月19-21日&#xff0c;第九届华为全联接大会将在上海世博展览馆和上海世博中心举办。作为华为的旗舰盛会&#xff0c;本次大会以“共赢行业智能化”为主题邀请了众多思想领袖、商业精英、技术专家、合作伙伴、开发者等业界同仁&#xff0c;从战略、产业、…

Nginx基础详解3(nginx.conf核心代码讲解、常用命令解析、Nginx日志切割)

续Nginx基础详解2&#xff08;首页解析过程、进程模型、处理Web请求机制、nginx.conf语法结构&#xff09;-CSDN博客 目录 8.nginx.conf核心代码 8.1错误日志 8.1.1第一列&#xff1a; 8.1.2第二列&#xff1a; 8.1.3第三列&#xff1a; 8.2 #pid 8.3http模块&#xff…

A开头的词根词缀:-ate+a-+ab\abs+ab\c\d\f\g\n\p\r\s\t+ad+amph+an+ana+ante+anti+anthrop+

ate -ate,它是英语单词中的后缀词缀。它加在词根或词干上分三种词性。 首先第一种词性adj.(形容词)&#xff0c;它主要加缀在名词词根或词干上构成的形容词&#xff1a;……的&#xff0c;有……的&#xff0c;像……的&#xff0c;For example:accurate(adj.正确的&#xff…

如何实现全行业证照一站式结构化识别?Textln企业资质证照识别上线!

企业经营活动中&#xff0c;资质证书是证明企业具备某项行业准入的必要证件。但企业资质证书种类繁多&#xff0c;各行各业的资质证书都有差异&#xff0c;同一行业、不同地区出具的资质证书版式也各不相同&#xff0c;通过传统标注训练的方式难以全量覆盖各类企业资质证照的识…

【JAVA开源】基于Vue和SpringBoot的墙绘产品展示交易平台

本文项目编号 T 049 &#xff0c;文末自助获取源码 \color{red}{T049&#xff0c;文末自助获取源码} T049&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 查…

机器人顶刊IEEE T-RO发布无人机动态环境高效表征成果:基于粒子的动态环境连续占有地图

摘要&#xff1a;本研究有效提高了动态环境中障碍物建模的精度和效率。NOKOV度量动作捕捉系统助力评估动态占用地图在速度估计方面的性能。 近日&#xff0c;上海交通大学、荷兰代尔夫特理工研究团队在机器人顶刊IEEE T-RO上发表题为Continuous Occupancy Mapping in Dynamic …

【C语言】手把手带你拿捏指针(完)(指针笔试、面试题解析)

文章目录 一、sizeof和strlen的对⽐1.sizeof2.strlen3.sizeof与strlen对比 二、数组和指针笔试解析1.一维数组2.字符、字符串数组和字符指针代码1代码2代码3代码4代码5代码6 3.二维数组4.总结 三、指针运算笔试题解析代码1代码2代码3代码4代码5代码6 一、sizeof和strlen的对⽐ …

线性跟踪微分器TD详细测试(Simulink 算法框图+CODESYS ST+博途SCL完整源代码)

1、ADRC线性跟踪微分器 ADRC线性跟踪微分器(ST+SCL语言)_adrc算法在博途编程中scl语言-CSDN博客文章浏览阅读784次。本文介绍了ADRC线性跟踪微分器的算法和源代码,包括在SMART PLC和H5U平台上的实现。文章提供了ST和SCL语言的详细代码,并讨论了跟踪微分器在自动控制中的作用…

排序--希尔排序

希尔排序介绍 希尔排序核心思想就是:1,分组;2,直接插入排序:越有序越快 希尔排序就是多次利用直接插入排序的一个排序算法. 希尔排序的算法思想:间隔式分组,利用直接插入排序让组内有序,然后缩小分组再次排序,直到组数为1希尔排序的理论基础就是直接插入排序越有序越快; 希尔排…

Redis-----通用命令(keys, exists, del, expire, ttl, type)

通用命令 一. 前言.1.1 通用命令1.2 Redis常用的数据类型1.2.1 String&#xff08;字符串&#xff09;1.2.2 List&#xff08;列表&#xff09;1.2.3 Set&#xff08;集合&#xff09;1.2.4 Hash&#xff08;哈希&#xff09;1.2.5 Zset&#xff08;有序集合&#xff09; 二. 通…

通过 OpenAI API 实测 o1 模型(附源码)

9.11 与 9.9 哪个大? 还记得之前给大家演示的幻觉问题么&#xff1f; 用 gpt4 系列模型提问“9.11 与 9.9 哪个大?” 大家可以回顾一下&#xff0c;即使引导了 COT 的思路&#xff0c;但是 gpt4 还是一本正经的胡说八道。 如今&#xff0c;o1 已经完美解决数学、逻辑推理方…

探索 Snowflake 与 Databend 的云原生数仓技术与应用实践 | Data Infra NO.21 回顾

上周六&#xff0c;第二十一期「Data Infra 研究社」在线上与大家相见。活动邀请到了西门子数据分析师陈砚林与 Databend 联合创始人王吟&#xff0c;为我们带来了一场关于 Snowflake 和 Databend 的技术探索。Snowflake&#xff0c;这个市值曾超过 700 亿美元的云原生数据仓库…

《概率论与数理统计》学渣笔记

文章目录 1 随机事件和概率1.1 古典概型求概率随机分配问题简单随机抽样问题 1.2 几何概型求概率1.3 重要公式求概率 2 一维随机变量及其分布2.1 随机变量及其分布函数的定义离散型随机变量及其概率分布&#xff08;概率分布&#xff09;连续型随机变量及其概率分布&#xff08…

数据结构和算法之树形结构(4)

文章出处&#xff1a;数据结构和算法之树形结构(4) 关注码农爱刷题&#xff0c;看更多技术文章&#xff01;&#xff01;&#xff01; 六、红黑树(接前篇) 红黑树是为了弥补AVL树在大规模频繁增删节点场景下性能不理想而设计出来的一种平衡二叉查找树。红黑树不是一种严…

【论文阅读】Diffusion Policy: Visuomotor Policy Learning via Action Diffusion

Abstract 本文介绍了扩散策略&#xff0c;这是一种通过将机器人的视觉运动policy表示为条件去噪扩散过程来生成机器人行为的新方法。我们对来自 4 个不同的机器人操作基准的 15 个不同任务的扩散策略进行了基准测试&#xff0c;发现它始终优于现有的 state-of-the-art 机器人学…

word批量裁剪图片,并调整图片大小,不锁定纵横比

在word中有若干图片待处理&#xff0c;裁剪出指定内容&#xff0c;调整成指定大小。如下是待处理的图片&#xff1a; 这时&#xff0c;选择视图&#xff0c;选择宏&#xff0c;查看宏 选择创建宏 添加cut_picture代码如下&#xff0c;其中上、下、左、右裁剪的橡塑尺寸根据自己…