进程信号(1)
目录
一、信号
1.1、生活中的信号
1.2、Linux中的信号
二、信号处理常见方式
三、信号的产生
3.1、简单理解信号的保存和发送
3.2、键盘产生信号
3.3、核心转储
3.4、系统调用接口产生信号
3.4.1、kill
3.4.2、raise
3.4.3、abort
3.5、软件条件产生信号
3.6、硬件异常产生信号
四、信号的保存
4.1、相关概念
4.2、信号保存——三个数据结构
4.3、信号集——sigset_t
4.3、信号操作函数
一、信号
1.1、生活中的信号
在生活中,我们很容易能够想到常见的一些信号。比如,红绿灯,手机闹钟,上下课铃声,转向灯等等。我们人不仅能够识别它们,还能够知道不同的信号对应的下一步动作应该怎么做。比如,红灯停绿灯行;上课铃响就上课,下课铃响就下课;转向灯告诉别人我要转的方向。
那么,我们是怎么识别并知道这些信号,并且知道信号发出后,接下来的动作应该是怎么样的呢?首先,这当然是规定过的,交通部门规定了红灯停绿灯行,而如果交通部门规定红灯行,绿灯停,那么我们也就只能照做。其次,我们从出生开始,大人们就不断告诉我们,要红灯停,绿灯行,久而久之,我们就记住了特定场景下的信号,以及后续我们需要做到动作,并且终身不忘。
而且,即使我们没有在过马路,而是在吃饭,我们也能够知道应该如何处理红绿灯信号。
再比如,如果,我的9点的闹钟响了,但是我没有立即起床,而是30分钟后再起床。这就说明,当信号产生的时候,我们不一定会立即执行后续动作,但是我记住了闹钟响过了,我该起床了,后面我再执行起床的动作。
上面就是一些生活中的信号,以及我们对待信号的方式。下面我们就来看看Linux中的信号。
1.2、Linux中的信号
什么是Linux信号?
Linux信号本质是一种通知机制,是用户或者操作系统,通过发送一定的信号,来通知进程某件事已经发生,你可以后续对其进行处理。
Linux信号的特点
结合上面生活中的信号的特点,Linux信号有如下特点:
a. 进程能够识别信号,即能够看到信号发送给了自己,并且知道后续的处理动作。
b. 进程能够识别信号,已经由Linux设计者提前设计好了,并且规定了各种信号的后续处理动作。
c. 信号的产生是随机的,信号产生时,进程可能正在做自己的事,所以,进程不一定会立即对信号进行处理。
d. 因为进程不一定立即处理信号,所以进程一定能够将信号记住,后续再进行处理。
e. 进程会在合适的时候处理信号(什么时候合适?后面会讲)。
g. 一般而言,信号的产生相对于进程是异步的。
信号查看:我们可以通过 kill -l 命令查看Linux中有哪些信号
其中,1~31号信号,是普通信号,34~64是实时信号。我们在平时使用中使用的最多的是普通信号。
二、信号处理常见方式
为了方便后面的讲解,我们首先了解一下信号处理的常见方式:
1、执行该信号的默认处理动作(进程自带的,Linux设计者写好的逻辑)。
2、用户自己提供一个信号处理函数,要求在进行信号处理时,使用用户自己定义的方式处理信号,这种方式称为捕捉(Catch)一个信号。
3、忽略该信号。
我们可以通过 man 7 signal 查看信号的默认处理动作:
value:信号编号 action:默认处理动作。
三、信号的产生
3.1、简单理解信号的保存和发送
为了下面我们讲解信号的产生,这里我们先简单地理解一下信号的保存。
前面讲到过,信号产生后,进程不一定会立即处理信号,而是在之后的某个合适的时间对信号进行处理。所以在这中间的一段时间里,我们必须对信号进行保存。
对于保存,进程只需要知道是否有这个信号,就可以对信号进行处理,所以我们可以使用位图来对信号进行保存。0就代表该比特位对应的信号没有产生,1就代表产生了该信号。这样,在之后进程只需要遍历一遍位图,就可以知道产生了哪些信号,然后进行处理。
该位图在进程的PCB中,属于内核数据,只有操作系统能够修改,所以信号的发送就是os把信号对应的比特位的数字由0改成1。
当然,关于信号的保存和发送我们会在下面的内容中,进行详细的讲解,这里只是有一个概念。
3.2、键盘产生信号
在之前讲进程等待时,我们知道使用 Ctrl + c 的组合键能够终止一个进程,而且我们也讲了,其本质就是通过向进程发送2号信号,来终止进程的。下面我们就来证明一下:
我们使用自定义函数,将信号进行捕捉:signal
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);RETURN VALUE
signal() returns the previous value of the signal handler, or SIG_ERR on error. In the event of an error, errno is set to indicate the cause.
#include <iostream>
#include <signal.h>
#include <unistd.h>using namespace std;void catchsig(int signum)
{cout << "进程捕捉到了一个信号:" << signum << " "<< "pid"<< " " << getpid() << endl;
}int main()
{signal(SIGINT, catchsig);while (true){cout << "我是一个进程,我正在运行"<< " "<< "pid"<< " " << getpid() << endl;sleep(1);}return 0;
}
通过对比上面两张图,我们发现Ctrl + c 和发送2号命令,都调用了我们自定义的处理动作。所以 Ctrl + c的本质就是发送2号命令。
3.3、核心转储
上面的一张图,在信号的默认动作action中,term表示只终止进程,而还有的信号的动作是core,这个动作不仅会终止进程,还可以发生核心转储。这个与我们前面的进程等待的内容又有些关联了。
上图是进程等待中,父进程获取子进程信息的status位图结构。低7位保存信号,之前有一个core dump标志,该比特位表示是否发生了核心转储。
核心转储:当进程出现某种异常时,是否由os将当前进程在内存中的相关核心数据,转存到磁盘中。
一般来说,云服务器上的核心转储功能是被关闭了的。而我们可以使用ulimit -a 命令查看core文件,ulimit -c 大小 命令打开云服务器的核心转储功能。
那么核心转储有什么作用呢?我们使用下面的代码来看看:
#include <iostream>
#include <signal.h>
#include <unistd.h>using namespace std;void catchsig(int signum)
{cout << "进程捕捉到了一个信号:" << signum << " "<< "pid"<< " " << getpid() << endl;
}int main()
{signal(SIGQUIT, catchsig);while (true){cout << "我是一个进程,我正在运行"<< " "<< "pid"<< " " << getpid() << endl;sleep(1);int a = 100;a /= 0;}return 0;
}
运行代码后生成了core文件,且以进程pid为后缀。
我们知道程序出错了,而有了core文件后,我们不用去一行一行找出错位置,使用core文件在gdb下可以直接定位出错位置,如下:
3.4、系统调用接口产生信号
3.4.1、kill
NAMEkill - send signal to a processSYNOPSIS#include <sys/types.h>#include <signal.h>int kill(pid_t pid, int sig);Feature Test Macro Requirements for glibc (see feature_test_macros(7)):kill(): _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE
其实,我们常常使用的kill命令的底层所调用的就是该函数,下面我们可以模拟实现一下 kill命令的实现。
#include <iostream>
#include <cassert>
#include <sys/types.h>
#include <signal.h>using namespace std;static void Usage(const string &proc)
{cout << "\nUsage:" << proc << " pid signo\n"<< endl;
}// ./mykill 2 pid
int main(int argc, char *argv[])
{if (argc != 3){Usage(argv[0]);exit(1);}int signo = atoi(argv[1]);int sigpid = atoi(argv[2]);int n = kill(sigpid, signo);assert(n == 0);return 0;
}
3.4.2、raise
作用:进程让os给自己发送某一个信号。
NAMEraise - send a signal to the callerSYNOPSIS#include <signal.h>int raise(int sig);DESCRIPTIONThe raise() function sends a signal to the calling process or thread. In a single-threaded program it is equivalent tokill(getpid(), sig);
#include <iostream>
#include <cassert>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>int main()
{cout << "我开始运行了" << endl;sleep(2);raise(2);return 0;
}
3.4.3、abort
作用:让os给自己发一个6号信号。其实abort的底层也是去调用 raise(6)去实现的。
NAMEabort - cause abnormal process terminationSYNOPSIS#include <stdlib.h>void abort(void);
#include <iostream>
#include <cassert>
#include <unistd.h>
#include<stdlib.h>
#include <sys/types.h>
#include <signal.h>using namespace std;int main()
{cout << "我开始运行了" << endl;sleep(2);abort();return 0;
}
所以,总的来说,系统调用接口产生信号的具体过程就是: 用户调用系统接口——os执行对应的代码——os向目标进程写入信号——修改信号对应的比特位——进程后续对信号进行处理。
3.5、软件条件产生信号
~ 管道
在前面的进程间通信的管道中,我们讨论了一个问题:对于正在通信的两个进程,当管道的读端不读了,而且读端关闭了,但是写端一直在写。这时,写就没有任何意义了。我们验证了,在这个时候,os会通过发送13号信号的方式终止进程。因为管道是一个通过文件在内存级的实现,所以管道是一个软件,所以这种情况就是软件条件不满足而产生信号的一种情况。
~ 设置闹钟 alarm
#include <unistd.h> unsigned int alarm(unsigned int seconds);
调用alarm函数可以设定一个闹钟,也就是告诉内核在seconds秒之后给当前进程发SIGALRM信号,该信号的默认处理动作是终止当前进程。这个函数的返回值是0或者是以前设定的闹钟时间还余下的秒数。闹钟一旦触发了,将会自动移除。
我们可以使用该函数写一个能够测试自己的电脑CPU的计算能力的代码:
#include <iostream>
#include <cassert>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>using namespace std;int count = 0;void sigcath(int sig)
{cout << "final count: "<< " " << count << endl;
}int main()
{alarm(1);signal(SIGALRM, sigcath);while (true)count++;return 0;
}
我们也可以写一个代码来让os帮助我们每隔1秒就可以显示cout最新的计算结果
#include <iostream>
#include <cassert>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>using namespace std;uint64_t count = 0;void sigcath(int sig)
{cout << "final count: "<< " " << count << endl;alarm(1);
}int main()
{alarm(1);signal(SIGALRM, sigcath);while (true)count++;return 0;
}
3.6、硬件异常产生信号
~ 除0错误
我们来看一看下面的代码:
#include <iostream> #include <cassert> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <signal.h>using namespace std;void hander(int sig) {cout << "我捕捉了一个信号:"<< " " << sig << endl;sleep(1); }int main() {signal(SIGFPE, hander);int a = 100;a /= 0;return 0; }
运行结果如下:
我们知道了如果代码中出现了除0错误,os会给进程发送8号信号,那么是怎么产生并发送的呢?
首先,计算以及各种信息的处理是由CPU这个硬件进行的。CPU中有一个寄存器,叫做状态寄存器,它含有一个位图,该位图上有溢出标记位。 CPU在进行计算时,发现代码中出现了除0错误,因此将溢出标记位由0改为1,进程异常,CPU将该进程切出。os会自动进行计算完成后,检测状态寄存器,当检查到溢出标记位为1时,os就会提取当前正在运行的进程的PID,给其发送8号信号。
那么为什么会是死循环打印呢?
上面讲到,溢出标记位由0改为1后,CPU就会将该进程切出,因为寄存器里面的数据是该进程的上下文,所以位图也会跟随进程一起切出。但是,我们虽然将信号进行了捕捉,但是并没有让进程退出,所以这个进程只是被切出去了,当CPU正常进行调度时,再次调度该进程,上下文恢复上去,os立马识别到了溢出标记位还是1,再次打印,如此反复。
所以,为了解决这个问题,我们要在捕捉函数最后加上 exit,让进程退出。
~ 野指针和越界访问
我们知道,指针变量必须通过地址才能找到目标位置。而我们语言上的地址是虚拟地址,所以我们前面讲了通过页表将物理地址和虚拟地址建立联系。但是事实上,我们是通过页表+MMU(memory manger unit,一个硬件)的方式将物理地址和虚拟地址建立联系的,所以当代码中出现了野指针或者越界访问时,因为这是一个非法地址,那么MMU一定会报错,它会将自己内部的寄存器进行标识,os就能够检测到,且知道是哪个进程的地址转化出错了。
四、信号的保存
4.1、相关概念
a. 信号递达:进程对信号的处理动作称为信号递达。
b. 信号未决:信号从产生到递达之间的这个状态称为信号未决。
c. 信号阻塞:被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。
阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。
4.2、信号保存——三个数据结构
前面我们讲到,在进程的PCB中,存在一种位图是用来保存信号的,但是事实上有3种数据结构与信号是相关的。他们分别是pending位图,block位图,typedef void(*handler_t)(int signo),handler_t handler[32]={0}结构。
pending位图:该位图就是我们常说的用来保存信号的位图。
block位图:该位图比特位的位置与信号标号一一对应,比特位的内容代表该信号是否阻塞。
typedef void(*handler_t)(int signo),handler_t handler[32]={0}:这个是一个函数指针数组,这个数组在内核中有指针指向它,这个数组称为当前进程所匹配的信号递达的所有方法,数组下标代表信号的编号,数组的每一个元素都是一个函数指针(存函数地址),指向信号对应的处理方法。
4.3、信号集——sigset_t
上面讲到的三个结构都是属于进程PCB,是内核数据结构。所以os必定不会让用户直接访问这三个结构,更不能够让用户直接进行位移操作。那么如果用户想要得到pending和block位图该怎么办呢?于是,Linux就提供了一种数据类型信号集——sigset_t,用户可以直接使用。
4.3、信号操作函数
既然Linux提供了信号集,那么必定也通过了与之相关的各种方法,让用户能够去操作,这样用户根本就不需要关系在内核中这些结构到底是怎么样的。下面的5个函数就是对信号集进行操作的函数。
#include <signal.h>
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset (sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
int sigismember(const sigset_t *set, int signo);
sigpending:获取当前进程的 pending 信号集。信号发送的本质就是对pending位图进行修改。
NAMEsigpending - examine pending signalsSYNOPSIS#include <signal.h>int sigpending(sigset_t *set);读取当前进程的未决信号集,通过set参数传出。调用成功则返回0,出错则返回-1。
sigprocmask :读取或更改进程的信号屏蔽字(阻塞信号集) (block)
NAMEsigprocmask - examine and change blocked signalsSYNOPSIS#include <signal.h>int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
下表说明了how参数的可选值及其作用:
选项 | 作用 |
SIG_BLOCK | set包含了我们希望添加到当前信号屏蔽字的信号 |
SIG_UNBLOCK | set包含了我们希望从当前信号屏蔽字中解除阻塞的信号 |
SIG_SETMASK | 设置当前信号屏蔽字为set所指向的信号 |
注:9号信号是不能被捕捉或阻塞的。
相关文章:

进程信号(1)
目录 一、信号 1.1、生活中的信号 1.2、Linux中的信号 二、信号处理常见方式 三、信号的产生 3.1、简单理解信号的保存和发送 3.2、键盘产生信号 3.3、核心转储 3.4、系统调用接口产生信号 3.4.1、kill 3.4.2、raise 3.4.3、abort 3.5、软件条件产生信号 3.6、硬…...

Pytorch入门(7)—— 梯度累加(Gradient Accumulation)
1. 梯度累加 在训练大模型时,batch_size 最大值往往受限于显存容量上限,当模型非常大时,这个上限可能小到不可接受。梯度累加(Gradient Accumulation)是一个解决该问题的 trick梯度累加的思想很简单,就是时…...

day12
第一题 本题我们可以使用以下方法: 方法一: 使用hash表<元素,出现次数>来统计字符串中不同元素分别出现的次数,当某一个元素的次数大于1时,返回false,如果每个元素的出现次数都为1,则返回…...
MySQL技术点合集
目录 1. MySQL目录 2. 验证是否首次登陆方法 3. 在Liunx中使用命令来输入sql语句方法 4. 获取修改密码 5. 关闭密码策略 6. 忘记MySQL密码找回 7. 旋转90度横向查看表 8. 添加一个远程连接的用户 1. MySQL目录 /usr/bin/mysql相关命令vim /etc/my.cnfmysql配置文件ls /…...

记录使用 Vue3 过程中的一些技术点
1、自定义组件,并使用 v-model 进行数据双向绑定。 简述: 自定义组件使用 v-model 进行传参时,遵循 Vue 3 的 v-model 机制。在 Vue 3 中,v-model 默认使用了 modelValue 作为 prop 名称,以及 update:modelValue 作为…...
6. C++通过fork的方式实现高性能网络服务器
我们上一节课写的tcp我们发现只有第一个与之连接的人才能收发信息。他又很多的不足 高性能网络服务器 通过fork实现高性能网络服务器 我们通过fork进行改装之后就可以成百上千的用户进行连接访问,对于每一个用户来说我们都fork一个子进程。让后让每一个子进程都是…...

直播美颜插件、美颜SDK详解:技术、功能与实现原理
今天,小编将详细解析直播美颜插件和美颜SDK的技术、功能以及实现原理。 一、美颜技术的背景与发展 1.1美颜技术的兴起 随着直播平台的普及,美颜SDK技术逐渐被集成到直播软件中,以满足用户对更美观、自然的直播效果的需求。 1.2美颜技术的…...

MyBatis入门(1)
目录 一、JDBC操作回顾 二、什么是MyBatis? 三、MyBatis入门 1、准备工作 (1)创建工程 (2)数据准备 2、配置数据库连接字符串 3、写持久层代码 4、单元测试 (1)使用IDEA自动成成测试类…...

打开服务器远程桌面连接不上,可能的原因及相应的解决策略
在解决远程桌面连接不上服务器的问题时,我们首先需要从专业的角度对可能的原因进行深入分析,并据此提出针对性的解决方案。以下是一些可能的原因及相应的解决策略: 一、网络连接问题 远程桌面连接需要稳定的网络支持,如果网络连接…...

用于时间序列概率预测的蒙特卡洛模拟
大家好,蒙特卡洛模拟是一种广泛应用于各个领域的计算技术,它通过从概率分布中随机抽取大量样本,并对结果进行统计分析,从而模拟复杂系统的行为。这种技术具有很强的适用性,在金融建模、工程设计、物理模拟、运筹优化以…...

VScode解决报错“Remote-SSH XHR failed无法访问远程服务器“的方案
VScode解决报错"Remote-SSH XHR failed无法访问远程服务器"的方案 $ ls ~/.vscode-server/bin 2ccd690cbff1569e4a83d7c43d45101f817401dc稳定版下载链接:https://update.code.visualstudio.com/commit:COMMIT_ID/server-linux-x64/stable 内测版下载链接…...

Python高级进阶--dict字典
dict字典⭐⭐ 1. 字典简介 dictionary(字典) 是 除列表以外 Python 之中 最灵活 的数据类型,类型为dict 字典同样可以用来存储多个数据字典使用键值对存储数据 2. 字典的定义 字典用{}定义键值对之间使用,分隔键和值之间使用:分隔 d {中…...

记忆力和人才测评,如何提升记忆力?
什么是记忆力? 如何通俗意义上的记忆力?我们可以把人的经历、经验理解成为一部纪录片,那么已经过去发生的事情,就是影片之前的情节,对于这些信息,在脑海里,人们会将其进行处理和组合ÿ…...
数据仓库建模
目录 数仓建模 为什么要对数据仓库进行分层 主题 主题的概念 维度建模: 模型的选择: 星形模式 雪花模型 星座模式 拉链表 维度表和事实表: 维度表 事实表 事实表设计规则 退化维度 事务事实表、周期快照事实表、累积快照事实…...

力扣:1738. 找出第 K 大的异或坐标值
1738. 找出第 K 大的异或坐标值 给你一个二维矩阵 matrix 和一个整数 k ,矩阵大小为 m x n 由非负整数组成。 矩阵中坐标 (a, b) 的 值 可由对所有满足 0 < i < a < m 且 0 < j < b < n 的元素 matrix[i][j](下标从 0 开始计数&…...

Keras深度学习框架第二十讲:使用KerasCV中的Stable Diffusion进行高性能图像生成
1、绪论 1.1 概念 为便于后文讨论,首先进行相关概念的陈述。 Stable Diffusion:Stable Diffusion 是一个在图像生成领域广泛使用的技术,尤其是用于文本到图像的转换。它基于扩散模型(Diffusion Models),这…...

C/C++ vector详解
要想了解STL,就必须会看: cplusplus.comhttps://legacy.cplusplus.com/ 官方内容全都是英文的,可以参考: C/C初始识https://blog.csdn.net/2301_77087344/article/details/138596294?spm1001.2014.3001.5501 vectorÿ…...

使用libtorch加载YOLOv8生成的torchscript文件进行目标检测
在网上下载了60多幅包含西瓜和冬瓜的图像组成melon数据集,使用 LabelMe 工具进行标注,然后使用 labelme2yolov8 脚本将json文件转换成YOLOv8支持的.txt文件,并自动生成YOLOv8支持的目录结构,包括melon.yaml文件,其内容…...

Oracle 并行和 session 数量的
这也就是为什么我们指定parallel为4,而实际并行度为8的原因。 insert create index,发现并行数都是加倍的 Indexes seem always created with parallel degree 1 during import as seen from a sqlfile. The sql file shows content like: CREATE INDE…...

Android 版本与 API level 以及 NDK 版本对应
采用 Android studio 开发 Android app 的时候,需要选择支持的最低 API Level 和使用的 NDK 版本,对应开发 app 的最低 SDK 版本: 在 app 的 build.gradle 文件里,对应于代码如下: 目前各版本的占有率情况如下…...
云计算——弹性云计算器(ECS)
弹性云服务器:ECS 概述 云计算重构了ICT系统,云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台,包含如下主要概念。 ECS(Elastic Cloud Server):即弹性云服务器,是云计算…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...

Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...
是否存在路径(FIFOBB算法)
题目描述 一个具有 n 个顶点e条边的无向图,该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序,确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数,分别表示n 和 e 的值(1…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)
漏洞概览 漏洞名称:Apache Flink REST API 任意文件读取漏洞CVE编号:CVE-2020-17519CVSS评分:7.5影响版本:Apache Flink 1.11.0、1.11.1、1.11.2修复版本:≥ 1.11.3 或 ≥ 1.12.0漏洞类型:路径遍历&#x…...
PostgreSQL——环境搭建
一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在࿰…...

Python 实现 Web 静态服务器(HTTP 协议)
目录 一、在本地启动 HTTP 服务器1. Windows 下安装 node.js1)下载安装包2)配置环境变量3)安装镜像4)node.js 的常用命令 2. 安装 http-server 服务3. 使用 http-server 开启服务1)使用 http-server2)详解 …...

nnUNet V2修改网络——暴力替换网络为UNet++
更换前,要用nnUNet V2跑通所用数据集,证明nnUNet V2、数据集、运行环境等没有问题 阅读nnU-Net V2 的 U-Net结构,初步了解要修改的网络,知己知彼,修改起来才能游刃有余。 U-Net存在两个局限,一是网络的最佳深度因应用场景而异,这取决于任务的难度和可用于训练的标注数…...

sshd代码修改banner
sshd服务连接之后会收到字符串: SSH-2.0-OpenSSH_9.5 容易被hacker识别此服务为sshd服务。 是否可以通过修改此banner达到让人无法识别此服务的目的呢? 不能。因为这是写的SSH的协议中的。 也就是协议规定了banner必须这么写。 SSH- 开头,…...

门静脉高压——表现
一、门静脉高压表现 00:01 1. 门静脉构成 00:13 组成结构:由肠系膜上静脉和脾静脉汇合构成,是肝脏血液供应的主要来源。淤血后果:门静脉淤血会同时导致脾静脉和肠系膜上静脉淤血,引发后续系列症状。 2. 脾大和脾功能亢进 00:46 …...