六十天Linux从0到项目搭建(第十天)(系统调用 vs 库函数/进程管理的建模/为什么进程管理中需要PCB?/exec 函数/fork原理与行为详解)
1 系统调用 vs 库函数:本质区别与协作关系
核心区别
| 特性 | 系统调用(System Call) | 库函数(Library Function) |
|---|---|---|
| 定义 | 操作系统内核提供的 底层接口,直接操作硬件。 | 封装系统调用的 高级函数,提供便捷功能。 |
| 权限 | 需要切换到 内核态(高权限)。 | 运行在 用户态(普通权限)。 |
| 性能 | 开销大(需切换内核态)。 | 开销小(纯用户态执行)。 |
| 稳定性 | 影响系统全局(如文件读写)。 | 仅影响当前进程。 |
| 例子 | open(), read(), fork() | printf(), fopen(), malloc() |
1. 系统调用(System Call)
特点
-
直接与内核交互:是用户程序访问硬件/内核功能的唯一合法途径。
-
通过软中断触发:如 x86 的
int 0x80或syscall指令。 -
权限提升:CPU 从用户态切换到内核态。
常见系统调用(Linux为例)
| 类别 | 示例 | 功能 |
|---|---|---|
| 文件操作 | open(), read() | 打开/读取文件 |
| 进程控制 | fork(), exec() | 创建进程/执行程序 |
| 内存管理 | brk(), mmap() | 分配内存 |
| 网络通信 | socket(), send() | 建立网络连接/发送数据 |
代码示例
#include <unistd.h>
int main() {// 直接调用系统调用 write(1=stdout, "Hello", 5)syscall(1, 1, "Hello", 5); // Linux x86_64 的 write 系统调用号=1return 0;
}
2. 库函数(Library Function)
特点
-
封装系统调用:提供更易用的接口(如
printf封装write)。 -
纯用户态执行:不直接触发权限切换,效率更高。
-
可能不依赖系统调用:如
strcpy()纯内存操作,无需内核介入。
常见库函数(C标准库为例)
| 类别 | 示例 | 底层依赖的系统调用 |
|---|---|---|
| 文件操作 | fopen(), fread() | open(), read() |
| 内存管理 | malloc(), free() | brk(), mmap() |
| 字符串处理 | strcpy(), strlen() | 无(纯用户态) |
| 格式化输出 | printf() | write() |
代码示例
#include <stdio.h>
int main() {printf("Hello"); // 库函数,内部调用 write() 系统调用return 0;
}
3. 协作关系
从 printf 看层级调用
用户程序调用 printf("Hello") ↓
C标准库处理格式化字符串(用户态) ↓
调用 write(1, "Hello", 5) 系统调用 ↓
CPU 切换至内核态,执行内核的 write() 代码 ↓
内核通过磁盘驱动写入硬件(如终端显示器)
关键点
-
库函数是“包装纸”:隐藏系统调用的复杂性(如
printf处理多种数据类型)。 -
系统调用是“终极手段”:只有需要硬件/内核资源时才触发。
-
部分库函数无需系统调用:如数学计算
sin()、字符串操作memcpy()。
为什么需要两层设计?
| 需求 | 系统调用的限制 | 库函数的优势 |
|---|---|---|
| 安全性 | 频繁切换内核态导致性能下降。 | 用户态执行,减少开销。 |
| 易用性 | 原始接口复杂(如手动管理文件描述符)。 | 提供高级抽象(如 FILE* 流)。 |
| 可移植性 | 不同OS系统调用差异大。 | 库函数屏蔽底层差异(如Windows/Linux的 fopen 实现不同,但接口一致)。 |
现实类比
-
系统调用:像直接找银行行长办业务(严格但低效)。
-
库函数:像通过柜台职员办业务(友好且高效,职员内部再找行长)。
总结
-
系统调用:内核提供的“终极接口”,权限高、开销大。
-
库函数:封装系统调用的“工具集”,更安全、易用。
-
协作模式:库函数处理复杂逻辑,必要时委托系统调用访问硬件。
记住:
-
当你调用
printf(),你是在用库函数; -
当
printf()调用write(),它是在用系统调用!
2 进程管理的建模:从程序到进程的转换
核心概念
1. 程序 vs 进程
| 程序(Program) | 进程(Process) |
|---|---|
存储在磁盘上的静态二进制文件(如 /bin/ls)。 | 程序被加载到内存后的 动态执行实例。 |
| 文件 = 内容 + 属性(如权限、大小)。 | 进程 = 代码 + 数据 + 内核数据结构。 |
2. 操作系统的作用
-
将程序转换为进程:
当你运行./a.out时,OS 负责:-
从磁盘读取可执行文件。
-
分配内存存放代码和数据。
-
创建管理进程的内核数据结构(如
task_struct)。
-
进程管理的建模过程
1. 磁盘中的程序(静态)
-
本质:一个普通文件,包含:
-
代码段(Text Segment):机器指令(如
main()函数)。 -
数据段(Data Segment):全局变量、静态变量。
-
文件属性:权限、所有者、大小等(通过
ls -l查看)。
-
2. 加载到内存(动态进程)
-
OS 的步骤:
-
读取文件内容:将代码和数据加载到内存的特定区域。
-
创建进程控制块(PCB):
-
Linux 中为
task_struct,存储进程的所有属性。 -
包括:进程ID(PID)、状态、优先级、内存映射、打开的文件等。
-
-
初始化执行上下文:
-
设置程序计数器(PC)指向
main()的入口地址。 -
分配栈空间(用于局部变量和函数调用)。
-
-
3. 进程的组成
// 进程 = 内核数据结构 + 代码/数据
struct task_struct { // PCB(进程控制块)int pid; // 进程IDchar state; // 运行状态(就绪、阻塞等)struct mm_struct *mm; // 内存管理信息struct file *files; // 打开的文件列表// ... 其他字段(优先级、父进程等)
};// 代码和数据(用户空间)
.text: 机器指令(如 main())
.data: 全局变量
.heap: 动态分配的内存(malloc)
.stack: 函数调用栈
关键问题解答
1. 什么是“进程”?
-
狭义:
task_struct(内核数据结构) + 代码/数据(内存中的内容)。 -
广义:程序的一次动态执行过程,包括:
-
执行状态(运行、就绪、阻塞)。
-
资源占用(CPU、内存、打开的文件)。
-
2. 为什么需要PCB?
-
统一管理:OS 通过
task_struct跟踪所有进程,实现:-
调度:决定哪个进程获得CPU。
-
隔离:防止进程A篡改进程B的内存。
-
资源统计:记录CPU使用时间、内存占用等。
-
3. 从程序到进程的完整流程
磁盘上的 a.out → OS 读取文件头(ELF格式) → 分配内存空间(代码/数据/堆栈) → 创建 task_struct → 加入调度队列 → CPU 执行指令
现实类比
-
程序:像一本食谱(静态文本)。
-
进程:像厨师按照食谱做菜(动态过程),需要:
-
工作台(内存):存放食材和工具。
-
任务清单(PCB):记录做到哪一步、用了哪些资源。
-
-
OS:像餐厅经理,分配厨师(CPU)和厨房资源(内存)。
总结
-
程序是“尸体”,进程是“生命”:
-
程序是磁盘上的文件,进程是内存中的鲜活实例。
-
-
PCB是进程的“身份证”:
-
task_struct让OS能管理进程的所有状态。
-
-
OS是“幕后导演”:
-
默默完成程序→进程的转换,并调度资源。
-
3 为什么进程管理中需要PCB?——从Bash到子进程的完整链条
1. 为什么需要PCB?
PCB(进程控制块,如 task_struct)是操作系统的“进程管理中心”,核心作用如下:
| 功能 | 具体实现 | 类比 |
|---|---|---|
| 唯一标识进程 | 通过 pid(进程ID)区分不同进程。 | 像学生的学号,避免混淆。 |
| 保存进程状态 | 记录运行/就绪/阻塞状态,供调度器决策。 | 像任务清单上的“已完成/待处理”标记。 |
| 管理资源 | 跟踪内存分配、打开的文件、网络连接等。 | 像仓库的库存表。 |
| 实现进程隔离 | 每个进程有独立的地址空间(通过 mm_struct 管理)。 | 像银行客户的独立保险箱。 |
| 支持父子关系 | 记录父进程 ppid,实现进程树(如 bash 是所有命令行进程的父进程)。 | 像家族族谱。 |
没有PCB的后果:
-
进程无法被调度(OS不知道谁该运行)。
-
内存泄漏(无法释放已终止进程的资源)。
-
安全漏洞(进程可随意篡改他人内存)。
2. Bash与子进程的关系
(1) Bash本身是一个进程
-
Bash的PCB:
-
当你在终端输入命令时,Bash(
/bin/bash)已作为进程运行,其PCB由OS维护。 -
可通过
ps查看:ps -ef | grep bash
输出示例:
ubuntu 1234 5678 0 10:00 pts/0 00:00:00 /bin/bash
-
(2) Bash如何创建子进程?
当你在Bash中运行 ./a.out 时:
-
Bash调用
fork():-
复制当前Bash进程的PCB(生成一个子进程,继承Bash的环境变量、文件描述符等)。
-
此时父子进程的代码执行位置完全相同(都停在
fork()返回处)。
-
-
通过返回值分流:
-
子进程的
fork()返回 0。 -
父进程(Bash)的
fork()返回 子进程的PID。 -
代码示例:
pid_t pid = fork(); if (pid == 0) { // 子进程执行流execvp("a.out", args); // 加载a.out替换当前进程 } else { // 父进程(Bash)执行流wait(NULL); // 等待子进程结束 }
-
-
子进程运行目标程序:
-
子进程调用
exec()系列函数,将自身替换为a.out的代码和数据。 -
关键点:
-
exec()会替换代码段,但 保留原PCB(如pid、打开的文件描述符)。
-
-
3. 子进程创建的核心机制
(1) fork() 的特性
| 特性 | 说明 |
|---|---|
| 写时复制(COW) | 父子进程共享内存,直到一方尝试修改时才会复制(节省资源)。 |
| 执行顺序不确定 | 由调度器决定父子进程谁先运行(可通过同步机制控制,如 wait())。 |
| 共享打开的文件 | 子进程继承父进程的文件描述符(如终端输入/输出)。 |
(2) 代码分流的关键
fork(); // 执行后,从这里分裂出两个执行流// 父子进程都会执行以下代码
if (pid == 0) {// 子进程专属代码
} else {// 父进程专属代码
}
现实类比
-
Bash:像餐厅经理(有自己的工作任务表-PCB)。
-
fork():经理复制一份自己的任务表,交给新员工(子进程)。 -
exec():新员工扔掉复制的任务表,换成具体的菜谱(a.out)。 -
wait():经理等待员工做完菜再继续自己的工作。
总结
-
PCB是进程的“大脑”:
-
没有它,OS无法管理进程的生死、资源、状态。
-
-
Bash是所有命令行进程的父进程:
-
通过
fork()+exec()启动子进程,并通过PCB维护父子关系。
-
-
fork()的魔法:-
复制PCB → 分流执行流 → 通过
exec()加载新程序。
-
4 exec 函数家族详解:替换当前进程的“灵魂”
exec 是操作系统提供的一组系统调用,用于 将当前进程的代码和数据替换为一个新程序,但保留原有进程的PID、文件描述符等属性。可以理解为“进程的灵魂置换术”。
核心功能
-
不创建新进程:仅在当前进程内加载新程序(与
fork()不同)。 -
完全替换:原程序的代码、数据、堆栈被新程序覆盖。
-
继承环境:保留原进程的PID、打开的文件描述符、信号处理等。
exec 函数家族(6个变体)
均定义在 <unistd.h> 中,根据参数传递方式不同分为:
| 函数原型 | 参数传递方式 | 搜索路径 | 适用场景 |
|---|---|---|---|
int execl(const char *path, const char *arg0, ..., NULL) | 列表传参(可变参数) | 需完整路径 | 参数固定且较少时 |
int execle(const char *path, const char *arg0, ..., NULL, char *const envp[]) | 列表传参 + 自定义环境变量 | 需完整路径 | 需指定环境变量 |
int execlp(const char *file, const char *arg0, ..., NULL) | 列表传参 | 自动搜索 PATH | 调用系统命令(如 ls) |
int execv(const char *path, char *const argv[]) | 数组传参 | 需完整路径 | 参数动态生成时 |
int execvp(const char *file, char *const argv[]) | 数组传参 | 自动搜索 PATH | 最常用(灵活+自动寻路) |
int execvpe(const char *file, char *const argv[], char *const envp[]) | 数组传参 + 自定义环境变量 | 自动搜索 PATH | 需自定义环境变量且自动寻路 |
使用示例
1. 基本用法(execlp 调用 ls)
#include <unistd.h>
#include <stdio.h>int main() {printf("Before exec\n");// 执行 ls -l /, 自动搜索PATHexeclp("ls", "ls", "-l", "/", NULL); // 参数列表必须以NULL结尾printf("This line won't be reached!\n"); // exec成功时不会返回return 0;
}
输出:
Before exec total 16 drwxr-xr-x 2 root root 4096 Jan 1 1970 bin ...
2. 动态参数(execvp 调用自定义命令)
#include <unistd.h>int main() {char *args[] = {"echo", "Hello, exec!", NULL}; // 参数数组execvp("echo", args); // 自动搜索PATH中的echoreturn 0;
}
输出:
Hello, exec!
3. 配合 fork() 创建子进程
#include <unistd.h>
#include <sys/wait.h>int main() {pid_t pid = fork();if (pid == 0) { // 子进程execlp("sleep", "sleep", "5", NULL); // 子进程替换为sleep 5} else { // 父进程wait(NULL); // 等待子进程结束printf("Child finished sleeping.\n");}return 0;
}
关键注意事项
-
成功时无返回值:
exec调用成功后,原进程的代码已被替换,后续代码不会执行。 -
失败时返回 -1:需检查错误(如文件不存在、无权限):
c
复制
if (execvp("nonexistent", args) == -1) {perror("execvp failed"); } -
参数列表必须以
NULL结尾:否则会导致未定义行为。 -
环境变量继承:默认继承父进程的环境变量,可用
execle或execvpe自定义。
现实类比
-
fork():克隆一个完全相同的你(复制PCB)。 -
exec():把你的大脑换成爱因斯坦的(保留身体和身份证,但思想和能力全变)。
总结
-
何时用:需要运行另一个程序,但不想创建新进程时(如Shell执行命令)。
-
怎么选:
-
参数固定 →
execlp。 -
参数动态 →
execvp。 -
需自定义环境 →
execvpe。
-
-
经典组合:
fork()+exec()+wait()实现进程创建与程序加载。
5. fork() 的原理与行为详解
1. fork() 的核心功能
fork() 是Linux/Unix系统的一个系统调用,用于 创建一个与当前进程几乎完全相同的子进程。其核心行为包括:
-
复制父进程的PCB(
task_struct):子进程继承父进程的进程属性(如文件描述符、信号处理等)。 -
复制内存空间:代码段、数据段、堆栈等(实际采用 写时拷贝 优化,见下文)。
-
分配新的PID:子进程获得唯一的进程ID。
2. 代码与数据的处理
-
代码段(只读)
父子进程 共享同一份代码(因为代码不可修改,无需复制)。 -
数据段(写时拷贝, Copy-On-Write, COW)
-
初始时,父子进程 共享同一份物理内存(仅标记为“只读”)。
-
当任一进程尝试 修改数据(如全局变量),操作系统会触发缺页异常,自动复制该内存页给修改方,实现隔离。
-
优势:避免无意义的拷贝,提升性能。
-
3. 进程的独立性
-
内存隔离:父子进程的修改互不影响(得益于COW)。
-
调度独立:由操作系统决定父子进程的执行顺序。
4. 两个返回值的奥秘
fork() 的“一次调用,两次返回”是理解的关键:
pid_t pid = fork(); // 从这里分裂出两个执行流
-
父进程:
fork()返回 子进程的PID(>0)。 -
子进程:
fork()返回 0。 -
失败时:返回 -1(如进程数超限)。
底层原理:
-
fork()执行完成后,内核已将父进程的上下文(寄存器、PC等)复制到子进程。 -
在 返回前的瞬间,父子进程的指令指针(PC)均指向
fork()的下一条指令。 -
返回值差异:内核通过检查进程的PCB(如
task_struct)决定返回哪个值。
5. 代码示例与分流
#include <unistd.h>
#include <stdio.h>int main() {int x = 100; // 父子进程初始共享xpid_t pid = fork();if (pid == -1) {perror("fork failed");} else if (pid == 0) { // 子进程x = 200; // 触发写时拷贝,子进程的x独立printf("Child: x=%d\n", x); // 输出 200} else { // 父进程printf("Parent: x=%d\n", x); // 输出 100}return 0;
}
6. 关键结论
-
fork()的本质:复制PCB + 虚拟内存映射(COW优化)。 -
返回值设计:通过区分父子进程的PCB实现逻辑分流。
-
独立性保障:写时拷贝确保进程间数据修改互不干扰。
类比:
-
fork()像细胞分裂,子细胞继承母细胞的“记忆”(PCB),但后续行为可独立发展。 -
返回值像“出生证明”,子进程拿到“0”(新生儿标识),父进程拿到“出生登记号”(PID)。
pc均指向fork,pc是什么
PC(Program Counter)是什么?
PC(程序计数器,Program Counter)是CPU中的一个 特殊寄存器,用于存储 当前正在执行的指令的内存地址。在x86架构中,它通常被称为 EIP(32位)或 RIP(64位),而在ARM架构中则直接称为 PC。
PC 的核心作用
-
指向下一条要执行的指令
-
CPU 根据
PC的值从内存中读取指令并执行。 -
每执行完一条指令,
PC会自动递增(或跳转,如分支、函数调用)。
-
-
在
fork()时的关键行为-
当调用
fork()时,父子进程的PC都指向fork()的下一条指令。 -
这是
fork()能“分裂”出两个执行流的根本原因。
-
fork() 与 PC 的关系
1. fork() 的执行流程
pid_t pid = fork(); // 执行到这里时,CPU的PC指向这一行
-
fork()执行前:-
PC指向fork()的调用指令(如call fork)。
-
-
fork()执行后:-
父进程:
PC指向fork()的下一条指令(即if (pid == 0))。 -
子进程:
PC同样 指向fork()的下一条指令(因为子进程完全复制了父进程的上下文,包括PC)。
-
2. 为什么 fork() 有两个返回值?
-
内核的魔法:
-
fork()在 内核态 完成进程复制后,会 手动修改父子进程的返回值:-
父进程的返回值:子进程的
pid。 -
子进程的返回值:
0。
-
-
-
PC的作用:-
父子进程的
PC相同,因此都会从fork()的下一条指令继续执行。 -
但返回值不同,导致代码分流(通过
if (pid == 0)判断)。
-
示例代码分析
#include <unistd.h>
#include <stdio.h>int main() {printf("Before fork\n");pid_t pid = fork(); // PC 指向这一行// fork() 返回后,PC 指向下一行(父子进程相同)if (pid == 0) {printf("Child: PID=%d\n", getpid());} else {printf("Parent: Child's PID=%d\n", pid);}return 0;
}
执行流程:
-
父进程:
-
fork()返回子进程的pid,进入else分支。
-
-
子进程:
-
fork()返回0,进入if分支。
-
-
关键点:
-
父子进程的
PC在fork()返回后指向同一位置,但返回值不同导致逻辑分流。
-
现实类比
-
PC像书签:标记你当前读到书的哪一页。 -
fork()像复印书:-
复印后,你和复印本的书签都停在原书的同一页。
-
但你可以选择继续读(父进程),或让复印本自己读(子进程)。
-
总结
-
PC是指令指针:决定CPU下一步执行哪条指令。 -
fork()复制PC:父子进程从同一位置继续执行,但返回值不同。 -
分流的关键:通过
if (pid == 0)判断当前是父进程还是子进程。
相关文章:
六十天Linux从0到项目搭建(第十天)(系统调用 vs 库函数/进程管理的建模/为什么进程管理中需要PCB?/exec 函数/fork原理与行为详解)
1 系统调用 vs 库函数:本质区别与协作关系 核心区别 特性系统调用(System Call)库函数(Library Function)定义操作系统内核提供的 底层接口,直接操作硬件。封装系统调用的 高级函数,提供便捷功…...
资本运营:基于Python实现的资本运作模拟
基于Python实现的一个简单的资本运营框架; 企业生命周期演示:观察初创→成长→上市→并购全流程 行业对比分析:不同行业的财务特征和估值差异 资本运作策略:体验IPO定价、投资决策、并购整合等操作 市场动态观察ÿ…...
当EFISH-SBC-RK3576遇上区块链:物联网安全与可信数据网络
在工业物联网场景中,设备身份伪造与数据篡改是核心安全隐患。EFISH-SBC-RK3576 通过 硬件安全模块 区块链链上验证,实现设备身份可信锚定与数据全生命周期加密,安全性能提升10倍以上。 1. 安全架构:从芯片到链的端到端防…...
关于spark在yarn上运行时候内存的介绍
在YARN上运行Spark时,内存管理是性能调优的核心环节。以下是 Driver Memory、Executor Memory、堆内存(Heap Memory) 和 堆外内存(Off-Heap Memory) 的区别与配置方法,以及实际场景中的最佳实践:…...
分布式系统面试总结:3、分布式锁(和本地锁的区别、特点、常见实现方案)
仅供自学回顾使用,请支持javaGuide原版书籍。 本篇文章涉及到的分布式锁,在本人其他文章中也有涉及。 《JUC:三、两阶段终止模式、死锁的jconsole检测、乐观锁(版本号机制CAS实现)悲观锁》:https://blog.…...
【VSCode的安装与配置】
目录: 一:下载 VSCode二:安装 VSCode三:配置 VSCode 一:下载 VSCode 下载地址:https://code.visualstudio.com/download 下载完成之后,在对应的下载目录中可以看到安装程序。 二:安装…...
ElasticSearch常用优化点
关闭交换分区:因为Linux采用了三级页表虚存管理,关闭交换分区可以减少系统IO,页面换入唤出时所耗费的总线时间以及减少系统中断次数;swap的使用会显著增加延迟和降低吞吐量。文件描述符配置:任何网络应用都需要增加文件…...
脱围机制-react18废除forwardRef->react19直接使用ref的理解
采用ref,可以在父组件调用到子组件的功能 第一步:在父组件声明ref并传递ref interface SideOptsHandle {refreshData: () > Promise<void> }const sideOptsRef useRef<SideOptsHandle>(null) // 创建 ref<SideOpts ref{sideOptsRef…...
Spark2 之 Expression/Functions
ExpressionConverter src/main/scala/org/apache/gluten/expression/ExpressionConverter.scala TopNTransformer src/main/scala/org/apache/gluten/execution/TopNTransformer.scala...
Windows中安装git工具
下载好git安装包 点击next 选择安装目录 根据需要去勾选 点击next 点击next PATH环境选择第二个【Git...software】即可,再点击【Next】。 第一种配置是“仅从Git Bash使用Git”。这是最安全的选择,因为您的PATH根本不会被修改。您只能使用 Git Bash 的…...
【CSS】CSS 使用全教程
CSS 使用全教程 介绍 CSS(层叠样式表,Cascading Style Sheets)是一种样式表语言,用于描述 HTML 或 XML 文档的布局和外观,它允许开发者将文档的内容结构与样式表现分离,通过定义一系列的样式规则来控制网页…...
《HarmonyOS Next自定义TabBar页签凸起和凹陷案例与代码》
引言 自定义TabBar在HarmonyOS Next应用中很常见,本文将介绍如何实现页签的凸起和凹陷效果,并通过代码示例展示实现过程。 实现思路 基于已有的自定义TabBar思路,通过调整布局和样式实现凸起和凹陷效果。凸起效果可以通过在选中的页签下方…...
全分辨率免ROOT懒人精灵-自动化编程思维-设计思路-实战训练
全分辨率免ROOT懒人精灵-自动化编程思维-设计思路-实战训练 1.2025新版懒人精灵-实战红果搜索关键词刷视频:https://www.bilibili.com/video/BV1eK9kY7EWV 2.懒人精灵-全分辨率节点识别(红果看广告领金币小实战):https://www.bili…...
如何在IDEA中借助深度思考模型 QwQ 提高编码效率?
通义灵码上新模型选择功能,不仅引入了 DeepSeek 满血版 V3 和 R1 这两大 “新星”,Qwen2.5-Max 和 QWQ 也强势登场,正式加入通义灵码的 “豪华阵容”。开发者只需在通义灵码智能问答窗口的输入框中,单击模型选择的下拉菜单&#x…...
C++11QT复习 (四)
Day6-1 输入输出流运算符重载(2025.03.25) 1. 拷贝构造函数的调用时机 2. 友元2.1 友元函数 3. 输入输出流运算符重载3.1 关键知识点3.2 代码3.3 关键问题3.4 完整代码 4. 下标访问运算符 operator[]4.1 关键知识点4.2 代码 5. 函数调用运算符 operator…...
LVS的 NAT 模式实验
文章目录 目录 文章目录 概要 IP规划与题目分析 实验步骤 一、nginx配置(rs1、rs2、rs3) 二、LVS配置 三、客户端配置 四、防火墙和selinux配置 实验结果 痛点解答 概要 LVS/NAT lvs/nat网络地址转换模式,进站/出站的数据流量经过分发器(IP负…...
【MacOS】2025年硬核方法清理MacOS中的可清除空间(Purgeable space)
背景 MacOS使用一段时间之后,硬盘空间会越来越少,但自己的文件没有存储那么多,在储存空间中可以发现可用空间明明还剩很多,但磁盘工具却显示已满,见下图。 尝试解决 df -h 命令却发现磁盘已经被快被占满。使用du命…...
ue材质学习感想总结笔记
2025 - 3 - 27 1.1 加法 对TexCoord上的每一个像素加上一个值,如果加上0.1,0.1, 那么左上角原来0,0的位置变成了0.1,0.1 右上角就变成了1.1,1.1,那么原来0,0的位置就去到了左上角左上边,所以图像往左上偏移。 总而言…...
Go 语言 sync 包使用教程
Go 语言 sync 包使用教程 Go 语言的 sync 包提供了基本的同步原语,用于在并发编程中协调 goroutine 之间的操作。 1. 互斥锁 (Mutex) 互斥锁用于保护共享资源,确保同一时间只有一个 goroutine 可以访问。 特点: 最基本的同步原语&#x…...
约束文件SDC常用命令
约束文件SDC常用命令 定义时钟create_clock -name CLK-period 2 [get_ports_clk]告诉工具主时钟周期是2ns(频率500MHz),从clk端口输入 输入信号延迟set_input_delay 0.5 -clock CLK [get_ports data_in]数据进芯片前,外部电路已消耗0.5ns,综合要预留这段“堵车时间”。 输出…...
信而泰PFC/ECN流量测试方案:打造智能无损网络的关键利器
导语: AI算力爆发的背后,如何保障网络“零丢包”? 在当今数据中心网络中,随着AI、高性能计算(HPC)和分布式存储等应用的飞速发展,网络的无损传输能力变得至关重要。PFC(基于优先级的…...
golang不使用锁的情况下,对slice执行并发写操作,是否会有并发问题呢?
背景 并发问题最简单的解决方案加个锁,但是,加锁就会有资源争用,提高并发能力其中的一个优化方向就是减少锁的使用。 我在之前的这篇文章《开启多个协程,并行对struct中的每个元素操作,是否会引起并发问题?》中讨论过多协程场景下struct的并发问题。 Go语言中的slice在…...
Android 底部EditView输入时悬浮到软键盘上方
1. 修改 Activity 的 Manifest 配置 确保你的 Activity 在 AndroidManifest.xml 中有以下配置: <activityandroid:name".YourActivity"android:windowSoftInputMode"adjustResize|stateHidden" /> 关键点: adjustResize 是…...
CNN和LSTM的计算复杂度分析
前言:今天做边缘计算的时候,在评估模型性能的时候发现NPU计算的大部分时间都花在了LSTM上,使用的是Bi-LSTM(耗时占比98%),CNN耗时很短,不禁会思考为什么LSTM会花费这么久时间。 首先声明一下实…...
UniApp 表单校验两种方式对比:命令式与声明式
目录 前言1. 实战2. Demo 前言 🤟 找工作,来万码优才:👉 #小程序://万码优才/r6rqmzDaXpYkJZF 以下主要针对Demo讲解,从实战中的体会 何为命令式 何为声明式 命令式的体验,随时都会有提交的按钮ÿ…...
【树莓派Pico FreeRTOS】-Mutex(互斥体)
Mutex(互斥体) 文章目录 Mutex(互斥体)1、硬件准备2、软件准备3、FreeRTOS的Mutex介绍4、完整示例RP2040 由 Raspberry Pi 设计,具有双核 Arm Cortex-M0+ 处理器和 264KB 内部 RAM,并支持高达 16MB 的片外闪存。 广泛的灵活 I/O 选项包括 I2C、SPI 和独特的可编程 I/O (P…...
LCR 187. 破冰游戏(python3解法)
难度:简单 社团共有 num 位成员参与破冰游戏,编号为 0 ~ num-1。成员们按照编号顺序围绕圆桌而坐。社长抽取一个数字 target,从 0 号成员起开始计数,排在第 target 位的成员离开圆桌,且成员离开后从下一个成员开始计数…...
【漏洞修复】为了修复ARM64 Android10系统的第三方库漏洞,将ARM64 Android16的系统库直接拷贝到Android10系统如何?
直接替换系统库的风险分析 将高版本Android(如Android 16)的系统库直接拷贝到低版本系统(如Android 10)可能会导致以下问题: 符号与依赖不兼容 高版本库可能依赖更高版本的NDK或Bionic libc(Android的C库&…...
Flutter环境配置
配置环境变量 PUB_HOSTED_URLhttps://pub.flutter-io.cnFLUTTER_STORAGE_BASE_URLhttps://storage.flutter-io.cn 这个命令是用来配置 Flutter 的镜像源地址,主要是为了解决在中国大陆地区访问 Flutter 官方资源较慢的问题。 具体的操作如下: 右键点…...
centOS 7.9 65bit 修复Openssh漏洞
一、背景: 在使用centos 7.9 64bit版本操作系统时有扫描出如下的漏洞: 二、修复openssh漏洞操作 升级注意事项 (一下所有的操作默认都是root或者管理员权限,如果遇到权限问题每个指令以及指令组合都要在前面加sudo) 1、查看CentOS操作系统信…...
