Linux进程创建与终止:fork、exit与僵尸进程全解析
你大概也遇到过这种场景后台服务跑着跑着某个 worker 进程悄悄消失了日志里没有任何报错又或者在进程列表里一瞥看到一堆状态是 Z 的进程kill -9 都拿它们没办法。我第一次被这种问题磨到怀疑人生的时候才发现自己在 Linux 进程控制这门基本功上欠了多少债。这篇内容定位是进程控制系列的第一篇核心就是进程创建和进程终止fork 到底怎么分裂出子进程exit / _exit / return 这三条退出路径到底有什么差别僵尸进程是怎么产生又该怎么善后。适合刚学系统编程的同学也适合写业务代码但偶尔要排查进程问题的朋友。1. 进程到底是什么先别急着写 fork这些概念必须理清1.1 程序和进程不是一回事程序是放在磁盘上的可执行文件是一坨二进制指令躺在那里不会动。进程是程序的一次运行实例是操作系统真正调度、分配资源、管理状态的最小单位。打个比方菜谱是程序你照着菜谱做菜的过程才是进程同一本菜谱可以做无数次菜同一个可执行文件也可以同时启动多个进程它们各自有独立的身份标识 PID互相之间默认不共享内存。很多人理解 fork 时会卡住就是因为在脑子里把进程和程序划了等号。fork 不是去磁盘加载一个程序而是基于当前已经存在的进程复制出一个新的进程。这个细微区别贯穿整个进程创建和终止的讨论。比如你 fork 之后父子进程跑的是同一份代码但从 fork 函数返回的那一刻起它们是两个独立调度的实体各有各的 PID、各有各的寄存器现场、各有各的执行路径。1.2 内核里那个叫 task_struct 的档案袋在 Linux 内核里描述一个进程的数据结构叫 task_struct有人也把它叫做进程控制块。你可以把它想象成一个档案袋里面塞满了这个进程的全部信息PID 和 PPID、当前状态运行、睡眠、停止、僵尸等、打开的文件描述符表、内存地址空间布局、信号处理函数表、调度优先级、各维度的统计计数等等。当内核创建一个新进程时本质上是新申请一个 task_struct把各种字段填好把它挂进可运行队列等待调度器分配 CPU。这也就是为什么 fork 比 exec 加载一个全新程序要轻量fork 主要是复制一个档案袋而 exec 要做的是把整个进程的内存映像替换成磁盘上的新程序。实际调试时想验证进程到底是不是独立实体最简单的办法就是打印 PID 和 PPID。后面所有 fork 的案例都会围绕这两个标识来观察父子关系。2. fork() 为什么调用一次却返回两次双返回值与父子分支2.1 原型和返回值先把规则背下来fork 的函数原型长这样#include sys/types.h #include unistd.h pid_t fork(void);它没有参数。调用成功后最关键的现象是调用一次返回两次。返回值的含义是父进程收到子进程的 PID一个大于 0 的整数子进程收到 0创建失败时父进程收到 -1并设置 errno常见的错误是 EAGAIN 和 ENOMEM。为什么子进程返回 0因为 0 不是合法 PID可以明确表示当前这个进程是子进程。父进程返回子进程 PID是因为父进程后续要对该子进程做 wait、kill、按 PID 做管理等操作必须知道新进程的编号。而子进程不需要知道自己的 PID 吗它完全可以调用 getpid() 自己查所以 fork 返回值 0 只是一个身份的旗帜。赶紧看一个最简单的例子#include stdio.h #include unistd.h #include sys/types.h #include stdlib.h int main() { printf(before fork, pid %d\n, getpid()); pid_t ret fork(); if (ret -1) { perror(fork error); exit(1); } if (ret 0) { printf(child process: pid%d, ppid%d, ret%d\n, getpid(), getppid(), ret); } else { printf(parent process: pid%d, child pid%d, ret%d\n, getpid(), ret, ret); } sleep(1); return 0; }编译运行大致会看到类似输出before fork, pid 12345 parent process: pid12345, child pid12346, ret12346 child process: pid12346, ppid12345, ret0注意before fork只输出一次而 fork 后面的 if/else 两个分支各输出了一次。这说明什么fork 创建子进程之后父子进程都从 fork 调用点继续往下执行于是同一份代码被两个进程各跑了一遍。子进程的 ret 是 0走 if 分支父进程的 ret 是子进程 PID走 else 分支。这就是调用一次返回两次的直观表现。我把三种返回值情况整理成一个表方便你记忆返回值代表含义需要注意的点 0父进程中返回值是新子进程的 PID父进程拿着这个 PID 管理子进程0子进程中返回子进程用 getpid() 查自己的 PID-1fork 失败父进程收到必须 perror 并处理错误不能再往下走2.2 父子进程谁先执行不要赌也不要猜fork 之后父子进程都处于就绪状态谁先拿到 CPU 完全由内核调度器决定。你可以在多个终端、多次运行中观察到子进程先打印、父进程先打印的情况都可能出现。如果没有人为同步输出顺序就是随机震荡的这是正常现象不是代码写错了。调试时为了让观察稳定我习惯在父进程分支里加一个 sleep让子进程先跑完。上面例子里的 sleep(1) 就是干这个用的。但这只是让特定场景更容易看不能依赖 sleep 去保证业务逻辑顺序。父子进程之间真正需要同步时应该用管道、信号等进程间通信机制那又是另一个话题了。另外有个现象值得提前打个预防针多个进程同时向同一个终端写数据时即使 printf 带了换行符输出顺序也可能乱。因为终端本身有锁两个进程都在抢谁抢到谁先写。把输出重定向到文件时则更微妙——文件是全缓冲printf 的内容可能还在进程各自的缓冲区里最后 flush 的顺序再次被打乱。所以拿 fork 程序做实验第一选择是直接用 \n 结尾的 printf第二是尽量不要一次跑太多进程。2.3 为什么父进程返回子进程 PID、子进程返回 0设计逻辑这里再掰开揉碎讲一下内核的意图。如果父子返回同样的值比如都返回 0父进程就永远没办法精准找到子进程了。父进程后续需要 wait 子进程的退出状态需要给特定子进程发信号这都要求父进程先知道子进程的 PID。所以父进程必须拿到自己的第一个孩子的编号。子进程返回 0 则是为了区分身份。因为父进程保存了子进程的 PID它天然知道自己在父分支子进程没有任何现成渠道知道我是谁必须通过 fork 返回值才能走进子分支。所以 0 是一个牺牲了信息量、换取了流程清晰度的设计。等我们后面讲到进程等待、进程替换你会发现这个返回值设计让整个 if/else 分支代码变得异常干净一个判断就能把父子行为分开。3. 写时拷贝、文件描述符共享与那个经典的 printf 两次 问题3.1 写时拷贝为什么 Linux 敢让 fork 复制一切早期的 Unix fork 会完整复制父进程的地址空间代码、数据、堆栈全部拷贝一份代价非常大。Linux 引入了写时拷贝技术后fork 的常规路径变成了只复制页表不复制物理内存。具体过程是这样创建子进程时父子进程的虚拟地址空间映射到同一批物理内存页同时这些页被标记为只读。谁都没有真正的写权限。当父子中某一方真的试图写入某个内存页时CPU 会触发缺页异常内核在异常处理里把这一页物理内存复制一份重新映射给发起写入的进程并且把新页设为可写。不写入的页面则永远共享省了复制成本。打个比方你在公司有一个很厚的共享文档fork 相当于给子进程发了一张同一份文档的访问卡大家先合着看只有其中一个人真的要在某页上做批注时系统才把那一页单独复印一份给他。这样大部分页面都可以一直共享拷贝量大幅下降。这也是为什么在 Linux 上 fork exec 的组合拳非常主流性能上吃得消。理解写时拷贝对排查进程内存问题很有帮助。比如你 fork 之后发现父子进程某个全局变量各自修改互不影响不是因为内核复制了所有内存而是因为发生写入的页面在写入瞬间已经悄悄分家了。3.2 文件描述符不是复制一份文件而是共享同一个文件对象fork 之后子进程会继承父进程打开的所有文件描述符包括标准输入、标准输出、标准错误、socket、普通文件等。这句话很容易被误读成父子进程各自有一份文件副本。实际不是。fork 复制的是进程的文件描述符表但表里面每一项指向的内核文件对象struct file是同一个。这个文件对象里保存着一个关键字段当前文件偏移量。于是你会看到反直觉的现象父子进程用同一个文件描述符写文件会接着对方的位置继续写而不是各写各的。举个例子。父进程用 write 向文件写入了 100 字节子进程 fork 出来之后也用同一个 fd 写 100 字节这 200 字节会严丝合缝地排在文件里因为它们的文件偏移是共享的。这在某些场景下很方便但也隐藏着风险如果父子进程都往同一个日志文件写并且没有做同步日志内容可能互相穿插。写业务代码时建议明确约定fork 之后谁负责写哪个 fd或者直接 close 掉不需要的那一端。3.3 令人迷惑的 printf 两次缓冲区在作怪如果你是先写代码后学理论的那类人一定见过这种诡异现象#include stdio.h #include unistd.h int main() { printf(hello ); fork(); return 0; }直觉上你以为只会输出一次hello 结果可能输出两次。很多人第一反应是fork 连 printf 都复制了吗还真说对了但细节在缓冲区。printf 是标准库函数数据先进入用户态的 stdio 缓冲区等到合适时机才会通过 write 系统调用交给内核。stdout 默认对终端的缓冲策略是行缓冲也就是遇到换行符才刷而重定向到文件时是全缓冲缓冲区满了或者进程正常退出时才刷。上面这段代码printf 里没有换行符所以hello 一直躺在父进程的缓冲区里没真正写出去。fork 复制进程映像时把这段缓冲区也复制给了子进程。于是子进程的缓冲区里也有一份hello 。最终父进程退出、子进程退出两边各自把缓冲区冲刷到终端你就看到了两个hello 。如果代码里加一个 \nprintf(hello \n); fork();结果通常只输出一次因为换行符触发了行缓冲刷新fork 之前数据已经进入内核。这个知识点几乎就是系统编程面试里必问的送分题同时也是排查输出重复、丢失问题时最重要的排查方向之一。我建议你在写 fork 相关实验程序时凡是关键打印都带上 \n或者干脆在 fork 前主动 fflush(stdout)避免缓冲区复制带来的困惑。4. 终止进程的三条路exit、_exit、return 的差异到底有多大4.1 正常终止和异常终止进程退出可以分成两大类正常终止和异常终止。正常终止包括 main 函数 return、显式调用 exit()、显式调用 _exit()异常终止一般是进程收到了信号比如 kill -9 发送 SIGKILL或者进程自己触发段错误。正常终止时进程会记录退出状态码内核会回收进程占用的绝大部分资源比如内存、页表、文件描述符等。注意绝大部分——后面马上会讲有一部分内核数据结构并不会立刻释放除非父进程配合。异常终止时进程根本没有机会执行任何清理代码自然也不会调用常规的退出处理函数这也是为什么很多程序需要通过信号处理器来处理被kill前必须做的善后工作。4.2 exit 与 _exit 的库函数和系统调用之别很多人以为 exit 和 _exit 只是名字不同实际上它们是两个层级完全不同的东西。exit() 是 C 库函数。它做的事依次是调用 atexit / on_exit 注册的清理函数按后进先出顺序、刷新所有标准 IO 缓冲区、最后再调用 _exit 进入内核完成终止。所以你看到的很多退出前先登录日志、先释放资源的行为依赖的是 exit 的清理阶段。_exit() 是系统调用进入内核后直接终止进程。它不会刷新 stdio 缓冲区不会执行 atexit 清理函数动作非常硬。看一个能直观感受差异的程序#include stdio.h #include unistd.h #include stdlib.h void cleanup(void) { printf(cleanup called\n); } int main() { atexit(cleanup); printf(hello without newline); _exit(0); }如果无视编译警告直接运行你会发现hello without newline没打印出来cleanup 也没被调用。因为程序还没把缓冲区内容写出去就直接被 _exit 终止了。把 _exit 换成 exit两行输出都会出现。实际写代码时为什么那么多人在子进程分支里用 _exit我举一个真实场景父进程已经通过 printf 往标准输出写了一些带缓冲的数据还没有 flush然后 fork 一个子进程。子进程处理完业务后如果调用 exit(0)它会把从父进程继承来的缓冲区内容也刷一遍导致同一段数据被输出两次。此时改用 _exit(0)子进程就不会碰那份缓冲区干净利落不污染父进程的输出。当然更严谨的做法是子进程在 fork 之后第一件事就是清楚自己不需要的 fd该 close 的 close再用 _exit 退出。但头一步里_exit 在子进程分支中的使用是非常常见的实践。4.3 return 0 在 main 里到底算什么main 函数里写 return 0很多教材直接说等价于 exit(0)这个说法在大方向上没错但细节上值得补充。main 的返回值是给了 C 运行时库的启动代码当 main return 后glibc 的启动代码会把这个返回值作为参数传给 exit然后 exit 再去执行清理函数、刷新缓冲区、最终调用 _exit。所以要清楚在 main 里的 return 等价于 exit会触发 atexit 清理和缓冲区刷新。但是如果是在其他普通函数里写 return那只是返回到调用者进程根本不会终止。这一点很容易被初学者弄混以为任何函数调了 return 就是退出进程。顺带一个细节main 里如果写 return -1最终退出码并不是 -1而是 255。因为退出状态只保留低 8 位-1 的二进制补码低 8 位全是 1也就是 255。这种截断规则让很多脚本判断程序退出码时出现莫名其妙的错误下面单独说。4.4 退出码的低 8 位截断exit() 接收的参数类型是 int但你传多大都没用父进程最终能拿到的退出状态只有低 8 位合法范围是 0 到 255。0 表示成功非零表示各类错误。举个例子如果 child 里执行 exit(300)那么父进程 wait 出来后看到的退出状态其实是 44因为 300 的低 8 位是 44。如果你在 shell 脚本里判断 $?会发现结果和你想象的不一样。反过来想表达退出码为 300 根本做不到必须选 0-255 之间的值。所以我建议在项目里约定一套稳定的退出码表比如 1 表示参数错误、2 表示文件不存在、3 表示初始化失败避免随手写一个大整数。否则排查问题时要看退出码到底是多少还得先做一次模 256 的心算。5. 僵尸进程与孤儿进程进程死后并不是立刻消失5.1 僵尸进程是怎么来的前面提到进程正常终止后绝大部分资源都会被回收但有一小部分必须留着等父进程来取。这一小部分包括 task_struct 里的退出状态、PID 等关键数据。为什么不能立刻彻底销毁因为父进程需要知道我的子进程是正常退出还是被信号杀死的它的退出码是多少如果直接全部清掉父进程就永远得不到答案了。这个已经死了、但数据还留着等人认领的中间状态就是僵尸状态在 ps 里显示为 ZZombie。下面这段代码可以快速制造一个僵尸#include stdio.h #include unistd.h #include stdlib.h int main() { pid_t pid fork(); if (pid 0) { printf(child pid%d, exit now\n, getpid()); exit(0); } else { printf(parent pid%d, sleep 30s no wait\n, getpid()); sleep(30); return 0; } }运行这个程序在 30 秒内打开另一个终端执行ps -eo pid,ppid,state,cmd会看到子进程的状态是 Z。父进程睡着没调 wait子进程的尸体就一直留在进程表里。5.2 为什么 kill -9 也杀不掉僵尸我见过不少人看到僵尸进程第一反应就是 kill -9 把它干掉然后发现毫无反应以为系统坏了。原因并不复杂僵尸进程已经没有生命活动了不执行代码、不接收信号、不参与调度。kill -9 只能作用于还活着的进程对僵尸只能干瞪眼。想清理僵尸正常渠道只有两条父进程调用 wait / waitpid 读取退出状态把这个尸体领走。这是最正确、最根本的解法也是下一篇进程等待要展开讲的内容。如果父进程本身不回收那就想办法结束父进程。父进程去世后僵尸子进程会被系统中具备收养能力的进程接管由收养进程统一 wait 掉最终从进程表里消失。很多守护进程长期不 wait 子进程日积月累就会养出一堆僵尸最终进程数爆表。所以写会 fork 子进程的程序时开工前就得想清楚子进程退出后谁来收尸。5.3 孤儿进程的收养机制另外一种常见情况是父进程先退出子进程还在运行。这时候子进程不会变成僵尸而是变成孤儿进程。孤儿进程不能被留在野地里没人管Linux 会让一个专门的进程来收养它。传统 Unix 世界里收养者是 PID 为 1 的 init 进程。现代 Linux 上如果系统中某个进程通过 prctl 注册成了 subreaper那么责任就由最近的 subreaper 承担。比如使用 systemd 作为 1 号进程的系统里systemd 通常就是这个角色。收养者会周期性 wait 掉这些孤儿帮它们收尸避免孤儿也变成僵尸。孤儿进程本身没什么危害但有一点容易让人困惑孤儿进程的 PPID 会变成收养进程的 PID不再是原来的父进程。你在排查时发现某个进程的 PPID 是 1基本说明它已经是孤儿了原本的父子链路已经断了。5.4 实战中的两个常见坑坑一守护进程长期运行子任务结束后父进程从不 wait。刚开始看不出问题跑了一两周后进程表里堆满 Z系统 PID 被大量占用新任务 fork 直接返回 EAGAIN服务开始大面积失败。这时候就算你定位到是哪个父进程也很难远程清理只能重启这个守护进程。坑二用 popen 调用外部命令时虽然标准库内部会处理一部分子进程状态但如果在自己的代码里对管道读写的时机控制不好依然可能积累僵尸。结合前面讲的缓冲区问题popen 的排查往往要同时看子进程状态和管道关闭的时机。排查方法不复杂先列出所有 Z 状态的进程再看它们的 PPID 是谁顺藤摸瓜找到那个不认领孩子的父进程接着看它的代码逻辑到底是漏了 wait 还是有 bug。6. 实战排查进程状态观察与创建进程的高频坑位6.1 学会用 ps 和 /proc 观察进程排查进程问题时我常用的命令是# 带状态的进程列表 ps -eo pid,ppid,state,cmd # 只看僵尸进程 ps -eo pid,ppid,state,cmd | awk $3Z # 看单个进程的详细信息 cat /proc/PID/status/proc/ /status 里的 State 字段详细描述了进程状态。常见的有Rrunning正在运行或可运行、Ssleeping可中断睡眠、Ddisk sleep不可中断睡眠一般是等 I/O、Tstopped被暂停、Zzombie僵尸、Iidle内核空闲线程。实际工作中D 状态和 Z 状态经常引起混淆。D 状态是进程还在等内核里的 I/O 完成它本质上还活着只是暂时睡死过去了Z 状态是已经死了只是没被收尸。前者你只能等 I/O 恢复后者你要找父进程来 wait。6.2 循环 fork 的指数爆炸如果你把 fork 写在循环里又没有加任何退出条件进程数量会呈指数级增长。看这段经典坑代码for (int i 0; i 3; i) { fork(); }不加 break 的情况下最终进程总数是 2 的 3 次方也就是 8 个。因为第 1 次 fork 把进程变成 2 个第 2 次 fork 时这两个进程同时执行下一次循环各自再 fork进程数翻倍成 4 个第 3 次再翻倍到 8 个。循环写 10 次就是 1024 个进程写 20 次就是百万级。这就是 fork 炸弹的基本原理。做实验一定要在自己掌控的虚拟机或容器里搞设置好资源限制再玩。同时要注意写真实的业务代码时子进程分支里要尽快 return 或调用 exec别让子进程继续跑同一个 for 循环。很多因为 fork 导致系统卡死的案例根因就是子进程没有走独立分支把父进程的循环逻辑又跑了一遍。6.3 每个 fork 第一件事检查返回值我很认真地说这是一条保命习惯。fork 失败时的情况特别容易被忽略。在容器环境里进程数量受 cgroup 的 pids 控制器限制一旦配额耗尽fork 就会返回 -1 并设置 errno 为 EAGAIN。另一种常见失败是内存不足内核复制页表或分配 task_struct 失败返回 errno 为 ENOMEM。如果代码里只看 pid 0 就进子进程逻辑而不处理 pid -1失败时你会用父进程的身份去执行子进程那套逻辑轻则逻辑错乱重则把业务数据搞坏。标准模板应该是pid_t pid fork(); if (pid 0) { perror(fork failed); // 记录日志、退避重试或者放弃任务 return -1; } if (pid 0) { // 子进程逻辑 ... _exit(0); } // 父进程逻辑 ...6.4 完整的进程创建流程通常是 fork exec严格来讲Linux 里要创建一个全新程序的进程最常见姿势是 fork 加 exec 族函数。fork 负责复制出当前进程然后子进程调用 execve、execlp、execvp 等函数用磁盘上新程序的内容替换掉当前进程的代码段、数据段、堆栈。exec 成功之后PID 保持不变但从运行内容到内存布局已经完全变成另一个程序了。shell 里敲一个命令本质就是这个流程shell fork 出子进程子进程 exec 加载对应命令的可执行文件。这也是为什么执行外部命令时你能在 ps 里看到临时出现的 shell 子进程接着它很快被替换成真正命令的进程。理解了 fork exec再去看各种后台管理脚本产生的输出混乱、僵尸进程、资源泄漏会清晰很多。6.5 几条浓缩的实操心得写到这里把这几年的经验浓缩成几点对刚开始接触进程控制的同学尤其有用调试 fork 程序时printf 里尽量加 \n或者 fork 前主动 fflush(stdout)避免缓冲区复制让输出次数对不上。不要依赖父子进程谁先运行来编写业务逻辑需要同步就用管道、信号等机制别靠 sleep 碰运气。每次 fork 前先问自己子进程退出后父进程会 wait 吗如果答案是不会那这个程序就是一个僵尸工厂。子进程分支里退出时优先用 _exit(exit_code)避免把从父进程继承来的 stdio 缓冲区再刷一遍导致输出重复或者顺序错乱。每次 fork 后第一件事就是判断返回值小于 0 的情况别让失败分支混进子进程逻辑。进程控制这个话题讲创建和终止只是开胃菜。真正把进程玩明白后面还要啃进程等待、进程替换、进程间通信这一串内容。但如果你现在把 fork 的双返回值、exit/_exit 的区别、僵尸进程的善后机制这三块彻底想透了后面的路会顺非常多。我这几年排查线上问题翻来覆去绕不开的其实就是这几个最基础的概念希望这篇能帮你少走弯路。

相关新闻

两天搭出可运行CRM原型:Flask+SQLite实现线索到成交闭环

两天搭出可运行CRM原型:Flask+SQLite实现线索到成交闭环

简介:本资源是一套面向前端开发初学者与毕业设计学生的CRM客户关系管理系统原型页面,聚焦于交互逻辑实现与界面体验优化,适用于Web前端课程设计、毕设原型开发及JavaScript实战练习。压缩包为RAR格式,大小10MB,虽未提供…

2026/10/13 2:40:33 阅读更多 →
IDEA代码补全大小写敏感怎么关?三步配置让提示更智能

IDEA代码补全大小写敏感怎么关?三步配置让提示更智能

如果你刚从其他IDE切到JetBrains IDEA,或者刚入行不久,大概率会碰到一个特别恼火的细节:明明类名叫UserService,你敲user却怎么都补全不出来,只能老老实实敲U或者u再等那一下,大写开头才出提示。这个问题的…

2026/10/13 2:40:33 阅读更多 →
从0到1搭建同城社区生鲜电商:Python+小程序+AI抢单实战

从0到1搭建同城社区生鲜电商:Python+小程序+AI抢单实战

这个项目上线到现在半年多了,我是在一次社区团购的失败案例里找到灵感的。当时某小区的业委会在群里抱怨:楼下的菜店又贵又不新鲜,而几十公里外的蔬菜基地因为没人收购,一批批绿叶菜直接烂在地里。那一刻我突然意识到,…

2026/10/13 2:39:33 阅读更多 →

最新新闻

Rust性能优化实战:五个技巧榨干硬件性能

Rust性能优化实战:五个技巧榨干硬件性能

很多人聊 Rust 性能优化,第一反应是“这语言天生就快,还用得着调?”我最初也这么想,直到接手一个用 Rust 写的日志处理服务,在单机 8 核上处理一千万条日志要跑十几秒,调完之后压缩到两秒出头,C…

2026/10/13 16:37:10 阅读更多 →
5G智慧校园解决方案:云网端架构、专网组网与评审避坑指南

5G智慧校园解决方案:云网端架构、专网组网与评审避坑指南

简介:这份PPT围绕5G智慧校园解决方案展开,面向教育信息化管理者、智慧校园规划人员及行业方案设计师,系统梳理了教育信息化从1.0“三通两平台”迈向2.0“三全两高一大”的政策背景与现实需求,并针对教学质量提升、资源优质共享、校…

2026/10/13 16:37:10 阅读更多 →
Python声纹识别实战:EcapaTdnn/ResNetSE等四大模型对比与工业部署

Python声纹识别实战:EcapaTdnn/ResNetSE等四大模型对比与工业部署

简介:本资源是一套基于PaddlePaddle实现的声纹识别系统完整工程,面向人工智能方向的学习者、语音算法初学者及高校课程设计实践者,聚焦于说话人身份验证这一典型生物特征识别任务。项目集成EcapaTdnn、ResNetSE、ERes2Net、CAM等前沿模型&…

2026/10/13 16:37:09 阅读更多 →
Rust性能优化五技巧:数据结构、内存分配与缓存友好

Rust性能优化五技巧:数据结构、内存分配与缓存友好

Rust 的性能优化,说起来很有意思。大家选 Rust 多半是冲着性能来的,但真正把程序写出来之后,你会发现“能跑”和“跑得快”之间还有很长一段路。我早期做 Rust 项目时也走过弯路,一度以为堆一些 unsafe 代码、换更高级的算法就是优…

2026/10/13 16:37:09 阅读更多 →
开源大模型本地部署四件套:环境配置、私有部署、LoRA微调与LangChain接入

开源大模型本地部署四件套:环境配置、私有部署、LoRA微调与LangChain接入

简介:本资源是一套面向AI工程师与大模型应用开发者的开源大模型全栈实践工具包,聚焦环境配置、私有化部署、LoRA微调及LangChain集成四大核心环节,助力开发者快速落地本地化大模型应用。压缩包共79个文件,涵盖45个Python脚本&…

2026/10/13 16:37:09 阅读更多 →
实习和校招到底在选什么?从简历到面试的完整求职指南

实习和校招到底在选什么?从简历到面试的完整求职指南

1. 实习和校招到底在选什么很多同学第一次接触实习和校招的时候,脑子里冒出来的第一个问题往往是“我该投哪家”“简历怎么写”“面试问什么”。这些问题当然重要,但如果只盯着这些操作层面的东西,很容易陷入一种“做了很多准备却始终不得要领…

2026/10/13 16:36:05 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →