10.Linux进程信号
1. 理解信号
信号VS信号量 = 老婆:老婆饼-》没有任何关系!信号:闹钟,上课铃声,脸色...人-》进程;信号中断人正在做的事,是一种事件的异步通知机制;
我们自习一会,等张三回来再讲 -- 同步
我们继续上课,而张三去取快递 -- 异步
信号->是一种给进程发送的,用来进行事件异步通知的机制!信号的产生,相对于进程的运行,是异步的!信号是发给进程的
基本结论:1.你怎么能识别信号呢?识别信号是内置的,进程识别信号,是内核程序员写的内置特性。
2.进程早已经内置了对于信号的识别和处理方式
3.处理信号,⽴即处理吗?我可能正在做优先级更⾼的事情,不会⽴即处理?什么时候?合
适的时候。
4.信号到来 | 信号保存 | 信号处理
5.怎么进⾏信号处理啊?a.默认 b.忽略 c.⾃定义, 后续都叫做信号捕捉。
1.1 一个例子
⽤⼾输⼊命令,在Shell下启动⼀个前台进程
• ⽤⼾按下 Ctrl+C ,这个键盘输⼊产⽣⼀个硬件中断,被OS获取,解释成信号,发送给⽬标前台进
程
• 前台进程因为收到信号,进⽽引起进程退出
注意:1. 要注意的是,signal函数仅仅是设置了特定信号的捕捉⾏为处理⽅式,并不是直接调⽤处
理动作。如果后续特定信号没有产⽣,设置的捕捉函数永远也不会被调⽤!!2. Ctrl-C 产⽣的信号只能发给前台进程。⼀个命令后⾯加个&可以放到后台运⾏,这样
Shell不必等待进程结束就可以接受新的命令,启动新的进程。03. Shell可以同时运⾏⼀个前台进程和任意多个后台进程,只有前台进程才能接到像 Ctrl-C
这种控制键产⽣的信号。4. 前台进程在运⾏过程中⽤⼾随时可能按下 Ctrl-C ⽽产⽣⼀个信号,也就是说该进程的⽤
⼾空间代码执⾏到任何地⽅都有可能收到 SIGINT 信号⽽终⽌,所以信号相对于进程的控
制流程来说是异步(Asynchronous)的。
1.2 信号处理
1. 自定义 -》 signal(SIGINT/*2*/, 函数指针);2. 忽略此信号 -》 signal(SIGINT/*2*/, SIG_IGN); // 设置忽略信号的宏3. 执⾏该信号的默认处理动作 -》 signal(SIGINT/*2*/, SIG_DFL);// 默认信号处理源码:#define SIG_DFL ((__sighandler_t) 0) /* Default action. */
#define SIG_IGN ((__sighandler_t) 1) /* Ignore signal. *//* Type of a signal handler. */
typedef void (*__sighandler_t) (int);其实SIG_DFL和SIG_IGN就是把0,1强转为函数指针类型
2. 产生信号
2.1 键盘产生信号
• Ctrl+C (SIGINT) 已经验证过,这⾥不再重复
• Ctrl+\(SIGQUIT)可以发送终⽌信号并⽣成core dump⽂件,⽤于事后调试
• Ctrl+Z(SIGTSTP)可以发送停⽌信号,将当前前台进程挂起到后台等
相当一部分信号的处理动作,就是让自己终止。
进程收到信号后,合适的时候,处理信号动作有三种:1.默认处理动作 2.自定义信号处理动作(自定义捕捉)3.忽略处理
编号34以上的是实时信号;这些信号各⾃在什么条件下产⽣,默认的处理动作是什么,在signal(7)中都有详细说明: man 7 signal。
2.1.1 OS如何得知键盘有数据
1.键盘按下
2.向CPU发送硬件中断
3.CPU识别自身针脚具有硬件中断信息,其实就是高电压
4.CPU执行OS中处理键盘数据的代码
5.OS停下来当前工作,将数据从外设读入内存,等待进一步处理
2.1.2 初步理解信号起源
• 信号其实是从纯软件⻆度,模拟硬件中断的⾏为
• 只不过硬件中断是发给CPU,⽽信号是发给进程
• 两者有相似性,但是层级不同
2.1.3 理解前后台进程
目标进程:前台进程和后台进程
# ./XXX --- 前台进程 --键盘产生的信号,只能发给前台进程
# ./XXX & --- 后台进程
命令行shell进程 --- 前台后台进程,无法从标准输入(键盘)中获取内容;而前台可以
但是都可以向标准输出上打印前台进程必须只有一个,后台进程可以有多个XXX
fork()
父 -- 先退出了
子 -- 孤儿进程 --》自动提到后台 -》ctrl + c 杀不掉
补充命令:前后台移动
jobs查看所有的后台任务
fg 任务号,特定的进程,提到前台ctrl + z:进程切换到后台
bg 任务号:让后台进行回复运行
2.1.4 什么叫做给进程发送信号
信号产生后,并不是立即处理的,所以要求,进程必须把信号记录下来
记录在:
struct task_struct
{unsigned int sigs;//位图结构
}//比特位位置,信号编号;//比特位内容,是否收到
这属于OS内的数据结构对象,修改位图的本质就是修改内核的数据
不管信号怎么产生,发送信号,在底层,必须让给OS发送
修改位图是OS自己完成的,它会提供发送信号的系统调用kill发送信号的本质是:向目标进程写信号,修改位图,pid,信号的编号
2.2 系统调用
⾸先在后台执⾏死循环程序,然后⽤kill命令给它发SIGSEGV信号$ kill -SIGSEGV 213784
$ // 多按⼀次回⻋
[1]+ Segmentation fault ./sig• 213784 是 sig 进程的pid。之所以要再次回⻋才显⽰ Segmentation fault ,是因为在
213784 进程终⽌掉之前已经回到了Shell提⽰符等待⽤⼾输⼊下⼀条命令, Shell 不希望
Segmentation fault 信息和⽤⼾的输⼊交错在⼀起,所以等⽤⼾输⼊命令之后才显⽰。• 指定发送某种信号的 kill 命令可以有多种写法,上⾯的命令还可以写成 kill -11
213784 , 11 是信号 SIGSEGV 的编号。以往遇到的段错误都是由⾮法内存访问产⽣的,⽽这个
程序本⾝没错,给它发SIGSEGV也能产⽣段错误。
2.3 使用函数产生信号
2.3.1 kill
kill第二个参数:信号编号(Signal Number)
实现⾃⼰的 kill 命令
2.3.2 raise
raise(sig) 等价于执行 kill(getpid(), sig)应用场景:
1.异常处理与程序终止
在检测到严重错误时,主动触发 SIGABRT(异常终止)或 SIGTERM(正常终止)
。。。
raise 函数可以给当前进程发送指定的信号(⾃⼰给⾃⼰发信号)NAMEraise - send a signal to the caller
SYNOPSIS#include <signal.h>int raise(int sig);
RETURN VALUEraise() returns 0 on success, and nonzero for failure.
2.3.3 abort
abort 函数使当前进程接收到信号⽽异常终⽌。NAMEabort - cause abnormal process termination
SYNOPSIS#include <stdlib.h>void abort(void);
RETURN VALUEThe abort() function never returns.// 就像exit函数⼀样,abort函数总是会成功的,所以没有返回值。
2.4 [硬件]异常(系统崩掉)
我们在C/C++当中除零,内存越界等异常,在系统层⾯上,是被当成信号处理的除0后发现⼀直有8号信号产⽣被我们捕获,这是为什么呢?上⾯我们只提到CPU运算异常后,如何
处理后续的流程,实际上 OS 会检查应⽤程序的异常情况,其实在CPU中有⼀些控制和状态
寄存器,主要⽤于控制处理器的操作,通常由操作系统代码使⽤。状态寄存器可以简单理解
为⼀个位图,对应着⼀些状态标记位、溢出标记位。OS 会检测是否存在异常状态,有异常存
在就会调⽤对应的异常处理⽅法。
除零异常后,我们并没有清理内存,关闭进程打开的⽂件,切换进程等操作,所以CPU中还
保留上下⽂数据以及寄存器内容,除零异常会⼀直存在,就有了我们看到的⼀直发出异常信
号的现象。
2.5 软件条件
• 调⽤ alarm 函数可以设定⼀个闹钟,也就是告诉内核在 seconds 秒之后给当前进程发
SIGALRM 信号,该信号的默认处理动作是终⽌当前进程。
• 这个函数的返回值是0或者是以前设定的闹钟时间还余下的秒数。打个⽐⽅,某⼈要⼩睡⼀觉,设定闹
钟为30分钟之后响,20分钟后被⼈吵醒了,还想多睡⼀会⼉,于是重新设定闹钟为15分钟之后响,“以
前设定的闹钟时间还余下的时间”就是10分钟。如果seconds值为0,表⽰取消以前设定的闹钟,函数
的返回值仍然是以前设定的闹钟时间还余下的秒数。
2.5.1 利用alarm -- 体会IO效率问题
IO多时(cout等等),效率明显下降;与IO少的相比,是数量级的差别(十万:1亿)
2.5.2 设置重复闹钟
pause() 是一个简单但强大的系统调用,用于让进程等待信号。它在实现事件驱动的程序、
超时控制或资源等待时非常有用。使用时需注意信号处理函数的设计和竞态条件,
必要时结合 sigprocmask() 和 sigsuspend() 确保安全。NAMEpause - wait for signalSYNOPSIS#include <unistd.h>int pause(void);DESCRIPTIONpause() causes the calling process (or thread) to sleep until a signalis delivered that either terminates the process or causes the invoca‐tion of a signal-catching function.RETURN VALUEpause() returns only when a signal was caught and the signal-catchingfunction returned. In this case, pause() returns -1, and errno is setto EINTR
实例:

2.5.3 如何理解软件条件
在操作系统中,信号的软件条件指的是由软件内部状态或特定软件操作触发的信号产⽣机制。这些条
件包括但不限于定时器超时(如alarm函数设定的时间到达)、软件异常(如向已关闭的管道写数据
产⽣的SIGPIPE信号)等。当这些软件条件满⾜时,操作系统会向相关进程发送相应的信号,以通知
进程进⾏相应的处理。简⽽⾔之,软件条件是因操作系统内部或外部软件操作⽽触发的信号产⽣。
3. 保存信号
• 实际执⾏信号的处理动作称为信号递达(Delivery) -> 自定义,默认,忽略
• 信号从产⽣到递达之间的状态,称为信号未决(Pending) -> 信号在位图中,还没来得及处理
• 进程可以选择阻塞 (Block )某个信号。
• 被阻塞的信号产⽣时将保持在未决状态,直到进程解除对此信号的阻塞,才执⾏递达的动作.
• 注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,⽽忽略是在递达之后可选的⼀种处理动
作。
3.1 在内核中的表示
1. 每个信号都有两个标志位分别表⽰阻塞(block)和未决(pending),还有⼀个函数指针表⽰处理动
作。信号产⽣时,内核在进程控制块中设置该信号的未决标志,直到信号递达才清除该标志。在上
图的例⼦中,SIGHUP信号未阻塞也未产⽣过,当它递达时执⾏默认处理动作。2. SIGINT信号产⽣过,但正在被阻塞,所以暂时不能递达。虽然它的处理动作是忽略,但在没有解除阻
塞之前不能忽略这个信号,因为进程仍有机会改变处理动作之后再解除阻塞。3. SIGQUIT信号未产⽣过,⼀旦产⽣SIGQUIT信号将被阻塞,它的处理动作是⽤⼾⾃定义函数
sighandler。如果在进程解除对某信号的阻塞之前这种信号产⽣过多次,将如何处理?POSIX.1允许系统递送该信
号⼀次或多次。Linux是这样实现的:常规信号在递达之前产⽣多次只计⼀次,⽽实时信号在递达之
前产⽣多次可以依次放在⼀个队列⾥。
3.2 sigset_t
,每个信号只有⼀个bit的未决标志, ⾮0即1, 不记录该信号产⽣了多少次,阻塞标志也是这样
表⽰的。因此, 未决和阻塞标志可以⽤相同的数据类型sigset_t来存储, , 这个类型
可以表⽰每个信号的“有效”或“⽆效”状态, 在阻塞信号集中“有效”和“⽆效”的含义是该信号
是否被阻塞, ⽽在未决信号集中“有 效”和“⽆效”的含义是该信号是否处于未决状态。阻塞信号集也叫做当前进程的 这⾥的“屏蔽”应该理解为阻塞⽽不是忽略。
3.3 信号集操作函数
sigset_t类型对于每种信号⽤⼀个bit表⽰“有效”或“⽆效”状态, ⾄于这个类型内部如何存储这些
bit则依赖于系统实现, 从使⽤者的⻆度是不必关⼼的, 使⽤者只能调⽤以下函数来操作sigset_ t变量,
⽽不应该对它的内部数据做任何解释, ⽐如⽤printf直接打印sigset_t变量是没有意义的。#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);• 函数sigemptyset初始化set所指向的信号集,使其中所有信号的对应bit清零,表⽰该信号集不包含
任何有效信号。
• 函数sigfillset初始化set所指向的信号集,使其中所有信号的对应bit置位,表⽰ 该信号集的有效信号
包括系 统⽀持的所有信号。
• 注意,在使⽤sigset_ t类型的变量之前,⼀定要调 ⽤sigemptyset或sigfillset做初始化,
使信号集处于确定的 状态。初始化sigset_t变量之后就可以在调⽤sigaddset和
sigdelset在该信号集中添加或删除某种有效信号。这四个函数都是成功返回0,出错返回-1。sigismember是⼀个布尔函数,⽤于判断⼀个信号集的有效信
号中是否包含 某种 信号,若包含则返回1,不包含则返回0,出错返回-1
3.3.1 sigprocmask
调⽤函数 sigprocmask 可以读取或更改进程的信号屏蔽字(阻塞信号集)。#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
返回值:若成功则为0,若出错则为-1如果oset是⾮空指针,则读取进程的当前信号屏蔽字通过oset参数传出。如果set是⾮空指针,则 更改
进程的信 号屏蔽字,参数how指⽰如何更改。如果oset和set都是⾮空指针,则先将原来的信号 屏蔽字
备份到oset⾥,然后 根据set和how参数更改信号屏蔽字。假设当前的信号屏蔽字为mask,下表说明了
how参数的可选值。如果调⽤sigprocmask解除了对当前若⼲个未决信号的阻塞,则在sigprocmask返回前,⾄少将其中⼀
个信号递达。
3.3.2 sigpending
#include <signal.h>
int sigpending(sigset_t *set);
读取当前进程的未决信号集,通过set参数传出。
调⽤成功则返回0,出错则返回-1
3.3.3 Core vs Term
Core:
核心:会在当前路径下,形成一个文件,进程异常退出的时候,进程在内存中的核心数据,
从内存拷贝到磁盘,形成一个文件(核心转储 -- 支持debug)然后进程退出Term:进程直接退出为什么会核心转储???
支持debug -- 开启core dump,直接运行崩溃,gdb,core-file core,直接帮助我们定位到
出错行 --- 事后调试!
4. 捕捉信号
信号的处理,不是立即处理,而是可以等一会再处理,合适的时候,进行信号处理
4.1 信号捕捉的流程
如果信号的处理动作是⽤⼾⾃定义函数,在信号递达时就调⽤这个函数,这称为捕捉信号。
由于信号处理函数的代码是在⽤⼾空间的,处理过程⽐较复杂,举例如下:• ⽤⼾程序注册了 SIGQUIT 信号的处理函数 sighandler 。
• 当前正在执⾏ main 函数,这时发⽣中断或异常切换到内核态。
• 在中断处理完毕后要返回⽤⼾态的 main 函数之前检查到有信号 SIGQUIT 递达(信号检查)
• 内核决定返回⽤⼾态后不是恢复 main 函数的上下⽂继续执⾏,⽽是执⾏ sighandler 函
数, sighandler 和 main 函数使⽤不同的堆栈空间,它们之间不存在调⽤和被调⽤的关系,是两个
独⽴的控制流程(自定义方法必须以用户身份执行)
• sighandler 函数返回后⾃动执⾏特殊的系统调⽤ sigreturn 再次进⼊内核态。
• 如果没有新的信号要递达,这次再返回⽤⼾态就是恢复 main 函数的上下⽂继续执⾏了。
4.2 sigaction
可以设置信号屏蔽字,可以同时处理多个不同信号,但是不能同时处理多个相同信号
#include <signal.h>
int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);1. sigaction函数可以读取和修改与指定信号相关联的处理动作。调⽤成功则返回0,出错则返回- 1。
signo是指定信号的编号。若act指针⾮空,则根据act修改该信号的处理动作。若oact指针⾮空, 则
通过oact传出该信号原来的处理动作。act和oact指向sigaction结构体:2. 将sa_handler赋值为常数SIG_IGN传给sigaction表⽰忽略信号,赋值为常数SIG_DFL表⽰执⾏系统
默认动作,赋值为⼀个函数指针表⽰⽤⾃定义函数捕捉信号,或者说向内核注册了⼀个信号处理函
数,该函数返回值为void,可以带⼀个int参数,通过参数可以得知当前信号的编号,这样就可以⽤同⼀
个函数处理多种信号。显然,这也是⼀个回调函数,不是被main函数调⽤,⽽是被系统所调⽤。3. 当某个信号的处理函数被调⽤时,内核⾃动将当前信号加⼊进程的信号屏蔽字,当信号处理函数返回时⾃
动恢复原来的信号屏蔽字,这样就保证了在处理某个信号时,如果这种信号再次产⽣,那么 它会被阻塞到
当前处理结束为⽌。 如果在调⽤信号处理函数时,除了当前信号被⾃动屏蔽之外,还希望⾃动屏蔽另外⼀
些信号,则⽤sa_mask字段说明这些需要额外屏蔽的信号,当信号处理函数返回时⾃动恢复原来的信号屏蔽字。
4.3 操作系统是怎么运行的
4.3.1 硬件中断
• 中断向量表就是操作系统的⼀部分,启动就加载到内存中了
• 通过外部硬件中断,操作系统就不需要对外设进⾏任何周期性的检测或者轮询
• 由外部设备触发的,中断系统运⾏流程,叫做硬件中断
4.3.2 时钟中断
当没有中断到来的时候,OS在做什么??什么也没做!!OS是暂停的;for(;;) pause(); 时钟源会以固定频率,源源不断向CPU发送时钟中断,就这样,OS就在硬件时钟中断的驱动下,进行调度了!所以,OS就是基于中断,进行工作的软件!
// Linux 内核0.11
// main.c
sched_init(); // 调度程序初始化(加载了任务0 的tr, ldtr) (kernel/sched.c)// 调度程序的初始化⼦程序。
void sched_init(void)
{...set_intr_gate(0x20, &timer_interrupt);// 修改中断控制器屏蔽码,允许时钟中断。outb(inb_p(0x21) & ~0x01, 0x21);// 设置系统调⽤中断⻔。set_system_gate(0x80, &system_call);...
}// system_call.s
_timer_interrupt:...
;// do_timer(CPL)执⾏任务切换、计时等⼯作,在kernel/shched.c,305 ⾏实现。
call _do_timer ;// 'do_timer(long CPL)' does everything from// 调度⼊⼝
void do_timer(long cpl){...schedule();
}void schedule(void)
{...switch_to(next); // 切换到任务号为next 的任务,并运⾏之。
}
4.3.3 死循环
4.3.4 软中断
系统调用通过中断机制(如 x86 的int 0x80
或syscall
指令)实现用户态到内核态的切换,确保内核不受用户程序非法操作的影响(汇编和C可以混用)
当我们进行系统调用的时候,具体是怎么进入OS完成调用过程的,毕竟CPU只有一个?
通过系统调用表 -> 从此以后,每一个系统调用,都有一个唯一的下标!!这个下标,我们叫做,系统调用号!!!
像open,fork这样的函数本身并不是OS提供的!!
OS不提供任何系统调用接口!!OS只提供 系统调用号!!
系统调用的过程,也是在进程地址空间上进行的!!所有的函数调用,都是地址空间之间的跳转!
• ⽤⼾层怎么把系统调⽤号给操作系统? - 寄存器(⽐如EAX)
• 操作系统怎么把返回值给⽤⼾?- 寄存器或者⽤⼾传⼊的缓冲区地址
• 系统调⽤的过程,其实就是先int 0x80、syscall陷⼊内核,本质就是触发软中断,CPU就会⾃动执
⾏系统调⽤的处理⽅法,⽽这个⽅法会根据系统调⽤号,⾃动查表,执⾏对应的⽅法
• 系统调⽤号的本质:数组下标!
• ⽤⼾态就是执⾏⽤⼾[0,3]GB时所处的状态
• 内核态就是执⾏内核[3,4]GB时所处的状态
• 区分就是按照CPU内的CPL决定,CPL的全称是Current Privilege Level,即当前特权级别。
• ⼀般执⾏ int 0x80 或者 syscall 软中断,CPL会在校验之后⾃动变更
4.3.5 缺页中断?内存碎片处理?除0野指针错误?
• 操作系统就是躺在中断处理例程上的代码块!
• CPU内部的软中断,⽐如int 0x80或者syscall,我们叫做 陷阱
• CPU内部的软中断,⽐如除零/野指针等,我们叫做 异常

缺页中断:
缺页中断是操作系统存储管理(尤其是虚拟存储系统)中的重要概念,属于中断机制的一种。
当进程访问的逻辑地址(虚拟地址)对应的页面不在物理内存中时,操作系统会触发缺页中断,
将缺失的页面从外存(如磁盘)调入内存后,再让进程继续执行。
4.4 如何理解内核态和用户态
用户和内核,都在同一个[0,4GB]的地址空间上了;如果用户随便拿一个虚拟地址[3,4GB],用户不就可以随便访问内核中的代码和数据了吗?
OS为了保护自己,不相信任何人!!只能采用系统调用的方式进行访问!
用户态:以用户的身份,只能访问自己的[0,3GB];
内核态:以内核的身份,运行你通过系统调用的方式,访问OS[3,4GB];
5. 可重入函数
1. main函数调⽤insert函数向⼀个链表head中插⼊节点node1,插⼊操作分为两步,刚做完第⼀步的
时候,因为硬件中断使进程切换到内核,再次回⽤⼾态之前检查到有信号待处理,于是切换 到
sighandler函数,sighandler也调⽤insert函数向同⼀个链表head中插⼊节点node2,插⼊操作的
两步都做完之后从sighandler返回内核态,再次回到⽤⼾态就从main函数调⽤的insert函数中继续
往下执⾏,先前做第⼀步之后被打断,现在继续做完第⼆步。结果是,main函数和sighandler先后 向
链表中插⼊两个节点,⽽最后只有⼀个节点真正插⼊链表中了。2. 像上例这样,insert函数被不同的控制流程调⽤,有可能在第⼀次调⽤还没返回时就再次进⼊该函
数,这称为重⼊,insert函数访问⼀个全局链表,有可能因为重⼊⽽造成错乱,像这样的函数称为 不可
重⼊函数,反之,如果⼀个函数只访问⾃⼰的局部变量或参数,则称为可重⼊(Reentrant) 函数。3. 如果⼀个函数符合以下条件之⼀则是不可重⼊的:
• 调⽤了malloc或free,因为malloc也是⽤全局链表来管理堆的。
• 调⽤了标准I/O库函数。标准I/O库的很多实现都以不可重⼊的⽅式使⽤全局数据结构。
6. volatile
标准情况下,键⼊ CTRL-C ,2号信号被捕捉,执⾏⾃定义动作,修改 flag=1 , while 条件不
满⾜, 退出循环,进程退出优化情况下,键⼊ CTRL-C ,2号信号被捕捉,执⾏⾃定义动作,修改 flag=1 ,但是 while 条
件依旧满⾜,进程继续运⾏!但是很明显flag肯定已经被修改了,但是为何循环依旧执⾏?很明显,
while 循环检查的 flag,并不是内存中最新的 flag,这就存在了数据⼆异性的问题。 while 检
测的 flag 其实已经因为优化,被放在了CPU寄存器当中。如何解决呢?很明显需要 volatilevolatile 作⽤:保持内存的可⻅性,告知编译器,被该关键字修饰的变量,不允许被优化,对该
变量的任何操作,都必须在真实的内存中进⾏操作
7. SIGCHLD信号(了解)

相关文章:

10.Linux进程信号
1. 理解信号 信号VS信号量 老婆:老婆饼-》没有任何关系!信号:闹钟,上课铃声,脸色...人-》进程;信号中断人正在做的事,是一种事件的异步通知机制; 我们自习一会,等张三回…...
Python 函数全攻略:函数基础
函数(Functions)基础 什么是函数? 一个命名的代码块,代指一大堆代码。 定义: def function_name(): (使用def关键字,英文括号,冒号,缩进代码块)。 执行/调用: function…...

机器学习基础(四) 决策树
决策树简介 决策树结构: 决策树是一种树形结构,树中每个内部节点表示一个特征上的判断,每个分支代表一个判断结果的输出,每个叶子节点代表一种分类结果 决策树构建过程(三要素): 特征选择 选…...
DDPM优化目标公式推导
DDPM优化目标公式推导 DDPM优化目标公式推导**1. 问题定义****2. 优化目标:最大化对数似然****3. 变分下界的分解****4. 关键步骤:简化 KL 散度项****(a) 后验分布 q ( x t − 1 ∣ x t , x 0 ) q(\mathbf{x}_{t-1} | \mathbf{x}_t, \mathbf{x}_0) q(xt…...

CentOS 7如何编译安装升级gcc至7.5版本?
CentOS 7如何编译安装升级gcc版本? 由于配置CentOS-SCLo-scl.repo与CentOS-SCLo-scl-rh.repo后执行yum install -y devtoolset-7安装总是异常,遂决定编译安装gcc7.5 # 备份之前的yum .repo文件至 /tmp/repo_bak 目录 mkdir -p /tmp/repo_bak && cd /etc…...

为什么React列表项需要key?(React key)(稳定的唯一标识key有助于React虚拟DOM优化重绘大型列表)
文章目录 1. **帮助 React 识别列表项的变化**2. **性能优化**3. **避免组件状态混乱**4. **为什么使用 rpid 作为 key**5. **不好的做法示例**6. **✅ 正确的做法** 在 React 中添加 key{item.rpid} 是非常重要的,主要有以下几个原因: 1. 帮助 React 识…...
Playwright自动化测试全栈指南:从基础到企业级实践(2025终极版)
引言 在Web应用复杂度指数级增长的今天,传统自动化测试工具面临动态渲染适配难、多浏览器兼容差、测试稳定性低三大挑战。微软开源的Playwright凭借跨浏览器支持、自动等待机制和原生异步架构,成为新一代自动化测试的事实标…...

飞牛云一键设置动态域名+ipv6内网直通访问内网的ssh服务-家庭云计算专家
IPv6访问SSH的难点与优势并存。难点主要体现在网络环境支持不足:部分ISP未完全适配IPv6协议,导致客户端无法直接连通;老旧设备或工具(如Docker、GitHub)需额外配置才能兼容IPv6,技术门槛较高;若…...
虚实共生时代的情感重构:AI 恋爱陪伴的崛起、困局与明日图景
一、虚拟恋人:从技术幻想到情感刚需的跨越 在 5G 网络编织的数字浪潮里,AI 驱动的虚拟恋人正打破次元界限。深度学习算法剖析 3000 万段真实对话语料库,搭配 VR 设备带来的多维度交互体验,如今的虚拟对象已能精准模拟瞳孔微表情&…...
嵌入式面试高频(5)!!!C++语言(嵌入式八股文,嵌入式面经)
一、C有几种传值方式之间的区别 一、值传递(Pass by Value) 机制:创建参数的副本,函数内操作不影响原始数据语法:void func(int x)特点: 数据安全:原始数据不受影响性能开销:需要复…...
C++动态规划-线性DP
这是一套C线性DP题目的答案。如果需要题目,请私信我,我将会更新题干 P1:单子序列最大和 #include <bits/stdc.h> using namespace std; int n,A,B,C; int a[200005]; int s[200005]; int main() {ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)…...

Java高级 | 【实验七】Springboot 过滤器和拦截器
隶属文章:Java高级 | (二十二)Java常用类库-CSDN博客 系列文章:Java高级 | 【实验一】Springboot安装及测试 |最新-CSDN博客 Java高级 | 【实验二】Springboot 控制器类相关注解知识-CSDN博客 Java高级 | 【实验三】Springboot 静…...
es地理信息索引的类型以及geo_point和geo_hash的关系
Elasticsearch中地理信息索引的主要数据类型有两种: geo_point:用于存储单个地理点坐标(如纬度/经度),支持精确位置查询和基于距离的搜索操作。geo_shape:用于存储复杂的地理形状(如点、线、多…...

深入理解 Spring IOC:从概念到实践
目录 一、引言 二、什么是 IOC? 2.1 控制反转的本质 2.2 类比理解 三、Spring IOC 的核心组件 3.1 IOC 容器的分类 3.2 Bean 的生命周期 四、依赖注入(DI)的三种方式 4.1 构造器注入 4.2 Setter 方法注入 4.3 注解注入(…...
Vue解决开发环境 Ajax 跨域问题
一、前言 在使用 Vue 进行前后端分离开发时,前端通常运行在本地开发服务器(如 http://localhost:8080),而后端接口可能部署在其他域名或端口下(如 http://api.example.com:3000)。这时就可能出现 跨域&…...

行为设计模式之Command (命令)
行为设计模式之Command (命令) 前言: 需要发出请求的对象(调用者)和接收并执行请求的对象(执行者)之间没有直接依赖关系时。比如遥控器 每个按钮绑定一个command对象,这个Command对…...
若依添加添加监听容器配置(删除键,键过期)
1、配置Redis的键触发事件 # 基础配置 bind 0.0.0.0 # 允许所有IP连接 protected-mode no # 关闭保护模式(生产环境建议结合密码使用) port 6379 # 默认端口 daemonize no …...

NeRF 技术深度解析:原理、局限与前沿应用探索(AI+3D 产品经理笔记 S2E04)
引言:光影的魔法师——神经辐射场概览 在前三篇笔记中,我们逐步揭开了 AI 生成 3D 技术的面纱:从宏观的驱动力与价值(S2E01),到主流技术流派的辨析(S2E02),再到实用工具的…...
ROS2,工作空间中新建了一个python脚本,需要之后作为节点运行。告诉我步骤?
提问 ROS2,工作空间中新建了一个python脚本,需要之后运行。告诉我步骤? 大概要包括而不限于:chmod给可执行权限、setup.py中entry point的配置,如果在launch文件中要使用,还涉及到launch.py文件的配置。最…...
【AI智能体】Spring AI MCP 从使用到操作实战详解
目录 一、前言 二、MCP 介绍 2.1 什么是MCP 2.2 MCP 核心特点 2.3 MCP 核心价值 2.4 MCP 与Function Calling 区别 三、Spring AI MCP 架构介绍 3.1 整体架构 3.1.1 三层架构实现说明 3.2 服务端与客户端 3.2.1 MCP 服务端 3.2.1 MCP 客户端 3.3 MCP中SSE和STDIO区…...
Vue:Ajax
AJAX 允许我们在不刷新页面的情况下与服务器交互,实现:动态加载数据,提交表单信息,实时更新内容,与后端 API 通信。通常使用专门的 HTTP 客户端库来处理 AJAX 请求。 npm install axiosimport axios from axios;expor…...

法律大语言模型(Legal LLM)技术架构
目录 摘要 1 法律AI大模型技术架构 1.1 核心架构分层 1.2 法律知识增强机制 2 关键技术突破与对比 2.1 法律专用组件创新 2.2 性能对比(合同审查场景) 3 开发部署实战指南 3.1 环境搭建流程 3.2 合同审查代码示例 4 行业应用与挑战 4.1 典型场景效能提升 4.2 关…...
理解 RAG_HYBRID_BM25_WEIGHT:打造更智能的混合检索增强生成系统
目录 理解 RAG_HYBRID_BM25_WEIGHT:打造更智能的混合检索增强生成系统 一、什么是 Hybrid RAG? 二、什么是 RAG_HYBRID_BM25_WEIGHT? 三、参数设置示例 四、什么时候该调整它? 五、实战建议 六、总结 理解 RAG_HYBRID_BM25…...
Hive终极性能优化指南:从原理到实战
摘要:本文系统总结Hive在生产环境的核心调优手段,涵盖执行引擎选择、存储优化、SQL技巧、资源调配及数据倾斜解决方案,附可复用的参数配置与实战案例。 一、执行引擎优化:突破MapReduce瓶颈 启用Tez/Spark引擎 优势&am…...

第六十二节:深度学习-加载 TensorFlow/PyTorch/Caffe 模型
在计算机视觉领域,OpenCV的DNN(深度神经网络)模块正逐渐成为轻量级模型部署的利器。本文将深入探讨如何利用OpenCV加载和运行三大主流框架(TensorFlow、PyTorch、Caffe)训练的模型,并提供完整的代码实现和优化技巧。 一、OpenCV DNN模块的核心优势 OpenCV的DNN模块自3.3…...

MobaXterm配置跳转登录堡垒机
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 背景操作步骤 背景 主要是为了能通过MobaXterm登录堡垒机,其中需要另外一台服务器进行跳转登录 操作步骤 MobaXterm登录堡垒机的操作,需…...

零基础在实践中学习网络安全-皮卡丘靶场(第八期-Unsafe Filedownload模块)
这期内容更是简单和方便,毕竟谁还没在浏览器上下载过东西,不过对于url的构造方面,可能有一点问题,大家要多练手 介绍 不安全的文件下载概述 文件下载功能在很多web系统上都会出现,一般我们当点击下载链接,…...
测试 FreeSWITCH 的 mod_loopback
bgapi originate loopback/answer,park/default/inline park inline show channels as xml show calls as xml 有 2 个 channels 有 2 个 calls 比较有意思 在 loopback-a 是播放 wav 在 loopback-b 上可以录音 这就是回环 有什么用呢? 除了做测试&#x…...
【C++快读快写】
算法竞赛中用于解决卡常问题 int rd(){int k 0;char c getchar();while(!isdigit(c)){c getchar();}while(isdigit(c)){k (k << 1) (k << 3) (c^0), c getchar();}return k; }void wr(int x) {if (x > 9)wr(x / 10);putchar((x % 10) ^ 0); }用法&#x…...
测试(面经 八股)
目录 前言 一,软件测试(定义) 1,定义 2,目的 3,价值 4,实践 二,软件测试(目的) 1,找 bug 2,验证达标 3,质量评价…...