【Linux我做主】探秘进程与fork
进程和fork
- 父子进程和fork
- github地址
- 前言
- 1. 进程的标识符PID
- 1.1 查看系统内所有的进程
- 1.2 kill杀掉进程
- 1.3 获取进程的PID
- 1.4 bash与父子进程
- 2. 创建进程与fork
- 2.1 fork创建子进程
- 2.2 fork困惑的解释
- 0. fork的工作原理
- 1. 为什么给子进程返回0,给父进程返回子进程PID
- 2. 一个函数如何做到返回两次?如何理解?
- 2.1 为什么父子进程共享fork之后的代码
- 2.2 理解一个函数做到返回两次
- 2.3 fork函数内做了什么?
- 3. 一个id变量里面怎么会有两个值
- 3.1 写时拷贝
- 3.2 fork中的写时拷贝
- 3. 进程与调度器
- 4. bash与fork
- 5. 结语
父子进程和fork
github地址
有梦想的电信狗
前言
在前文从冯诺依曼体系到进程中,我们认识了进程的概念。在操作系统中,进程是程序执行的基本单位。Linux
下进程是由一个个的task_struct
组织起来的。Linux
通过task_struct
结构体管理进程,其中包含进程的所有属性。本文将从进程的标识符(PID)
入手,深入探讨如何通过fork
系统调用创建子进程,并分析其背后的深层次原理。
1. 进程的标识符PID
每个进程都有一个唯一的进程标识符(PID)
,用于在系统中唯一标识该进程。
1.1 查看系统内所有的进程
在Linux
中,可以通过ps
命令查看进程的PID
命令:ps
选项:ajx
等
ps -ef # 显示所有进程的详细信息
ps aux # 显示所有用户的所有进程,重在用户# 我们一般上使用 ps ajx 查看系统内所有的进程
ps ajx # 查看系统内所有的进程./proc # 以运行proc进程为例
ps ajx | head -1 && ps ajx | grep proc
ps ajx | head -1 && ps ajx | grep proc | grep -v grep
ps ajx演示:
我们一般在进行grep
进程时,会过滤出grep
命令本身,因为grep
命令也是一个进程
隐藏掉grep
关键字的命令:ps ajx | head -1 && ps ajx | grep proc | grep -v grep
&&
表示左边的命令执行完,紧接着执行右边的命令。左边的命令执行成功,右边的命令也要执行成功。ps ajx | head -1 && ps ajx | grep proc
该命令得到的结果会同时显示proc
进程和grep
进程。- 如果我们不想显示
grep
进程,可以使用管道,对上述命令的结果再进行grep
ps ajx | head -1 && ps ajx | grep proc | grep -v grep
-v
选项配合管道,在已有的结果中反向匹配grep,可以隐藏掉grep
关键字
- 如果我们不想显示
1.2 kill杀掉进程
命令:kill [选项] PID
常用选项:-9
功能:
kill PID
是温柔的杀掉这个进程kill -9 PID
向指定PID
的某个进程发送9
信号,暴力的杀掉这个进程
演示:
kill -9 760776 # 强制终止PID为760776的进程
kill -9 760776
的结果如下:
当使用不带 -9
选项的 kill PID
命令时,默认会向目标进程发送 SIGTERM
信号(信号编号为 15)。与 kill -9
(发送 SIGKILL 信号)的强制终止不同,SIGTERM
是一种更“友好”的终止方式,(信号部分之后的文章会介绍,)目前介绍区别如下:
kill PID
(默认发送 SIGTERM)的行为:
- 允许进程优雅退出
进程收到 SIGTERM 后,可以执行清理操作(如保存数据、关闭文件、释放资源等),然后自行终止。 - 进程可以捕获或忽略 SIGTERM
如果进程代码中注册了信号处理函数(例如通过signal()
或sigaction()
),它可以自定义对 SIGTERM 的响应(如延迟退出或忽略信号)。 - 可能无法立即终止进程
如果进程因代码缺陷、死锁或无限循环无法响应 SIGTERM,它可能不会退出。此时需手动使用kill -9
强制终止。
kill -9 PID
(发送 SIGKILL)的行为:
- 强制立即终止进程
SIGKILL 信号无法被进程捕获或忽略,操作系统会直接终止进程,不给进程任何清理的机会。 - 可能导致资源泄漏
进程无法执行清理操作,可能导致临时文件残留、内存未释放、文件句柄未关闭等问题。 - 适用于“无响应”的进程
当进程对 SIGTERM 无响应时,SIGKILL 是最后手段。
使用建议:
- 优先使用默认的
kill PID
(SIGTERM)
给进程机会优雅退出,避免数据丢失或资源泄漏。 - 仅在必要时使用
kill -9
(SIGKILL)
例如进程完全卡死、僵尸进程或恶意进程等情况。
扩展知识:
-
查看所有信号:
kill -l
-
常用信号:
SIGHUP
(1):挂起(重新加载配置)SIGINT
(2):中断(同 Ctrl+C)SIGTERM
(15):终止SIGKILL
(9):强制终止
通过合理选择信号,可以更安全地管理系统进程。
1.3 获取进程的PID
Linux
操作系统中,描述系统进程的task_struct
是用双向链表组织的ps ajx
的作用,相当于遍历task_struct
的链表,拿到所有进程的相关属性,打印出来,供我们查看PID
- 既然可以拿到所有进程的相关属性,那么对于一个特定进程的PID,应当也是可以获取到的
操作系统不相信任何用户,不能让用户通过
task_struct.pid
的方式通过结构体直接访问PID
。因此操作系统一定对外提供了系统调用接口,供用户访问task_struct
内描述进程的相关属性。
在Linux
中,可通过系统getpid()
或getppid()
调用获取进程的PID
:
pid_t getpid()
:获取当前进程的PID。- 进程都是被创建出来的,一个进程除了有自己的PID,也有自己的父进程,父进程的
PID
也可以被获取到 pid_t getppid()
:获取父进程的PID。- 返回值类型均为
pid_t
,本质是int
的类型别名。typedef int pid_t
getpid代码演示
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>int main() {pid_t pid = getpid();pid_t ppid = getppid(); // pid_t 本质是有符号整数while (1) {printf("I am a precess, my ID is %d, my parent ID is %d\n", pid, ppid);sleep(1);}
}
- 我们可以手动将
ps ajx
配合grep
命令,制作一个系统内进程监控脚本,监控系统内进程的信息。
while :; do ps axj | head -1 ; ps ajx | grep proc | grep -v grep | grep -v .vscode; sleep 1; done
-
sleep 1
:每秒执行一次 -
ps axj | head -1 ; ps ajx | grep proc | grep -v grep | grep -v .vscode
:过滤我们的proc
进程,不显示grep
命令本身的进程和.vscode
远程连接的进程 -
我们可以手动在终端中执行以上命令。也可以将以上内容保存在一个后缀为
.sh
的文件中,这里我选择保存为一个sh文件,并命名为monitor.sh
-
保存后,为当前文件增加执行权限后并执行
chmod u+x monitor.sh # 增加执行权限 bash monitor.sh # 执行脚本
-
-
运行效果如下:
- 这样就实现了系统内运行进程的实时监控,我们利用该脚本来监测我们启动的进程
- 可以看到:
- 进程启动前,检测脚本未检测到任何内容
- 进程启动后,检测脚本就检测到了我们启动的
proc
进程 - 且
ps ajx
查看到的PID
及PPID
和getpid()
和getppid()
获取到的内容完全一致!!!
- 这样我们就可以通过
PID
来对进程进行管理了
1.4 bash与父子进程
观察以下现象,注意proc进程的PID
- 一个程序,多次启动时PID在变。
-
一个程序,多次启动时PID在变。。
- 这是因为:
PID
只保证在每次运行期间有效,下次启动,操作系统为该进程分配的PID
可能会变化,这是正常的。
- 这是因为:
-
一个程序,多次启动时的PID在变,其父进程的PID一直不变。这是为什么?父进程的
PID
一直是761739
,这里的父进程是什么进程? -
我们来查看
PID
为761739
的进程ps ajx | head -1 && ps ajx | grep 761739
-
这里可以清楚的看到,
761739
在这里就是我们的命令行解释器bash
!
Bash
(命令行解释器)本身是一个进程,用户执行的命令(如ls
、./a.out
等)均为Bash的子进程
。Bash
的PID
在单次登录会话中固定,但重新登录后会变化。
我们在命令行解释器bash
中,执行的所有指令(包括命令和./执行的程序
)的父进程,就是bash
本身
-
每次重新登陆
xshell
时,Linux
系统会单独为我们创建一个bash
进程,即为我们创建一个命令行解释器进程,帮我们在显示器中打印出命令行终端 -
我们在命令行解释器中,执行或输入的所有指令和程序,
bash
都会为我们创建进程,这些程序都是bash
的子进程
bash
只负责命令行解释,具体执行出问题时,只会影响对应的子进程。因此在同一登陆状态下,指令和程序的父进程PID
不变,也就是bash
的PID
不变 -
一个
bash
有一个PID
,多开多个bash
,会有多个bash
PID。进程主要维护父子关系
./
操作执行程序或者命令时,就是操作系统为我们创建了一个进程,在操作系统上运行
父子进程关系特点:
- 父进程负责管理子进程。
- 子进程退出后,父进程需要通过
wait
系统调用回收资源。 - 若父进程先退出,子进程会成为“孤儿进程”,由
init
进程(PID=1
)接管。
2. 创建进程与fork
2.1 fork创建子进程
以上我们介绍了获取父子进程
PID
相关的知识,那么我们用户可否创建一个进程呢
我们目前已知的创建进程的方式,
./exe
操作,执行程序或者执行命令时,就是操作系统为我们创建了一个进程,在操作系统上运行
这种方式是我们手动创建进程,那么可否在程序运行时创建进程呢?
Linux
内核为我们提供了系统调用fork()
,用于为当前进程创建一个子进程。
fork
是Linux
中创建子进程的核心系统调用,其独特的行为常引发初学者的困惑
使用man 手册查看fork函数的用法
man 2 fork
-
函数名和功能:
fork
- 为当前调用fork的进程创建一个子进程。
- 新进程为子进程(
child process
),当前调用进程为父进程(parent process
) - 子进程复制父进程的代码、数据、堆栈和打开的文件描述符。
-
头文件:
<sys/types.h>
和<unistd.h>
-
参数类型:
void
无需传参 -
返回值:类型为
pid_t
,这里pid_t
是int
的类型别名。typedef int pid_t
-
根据文档介绍,
- 创建子进程失败时,在原进程中返回
-1
,并设置errno
- 成功时:
- 在父进程中返回子进程的
PID
- 在子进程中返回0
- 在父进程中返回子进程的
- 创建子进程失败时,在原进程中返回
fork
的简单使用:
int main(){printf("before: only one line\n");fork();printf("after: only one line\n");sleep(1);
}
这里我们不免会有疑惑???
==fork之后的代码,执行了两次!!!==这是为什么?
先给出结论:fork之后的代码,父子进程是共享的,既然父子进程各有一份代码(共享),那fork
之后的代码执行了两次就可以说得通了
再看如下代码和现象:
- 这里
./proc
是当前进程,fork
之后,现象是:代码被一分为二了,两个循环各自在执行,父子进程各执行一个循环。这说明,id == 0
和id > 0
同时成立了,且根据进程的PID
,我们可以得出:- 执行
fork
之后当前进程是父进程 - 其子进程是由
fork
创建的 - 当前进程的父进程是
bash
进程
- 执行
- fork之后一定存在两个进程,存在两个执行流
以上种种现象,我们不免产生很多疑问???
-
- 为什么
fork
要给子进程返回0
,给父进程返回子进程的pid
呢?为什么父子进程的返回值不同呢?
- 为什么
-
- 一个函数,怎么会有两个返回值,如何做到返回两次呢?
-
- 变量
id
接收fork
的返回值,为什么一个变量可以有两个不同的值?
- 变量
-
fork
函数究竟做了什么?
这些问题我们后文会一一解答
2.2 fork困惑的解释
- 结合
fork
的翻译,分支,分叉,代表我们的代码在fork
这里要进行分叉。
0. fork的工作原理
- 创建子进程的PCB:内核为子进程分配新的
task_struct
。 - 复制父进程上下文:子进程继承父进程的代码段、数据段、堆栈和文件描述符表。
- 分流执行:
fork
返回后,父子进程从同一位置继续执行,但通过返回值区分逻辑分支。
1. 为什么给子进程返回0,给父进程返回子进程PID
一般而言,fork之后的代码,父子共享
解答这个问题,先思考我们为什么要创建子进程?
- 是为了让父子进程协作,执行不同的事情,因此需要想办法让父子进程执行不同的代码块。为了让父子进程协同,执行不同的执行流,就设计了
fork
返回值要不同
为什么要父子进程要分别返回不同的值?
- 是为了在
fork
之后,可以根据不同的返回值区分父子进程,来让父子进程执行不同的代码片段 - 返回不同的返回值,是为了区分。让不同的进程执行流,执行不同的代码块
为什么给子进程返回0,给父进程返回子进程的PID?
-
一个子进程只会有一个父进程,一个父进程可能会有多个子进程
-
父进程需要管理多个子进程,通过
PID
区分不同子进程。- 父进程需要区分不同的子进程:给父进程返回子进程的
PID
,用来标识子进程的唯一性,方便直接通过不同的PID
,对不同的子进程直接做控制
- 父进程需要区分不同的子进程:给父进程返回子进程的
-
子进程只需确认自身身份,返回
0
简化逻辑判断。- 子进程只有一个父进程。
标识子进程
,只需要在父进程内判断fork的返回值是否PID == 0
即可标识子进程。子进程得到父进程的PID
,只需要调用getppid()
函数即可。
- 子进程只有一个父进程。
2. 一个函数如何做到返回两次?如何理解?
2.1 为什么父子进程共享fork之后的代码
原因如下:
原因如下:
-
进程 = 内核数据结构 + 代码和数据
-
fork
创建子进程,也就是内存中多了一个进程,Linux
操作系统会为该子进程创建一个task_struct
-
创建子进程前,该进程有自己的
task_struct
和代码和数据。创建子进程,操作系统层面只创建了子进程的task_struct
,子进程没有自己的代码和数据,只能和父进程共享同一份代码。数据呢?(下文解释) -
因此fork之后,父子进程共享后续的代码
fork之后,父子进程共享后续的代码。
fork
之后父子进程执行的代码一样,那我们为什么要创建子进程呢?我们的目的就是为了让父子进程协同起来分别做不同的事情。因此需要想办法让父子进程执行不同的代码块
。让fork
函数具有不同的返回值就是为此设计的。那么
fork
如何设计实现了以上功能?
2.2 理解一个函数做到返回两次
代码示例:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>int main() {printf("begin:我是一个进程, pid: %d, ppid: %d\n", getpid(), getppid());pid_t id = fork();if (id == 0) { // 子进程分支while (1) {printf("我是子进程, pid: %d, ppid: %d\n", getpid(), getppid());sleep(1);}} else if (id > 0) { // 父进程分支 成功时,子进程的PID > 0 返回到父进程while (1) {printf("我是父进程, pid: %d, ppid: %d\n", getpid(), getppid());sleep(1);}} else {// error}return 0;
}
fork
有不同的返回值? 一个函数如何做到返回两次
解释如下:
-
任何一个函数在即将
return
时,这个函数的核心功能就已经完成了。 -
fork
是一个函数,内部的实现是要创建子进程。return之前,父子进程都已存在,各自有独立的task_struct
且可以被CPU调度。此时在临界执行return
之前,父子进程的task_struct(PCB)
都已存在了,父子进程也都已存在。
return
执行之前父子进程均已创建完成,且可以被CPU
调度。创建子进程后,父子进程代码共享,return
语句也属于代码,因此父子进程共享return语句。CPU
分别调度父子进程,父子进程分别执行了return
,就实现了一个函数返回两次
fork
在即将执行return
之前,创建子进程的工作已经完成了,子进程也允许被CPU调度了,之后的return
代码父子进程共享,父和子进程分别执行return
,因此fork
函数就实现了返回两次
2.3 fork函数内做了什么?
综上我们可以总结出fork
内部究竟做了什么:
- 创建子进程
- 为子进程创建PCB
- 用父进程的字段初始化子进程
- 让父子进程实现代码共享
- …其他工作
3. 一个id变量里面怎么会有两个值
任何平台下,进程在运行时具有独立性!进程的运行互不干扰
- 一个id变量里面有两个值的现象,实际是通过写时拷贝实现的
-
子进程刚被创建时,代码和数据都是父子进程共享的
代码加载到内存中后,是不可修改的,因此代码直接父子共享。 -
子进程在读取父进程的数据时,数据也是共享的。当子进程尝试修改父进程的数据时,操作系统为子进程新分配一块内存空间,让子进程对待修改数据的拷贝进行修改,修改多少内容,就申请多少空间,从而不会影响到父进程的数据,该过程称为写时拷贝
以上称为父子进程之间,数据层面的写时拷贝,写时拷贝保证了父子进程间数据的独立性,避免了父或子进程修改数据后对子或父进程的数据产生影响
3.1 写时拷贝
fork
之后父子进程共享代码段,但数据段通过写时拷贝(COW——Copy-On-Write)技术实现高效复制。
原理:
- 子进程创建时,与父进程共享物理内存。
- 当子进程尝试修改内存时,内核会为该内存页创建副本,确保修改独立。
优点:
- 减少内存全量复制开销。
- 提高
fork
的执行效率
3.2 fork中的写时拷贝
return
时,return
是对id
做写入。- return语句父子进程共享,父子进程分别return。
- 父进程
return
时,直接对id
做写入,子进程return
时,对id
做写时拷贝
3. 进程与调度器
问题:fork()
后父子进程谁先运行?
当调用 fork()
创建子进程后,父子进程会被同时放入操作系统的就绪队列中等待执行。它们的运行顺序由操作系统的进程调度器决定,用户无法预测。这是操作系统的核心设计原则之一:调度器保证公平性,而非确定性。因此fork()
后父子进程谁先运行,无法确定,取决于调度器。
调度器的工作原理
-
进程的组织形式
所有进程的PCB(进程控制块)以数据结构(如链表、队列、树等)组织在内存中,例如:-
就绪队列:等待CPU时间的进程
-
阻塞队列:等待I/O或其他资源的进程
-
-
调度器的核心任务
调度器从就绪队列中选择一个进程分配CPU时间,具体流程如下:-
触发时机:时钟中断、进程阻塞、进程退出等。
-
选择算法:通过调度算法(如时间片轮转、优先级调度)选择下一个进程。
-
上下文切换:保存当前进程的CPU状态(寄存器、程序计数器等),加载目标进程的上下文。
-
-
常见的调度算法
算法 特点 先来先服务 (FCFS) 简单,但可能导致“饥饿”现象 时间片轮转 (RR) 每个进程分配固定时间片,公平性强,适合交互式系统 优先级调度 高优先级进程优先执行,需防止低优先级进程“饿死” 多级反馈队列 结合时间片和优先级,动态调整进程优先级,兼顾响应时间和吞吐量
为什么单核CPU能“同时”运行数百个进程?
-
并发假象
调度器通过快速切换进程(纳秒级时间片)营造“并行”假象。例如:-
进程A运行5ms → 切换到进程B运行5ms → 切换回进程A……
-
人类无法感知微小的时间片切换,因此感觉多个进程在“同时”运行。
-
-
父子进程的竞争
在fork()
后,父子进程进入就绪队列,可能发生以下情况:-
父进程先获得CPU时间(常见,因父进程可能处于活跃状态)。
-
子进程先获得CPU时间(可能因父进程被阻塞或时间片耗尽)。
-
父子进程交替执行(取决于调度策略和系统负载)。
-
4. bash与fork
bash
也是通过fork
创建子进程的
Bash执行命令的流程
当在Bash
中输入命令(如 ls -l
)时,Bash
的底层操作如下:
-
fork()
:创建子进程-
子进程复制父进程(
Bash
)的内存、文件描述符、环境变量等。 -
优化技术:写时复制 (Copy-On-Write),仅在数据被修改时复制内存,减少开销。
-
-
exec()
:加载新程序- 子进程调用
exec()
系统调用,用目标程序(如/bin/ls
)替换当前内存空间。
- 子进程调用
-
wait()
:父进程等待
-
Bash
(父进程)调用wait()
阻塞自身,直到子进程结束并回收资源。 -
若未调用
wait()
,子进程退出后会成为僵尸进程(Zombie),占用系统资源。
5. 结语
进程是操作系统资源分配的基本单位,而 fork
作为创建子进程的核心机制,通过代码共享、写时拷贝和调度器的协同工作,实现了高效的多任务管理。理解父子进程的关系、fork
的“分流”特性以及调度器的随机性,是掌握进程管理的关键。无论是 bash
执行命令时的隐式 fork
,还是程序内显式创建子进程,本质都在通过进程的独立性完成并发任务。写时拷贝技术平衡了性能与资源隔离,而调度器的公平策略让单核 CPU 也能营造“并行”假象。希望本文为你揭开了进程与 fork
的神秘面纱
以上就是本文的所有内容了,如果觉得文章写的不错,还请留下免费的赞和收藏,也欢迎各位大佬在评论区交流
分享到此结束啦
一键三连,好运连连!
相关文章:
【Linux我做主】探秘进程与fork
进程和fork 父子进程和forkgithub地址前言1. 进程的标识符PID1.1 查看系统内所有的进程1.2 kill杀掉进程1.3 获取进程的PID1.4 bash与父子进程 2. 创建进程与fork2.1 fork创建子进程2.2 fork困惑的解释0. fork的工作原理1. 为什么给子进程返回0,给父进程返回子进程P…...
学习python day4
1.顺序语句结构 #赋值语句 name张三 age20 a,b,c,droom#字符串分解赋值 print(a,b,c,d) #输入输出也是典型的顺序结构 nameinput(请输入您的姓名:) ageeval(input(请输入您的年龄:)) #使用eval进行转换 print(姓名:,name) print(年龄&#x…...
如何通过外链建设提升Shopify独立站的权重和排名
一、外链质量评估与筛选原则 相关性优先 选择与自身行业、产品或目标用户群体高度相关的网站(如行业论坛、垂直媒体、评测博客)交换外链,避免低相关性垃圾链接导致搜索引擎惩罚。 权威性指标 关注外链来源网站的域名权威(DA…...
高并发内存池|六、page cache的设计
六、page cache的设计 1. page cache的结构 page cache 也是一个哈希桶结构,但它的映射结构与前两个 cache 不同。它的每一个桶是容量从 1 到 128 页大小的内存块,桶中的每个内存块由 SpanList 管理。page cache 的内存由其向系统申请所得,…...
C++线程池实现
C线程池实现 知识点补充为什么要实现线程池线程池的实现过程 知识点补充 在C11中引入了对线程库的支持,接下来我们介绍一下跟线程相关的一些知识点: 线程对象的构造方式 在C11中主要提供的三种线程的构造方式:无参构造、带参构造和调用移动构…...
#Redis缓存篇#(七)分布式缓存
目录 一 单节点Redis 1 单节点的问题 二 分布式缓存 1 Redis持久化 (1 RDB持久化 (2 AOF持久化 2 Redis主从集群 (1 搭建主从架构 (2 主从数据同步原理 3 Redis哨兵 (1 哨兵的作用和原理 (2 搭…...
【VSCode】安装与 ssh 免密登录
【VSCode】安装与 ssh 免密登录 下载SSH 登录设置免密登录关闭远程连接删除ssh连接(慎用!!!删除了建立的连接就没有了!!) 下载 https://code.visualstudio.com/docs/?dvwin64user 选择安装路径…...
【Python解决八皇后问题】回溯算法与优化策略全解析
目录 🌟 前言🏗️ 技术背景与价值🩹 当前技术痛点🛠️ 解决方案概述👥 目标读者说明🧠 一、技术原理剖析📊 核心概念图解💡 核心作用讲解🔧 关键技术模块说明⚖️ 技术选型对比🛠️ 二、实战演示⚙️ 环境配置要求💻 核心代码实现基础回溯实现位运算优化…...
判断一个元素是否在可视区域
判断元素是否在可视区域的方法 方法一:offsetTop 和 scrollTop 通过计算元素的 offsetTop 和容器的 scrollTop 来判断元素是否位于视口内。这种方法适用于简单的垂直滚动场景。 优点: 实现简单,性能较好。缺点: 不支持复杂的布局结构(如嵌套滚动),无法处理水平方向上的可…...
作物遗传与种质创新利用全国重点实验室-随笔10
作物遗传与种质创新利用全国重点实验室依托于南京农业大学,2022年11月完成国家重点实验室重组工作,由原名称“作物遗传与种质创新国家重点实验室”正式更名为“作物遗传与种质创新利用全国重点实验室”。 实验室面向国家粮食安全和农业高质量发展的重大战…...
分布式电源的配电网无功优化
分布式电源(Distributed Generation, DG)的大规模接入配电网,改变了传统单向潮流模式,导致电压波动、功率因数降低、网损增加等问题,无功优化成为保障配电网安全、经济、高效运行的关键技术。 1. 核心目标 电压稳定性:抑制DG并网点(PCC)及敏感节点的电压越限(如超过5%…...
游戏引擎学习第301天:使用精灵边界进行排序
回顾并为今天的内容做准备 昨天,我们解决了一些关于排序的问题,这对我们清理长期存在的Z轴排序问题很有帮助。这个问题我们一直想在开始常规游戏代码之前解决。虽然不确定是否完全解决了问题,但我们提出了一个看起来合理的排序标准。 有两点…...
网络框架二次封装:基于Kotlin的高扩展性网络请求框架完整实现
完整目录结构 1. 架构设计1.1 分层架构1.2 核心组件1.3 接口关系图2. 基础配置实现2.1 NetworkConfig完整代码2.2 CacheConfig完整代码3. 核心网络客户端3.1 SmartHttpClient完整实现3.2 单例管理3.3 服务创建与执行4. DSL请求构建器4.1 NetworkRequest完整实现4.2 生命周期绑…...
高噪声下扩展边缘检测算子对检测边缘的影响
目录 一、常见的边缘检测算子 二、扩展边缘检测算子对检测边缘的影响 三、结论 一、常见的边缘检测算子 Sobel 算子: Prewitt算子;...
Linux 内核音视频架构(V4L2 )介绍
一.概述 Linux 内核中的 V4L2(Video for Linux Two)框架 是用于管理音视频设备(如摄像头、电视调谐器、视频采集卡等)的核心子系统。 它提供了一套统一的接口,使得用户空间应用程序能够方便地访问和控制硬件设备&…...
专业 YouTube SEO 方案:打造高排名视频的关键步骤
YouTube 是全球订阅量最高的社交媒体平台之一。YouTube 为发布创意视频内容和针对特定受众开展营销活动提供了无限可能,是任何品牌内容营销策略的重要组成部分。 但是,为了发展您的 YouTube 频道并消除噪音,优化您的视频内容以便可以在搜索结…...
基于STM32的智能台灯_自动亮度_久坐提醒仿真设计(Proteus仿真+程序设计+设计报告+讲解视频)
这里写目录标题 1.主要功能2.仿真设计3.程序设计4.设计报告5.下载链接 基于STM32的智能台灯_自动亮度_久坐提醒仿真设计 (Proteus仿真程序设计设计报告讲解视频) 仿真图Proteus 8.9 程序编译器:keil 5 编程语言:C语言 设计编号࿱…...
labview硬件部分——压力测量
0kg的电压需要我们手动输入!在不放东西的时候的电压,先运行一次程序,将其记录后写到程序中的0kg输入按键即可。 整体的程序:...
Mysql索引实战1
对于上面这两种 name>‘a’ 和 name>‘zzz’ 的执行结果,mysql最终是否选择走索引或者一张表涉及多个索引,mysql最终如何选择索引,我们可以用trace工具来一查究竟,开启trace工具会影响mysql性能,所以只能临时分析…...
在实际网络部署中,静态路由的优先级通常高于RIP
是的,在实际网络部署中,静态路由的优先级通常高于RIP,尤其是在中小型网络或对可控性要求高的场景中。以下是关键原因和典型应用场景分析: 1. 为何静态路由比RIP更受青睐? (1) 简单性与可靠性 静态路由: 手…...
Linux系统编程-DAY02
一、标准io 1.写文件 fgets函数中判断有多少行,且判断最后一个是不是终止符 if( buf[strlen(buf) - 1] \n ) 2. wc命令行:字符统计 wc -l 文件名 行数 文件名 3. write 用于操作二进制的文件(文办文件和图片文件也可以…...
【C++ 真题】P5736 【深基7.例2】质数筛
P5736 【深基7.例2】质数筛 题目描述 输入 n n n 个不大于 10 5 10^5 105 的正整数。要求全部储存在数组中,去除掉不是质数的数字,依次输出剩余的质数。 输入格式 第一行输入一个正整数 n n n,表示整数个数。 第二行输入 n n n 个正…...
自制操作系统day6(GDTR、段描述符、PIC、实模式和保护模式、16位到32位切换、中断处理程序、idt的设定、EFLAG寄存器)(ai辅助整理)
day6 分割源文件(harib03a) 优点 按照处理内容进行分类,如果分得好的话,将来进行修改时,容易找到地方。如果Makefile写得好,只需要编译修改过的文件,就可以提高make的速度。单个源文件都不长。…...
大模型评测与可解释性
随着大模型在各个领域展现出惊人的能力,我们对其性能的评估和对其决策过程的理解变得尤为重要。一个模型即使在基准测试中表现出色,也可能在实际应用中遇到意想不到的问题。同时,由于大模型的复杂性,它们常常被视为“黑箱”,这给其在关键领域的应用带来了挑战。 本章将深…...
【TTS回顾】StyleTTS 深度剖析:TTS+风格迁移
写在前面 这篇博客我们回顾一下StyleTTS,当时的背景是,文本转语音(TTS)技术,早已不再满足于仅仅将文字转化为可听的语音。行业需要的是“真人TTS”,AI 不仅能“说得清楚”,更能“说得生动”、“说得有感情”,甚至能模仿特定人物的说话风格。富有表现力的语音合成,即能…...
GStreamer (四)交叉编译
交叉编译 下载链接库交叉编译1、下载Gstreamer (方式二 ),进入到编译目录2、在gst-build目录下创建交叉编译配置文件cross_file.txt3、修改meson_options.txt中libmount选项为false,否则编译前需要先编译libmount。4、在gst-build…...
电路设计基础
只有当电容两端的电压等于0伏的时候,就是这一点的电压和这一点电压之间没有压差的时候,我门才可以把电容当成是一根导线,如果当我电容比如说它己经充到有一个1伏的电压了,这个时候我们是不可以把电容当成是导线的,所以…...
C语言——函数递归与迭代
(1)递归的例子: 顺序打印一个整数,打印整数的每一位。 例如: input:1234 output:1 2 3 4 input:520 output:5 2 0 我们可能会想到用这种方法:(但是运行之后,我们发现结果是事…...
详解 C# 中基于发布-订阅模式的 Messenger 消息传递机制:Messenger.Default.Send/Register
🧑 博主简介:CSDN博客专家、CSDN平台优质创作者,高级开发工程师,数学专业,10年以上C/C, C#, Java等多种编程语言开发经验,拥有高级工程师证书;擅长C/C、C#等开发语言,熟悉Java常用开…...
8 种快速易用的Python Matplotlib数据可视化方法
你是否曾经面对一堆复杂的数据,却不知道如何让它们变得直观易懂?别慌,Python 的 Matplotlib 库是你数据可视化的最佳伙伴!它简单易用、功能强大,能将枯燥的数字变成引人入胜的图表。无论是学生、数据分析师还是程序员&…...
嵌入式开发学习日志(linux系统编程--文件读写函数(2))Day25
一、linux操作命令 【wc】:指定字符统计; 【file 文件名】:可以查看文件的类型; 二、写入函数【fwrite】————可写入二进制文件 形式: size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE…...
离线服务器Python环境配置指南
离线服务器Python环境配置指南:避坑与实战 0. 场景分析:当服务器与世隔绝时 典型困境: 无法访问国际网络(如PyPI、Conda官方源)服务器处于内网隔离环境安全策略限制在线安装 解决方案矩阵: 方法适用场…...
Java线程池调优与实践经验
在Java面试中,线程池调优是一个常见且重要的考察点,尤其是当涉及Spring生态时,ThreadPoolTaskExecutor的使用经验通常会被深入追问。以下是针对该问题的结构化回答,结合原理、实践和调优经验: 1. 线程池调优的核心参数…...
Python 包管理工具核心指令uvx解析
uvx 是 Python 包管理工具 uv 的重要组成部分,主要用于在隔离环境中快速运行 Python 命令行工具或脚本,无需永久安装工具包。以下是其核心功能和使用场景的详细解析: 一、uvx 的定位与核心功能 工具执行器的角色 uvx 是 uv tool run 的别名&a…...
力扣-三数之和
1.题目描述 2.题目链接 LCR 007. 三数之和 - 力扣(LeetCode) 3.题目代码 import java.util.*; class Solution {public List<List<Integer>> threeSum(int[] nums) {Arrays.sort(nums);int tempnums.length-1;Set<List<Integer>…...
【AI模型学习】ESM2
文章目录 1. 版本2. 开始2.1 安装2.2 使用预训练模型2.2.1 代码2.2.2 讲解 2.2 结构预测 3. 任务类型总结1. 蛋白质结构预测(ESMfold)2. 特征嵌入提取(esm-extract)3. 零镜头变体预测(ESM-1v/ESM-2)4. 逆向…...
c++11特性——可变参数模板及emplace系列接口
文章目录 可变参数模板基本语法和使用sizeof...运算符 从语法角度理解可变参数模板包扩展通过编译时递归解析参数包直接对解析行为展开 emplace系列接口举例讲解emplace_back的实现 可变参数模板 可变参数模板是c11新特性中极其重要的一节。前文我们提到过,c11中对…...
深入理解 Pre-LayerNorm :让 Transformer 训练更稳
摘要 在超深 Transformer 与大语言模型(LLM)时代,归一化策略直接决定了模型能否稳定收敛、推理性能能否最大化。把归一化层从 “残差之后” 挪到 “子层之前”(Pre-LayerNorm,Pre-LN),再将传统…...
vue3:十三、分类管理-表格--分页功能
一、实现效果 实现分页功能,并且可对分页功能和搜索框功能能动态显示 1、显示分页 2、分页功能和搜索栏隐藏 二、基础搭建 1、官网参考 Pagination 分页 | Element Plus 使用分页的附加功能 2、表格中底部写入分页 (1)样式class 在全局js中写入顶部外边距样式margin-t…...
工商总局可视化模版-Echarts的纯HTML源码
概述 基于ECharts的工商总局数据可视化HTML模版,帮助开发者快速搭建专业级工商广告数据展示平台。这款模版设计规范,功能完善,适合各类工商监管场景使用。 主要内容 本套模版采用现代化设计风格,主要包含以下核心功能模块&…...
8.2 线性变换的矩阵
一、线性变换的矩阵 本节将对每个线性变换 T T T 都指定一个矩阵 A A A. 对于一般的列向量,输入 v \boldsymbol v v 在空间 V R n \pmb{\textrm V}\pmb{\textrm R}^n VRn 中,输出 T ( v ) T(\boldsymbol v) T(v) 在空间 W R m \textrm{\pmb W}\…...
工业路由器WiFi6+5G的作用与使用指南,和普通路由器对比
工业路由器的技术优势 在现代工业环境中,网络连接的可靠性与效率直接影响生产效率和数据处理能力。WiFi 6(即802.11ax)和5G技术的结合,为工业路由器注入了强大的性能,使其成为智能制造、物联网和边缘计算的理想选择。…...
Nginx核心服务
一.正向代理 正向代理(Forward Proxy)是一种位于客户端和原始服务器之间的代理服务器,其主要作用是将客户端的请求转发给目标服务器,并将响应返回给客户端 Nginx 的 正向代理 充当客户端的“中间人”,代…...
条件随机场 (CRF) 原理及其在语义分割中的应用
条件随机场 (CRF) 原理及其在语义分割中的应用 一、条件随机场的原理 条件随机场 (Conditional Random Fields, CRF) 是一种判别式概率无向图模型。它用于在给定观测序列 (如图像中的像素) 的条件下,对另一组序列 (如像素的语义标签) 进行建模和预测。 与生成式模…...
2025年Y2大型游乐设施操作证备考练习题
Y2 大型游乐设施操作证备考练习题 单选题 1、《游乐设施安全技术监察规程(试行)》规定:对操作控制人员无法观察到游乐设施的运行情况,在可能发生危险的地方应( ),或者采取其他必要的安全措施。…...
L53.【LeetCode题解】二分法习题集2
目录 1.162. 寻找峰值 分析 代码 提交结果 2.153. 寻找旋转排序数组中的最小值 分析 图像的增长趋势可以分这样几类 逐个击破 比较明显的 先增后减再增 用二段性给出left和right的更新算法 代码 提交结果 其他做法 提交结果 3.LCR 173. 点名(同剑指offer 53:0~…...
趣味编程:抽象图(椭圆组成)
概述:本篇博客主要讲解由椭圆图案组合而成的抽象图形。 1.效果展示 该程序的实际运行是一个动态的效果,因此实际运行相较于博客图片更加灵动。 2.源码展示 // 程序名称:椭圆组合而成的抽象图案// #include <graphics.h> #include <…...
RPA浪潮来袭,职业竞争的新风口已至?
1. RPA职业定义与范畴 1.1 RPA核心概念 RPA(Robotic Process Automation,机器人流程自动化)是一种通过软件机器人模拟人类操作,实现重复性、规律性任务自动化的技术。它能够自动执行诸如数据输入、文件处理、系统操作等任务&…...
【Elasticsearch】字段别名
在 Elasticsearch 中,字段别名(Field Alias)主要用于查询和检索阶段,而不是直接用于写入数据。 为什么不能通过字段别名写入数据? 字段别名本质上是一个映射关系,它将别名指向实际的字段。Elasticsearch …...
【Linux笔记】防火墙firewall与相关实验(iptables、firewall-cmd、firewalld)
一、概念 1、防火墙firewall Linux 防火墙用于控制进出系统的网络流量,保护系统免受未授权访问。常见的防火墙工具包括 iptables、nftables、UFW 和 firewalld。 防火墙类型 包过滤防火墙:基于网络层(IP、端口、协议)过滤流量&a…...