Linux进程详解:从PCB、fork/exec到生命周期与僵尸进程管理
1. 进程概念从“程序”到“进程”的关键一跃1.1 程序与进程的本质区别很多刚开始接触Linux的朋友都会有这样一个困惑明明程序就在那儿为什么还要单独弄一个“进程”的概念这两者到底是什么关系我打个比方程序就像一本菜谱安安静静地躺在书架上内容是固定的、静态的它是一个文件有明确的路径和大小。而进程则是你照着菜谱实际开火炒菜的过程——锅热了、油下了、菜入锅了这一整套正在发生的动作才是进程。同一本菜谱你可以在不同的灶台上同时做两份一模一样的菜这就对应了同一个程序可以被多次执行产生多个进程。从系统层面看程序是磁盘上的一个静态文件它由代码段、数据段等组成不占据CPU和内存的运行资源加载前的状态。而进程是程序的一次执行过程是操作系统进行资源分配和调度的基本单位。每个进程都拥有独立的地址空间、文件句柄、环境变量等运行时资源。其中那个“正在发生”的动态属性是由一个叫做PID的数字唯一标识的。你在Shell里运行一个命令比如lsShell会帮你启动一个进程来完成这个操作这个进程瞬间运行完就退出了。但你打开一个交互式Shell或者启动一个常驻服务比如nginx或者某个自写的Python服务那个进程就会一直存在直到你主动终止它或它自己崩溃退出。1.2 PCB——进程在系统里的“身份证档案”如果进程是一个活生生的实体那么操作系统是如何追踪和管理每一个进程的你可能会想系统内核那么庞大它怎么知道该给谁分配CPU谁在等待IO谁的内存空间在哪里答案就是PCBProcess Control Block进程控制块。这是内核中数据结构的核心你可以理解成公安系统里每个人的户籍档案。每一个进程从诞生的那一刻起内核就为它开辟一块内存区域记录这个进程的全部身份信息。一个典型的PCB里包含以下关键字段进程标识符PID每个进程在系统内的唯一编号同一个程序启动多次得到的PID一定不同。父进程标识符PPID记录是谁创建了我。这个字段在进程管理时特别有用后面讲进程树会用到。进程状态记录当前进程处于就绪、运行、阻塞、僵尸等哪个状态这个状态决定它是否会被调度器选中执行。程序计数器记录下一条要执行的指令地址这样进程从CPU上被切换下来再切回去时还能知道从哪接着跑不会“断片”。内存指针指向进程自己的地址空间包括代码段、数据段、堆栈等各进程之间通过这种方式隔离。上下文数据进程上下文即CPU寄存器的值当进程被切换时这些寄存器的内容要被保存下来下次恢复时再装回去。这一套保存和恢复的动作就叫上下文切换。IO状态信息记录了进程打开的文件描述符列表、正在等待的设备等。这也是为什么进程能知道自己访问了哪些文件、哪些管道。记账信息CPU占用时间、运行时长等统计字段top命令显示的CPU时间基本都来源于此。所以在Linux内核里所有的进程都通过一个双向链表把各自的PCB串在一起管理和遍历。当你敲出ps的时候表面上是命令在输出实际上是内核把链表中每个PCB的关键字段读出来给你看。1.3 进程的地址空间布局关于进程的内存还有一个重要的认知一个进程的地址空间并不是一坨平坦无奇的内存。在Linux的虚拟内存机制下每个进程都认为自己独占整个内存空间而这个空间从低地址到高地址是规范排列的。标准布局从低地址到高地址大概是代码段、已初始化数据段、未初始化数据段BSS、堆向上增长、共享库映射区、栈向下增长、内核空间。看起来这是理论课知识但其实对排查问题非常有用。比如你频繁地在程序里new对象不释放堆空间就会一直上涨。如果写递归没有终止条件栈空间会被耗尽发生段错误。理解了进程的地址空间分布你看到Segmentation fault的时候就不会两眼一抹黑至少能判断方向是踩了野指针、栈溢出还是越界访问了。2. 进程生命周期从创建到消亡的四态循环2.1 进程的状态模型与转换Linux的进程不只是一个“在运行”或“不在运行”的二元状态。用ps命令看进程状态列会看到R、S、D、Z、T等字母。这些其实对应了进程生命周期中的几种状态RRunning/Ready进程正在运行或者排在可运行队列中等待被调度。注意这个状态也包含了“就绪”也就是说进程随时准备运行只是CPU还没轮到他。SSleeping可中断睡眠。进程在等待某个事件发生比如等IO完成、等定时器到期。这种睡眠可以被信号唤醒。DUninterruptible Sleep不可中断睡眠通常发生在等待磁盘IO等场景。这种状态下进程不响应外部信号连kill -9都杀不掉。如果大量D状态进程堆积多半是存储设备出了问题。TStopped/Traced停止状态进程被暂停执行。常见于调试器打断点的时候这时进程就进入这个状态。也可以通过发送SIGSTOP信号让进程暂停。ZZombie僵尸状态。这是新手最容易困惑的状态我先卖个关子稍后专门展开。进程状态之间不是任意跳转的比如一个进程不可能从停止状态直接跳到僵尸状态它必须先经过退出清理流程。理解这些状态转换在排查系统负载问题时是基础技能。举个例子某次线上应用响应缓慢top里看到大量进程处于D状态而磁盘IO的%util已经接近100%基本可以判定是磁盘性能瓶颈导致进程阻塞在不可中断睡眠而不是应用本身死循环了。2.2 fork进程创建的根本机制在Linux中产生新进程的系统调用是fork()。名字很形象——分叉。程序跑到fork()这一行内核会创建一个和当前进程几乎一模一样的子进程。这个复制动作包含了分配新的PCB和内核数据结构复制父进程的地址空间内容复制打开的文件描述符表和环境变量传统认知里fork()是“完全复制”但在现代Linux实现中其实采用了写时复制Copy-On-WriteCOW技术。刚fork出来的子进程并不真的把父进程的内存全部拷贝一遍而是共享同一份物理内存页面只有当其中一方试图修改共享页面时内核才真正执行复制。这个优化极大地提升了fork()的速度也让传统的“复制”代价大大降低。fork()最“迷惑”的行为在于它的返回值在父进程中返回子进程的PID0在子进程中返回0。如果你不去判断返回值程序执行完fork()后父子进程会各自从下一行代码开始继续执行这会导致两份代码同时在跑。2.3 exec族函数给子进程换“身体”有了fork()我们就能创建出一个“克隆体”了。但在绝大多数场景中光有克隆体是不够的。你打开终端执行vimShell的做法是fork()出一个子进程然后这个子进程立刻调用exec系列函数把自己替换成了全新的程序。这里就引出了exec族函数的核心作用加载一个可执行文件并将其代码段、数据段覆盖到当前进程的地址空间中直接“改头换面”。这个过程中PID不会变变的只是进程的内核映像。常见的exec函数有execl、execv、execle、execve等它们的区别主要在参数的传递方式上是逐个列举参数l系列还是用数组传递v系列是否需要指定环境变量e系列。一个典型组合就是pid_t pid fork(); if (pid 0) { // 子进程执行到这里 execl(/bin/ls, ls, -l, NULL); // exec成功就不会走到下面的代码 perror(exec failed); exit(1); } else if (pid 0) { // 父进程执行到这里可以wait子进程 wait(NULL); }这是我迄今见过最经典的进程创建范式它的关键点在于fork负责“生”exec负责“换”。二者一组合系统才能实现“启动另一个程序”的效果否则fork出来的永远是自己的复制品。2.4 僵尸进程与孤儿进程最容易被忽视的两个坑现在来说前面卖关子的僵尸进程。它的成因是子进程先于父进程退出而此时父进程没有调用wait()或waitpid()来回收子进程的退出状态码。子进程虽然已经停止了运行但它的PCB仍残留在内核中等待父进程来收取“死亡证明”。系统把这种状态标记为ZZombie。为什么不能直接销毁因为父进程需要通过wait系统调用读取子进程的退出码是正常退出还是被信号杀死退出码是多少这是父进程了解子进程执行结果的重要途径。在父进程没有读取之前内核必须把这个信息留着。如果父进程一直不回收僵尸进程就会一直存在。由于僵尸进程已经不再占用内存和CPU资源所以在top里看到它CPU和内存都显示0但它占据着PID和内核PCB大量积累会导致系统无法创建新进程。还有一个相关的概念是孤儿进程父进程先退出子进程变成了“没妈的孩子”。但Linux设计得很周到孤儿进程会被1号进程init或systemd收养由它来负责后续的回收工作。所以孤儿进程最终不会被丢掉但要注意当父进程退出时子进程会成为新的会话首进程并运行在后台如果终端关闭它可能不会再收到原终端相关的信号了。3. 进程管理的核心操作看懂PID的世界3.1 ps命令实战从进程列表读出有用信息前面聊完了理论现在进入实操。ps是查看进程最基础的命令但很多人用了很久也只停留在ps aux的阶段。我建议从ps -ef和ps aux两个流派入手。ps -ef是标准格式输出每一列含义为UID用户、PID进程号、PPID父进程号、CCPU占用率、STIME启动时间、TTY关联终端、TIME累计CPU时间、CMD完整命令行。ps aux是BSD风格输出多了%CPU、%MEM、VSZ、RSS等列。VSZ是虚拟内存大小RSS是常驻物理内存大小这两个值直观看程序占了多少内存。很多时候你以为某个程序内存占用很高其实大部分是VSZ的虚拟内存——程序可能只是申请了一大段但从未碰过的空间实际占用的只是RSS。我常用的排查命令是# 查看所有进程包含完整命令行 ps -ef # 查看某个进程的详细信息这里以nginx为例 ps -ef | grep nginx # 按内存占用排序展示进程 ps aux --sort-%mem | head -20 # 按CPU占用排序展示进程 ps aux --sort-%cpu | head -20grep某个进程时有个经典的小坑ps -ef | grep nginx会匹配到包括grep自身在内的行。因为grep命令在运行时也是一个进程它的命令行里恰好包含了“nginx”这个关键字。你会看到一行grep nginx的进程这就是为什么很多老手会写ps -ef | grep [n]ginx用正则排除自身。3.2 top命令动态监控进程资源ps是快照而top是持续刷新的监控面板。真正用过一段时间top的人都知道这个命令的价值不只是看几个数字而是看变化趋势。top默认按CPU使用率排序每3秒刷新一次。上半部分是系统概况负载均衡值load average、进程总数、运行/睡眠/僵尸进程计数、CPU使用率分布us用户态、sy内核态、waIO等待、id空闲、st被虚拟机偷走的时间以及内存使用情况。下半部分是各进程的实时列表。实际排障时我通常先看load average再看wa占用比例最后定位到具体进程。如果wa很高说明CPU在干等盘IO如果st很高说明宿主机超卖严重云主机的计算资源被邻居抢了如果sy很高说明系统陷入大量系统调用可能是进程频繁切换或锁竞争严重。配合快捷键还能玩出更多花样按1展开每个CPU核心的使用率、按P按CPU排序、按M按内存排序、按H展开线程视图。多核机器上如果只有一个核满载而其他核空闲说明进程是单线程的需要从程序并发模型上考虑优化。3.3 kill命令与信号机制优雅终止进程的几种姿势很多人把kill理解为“杀死进程”其实kill的真实身份是“向进程发送信号”。信号是Linux进程间通信的一种异步机制进程收到信号要么执行默认动作要么调用自定义的signal handler处理。kill不带参数时默认发送SIGTERM15号信号这是请求进程优雅退出。进程可以捕获这个信号执行清理工作后再退出比如释放文件句柄、保存状态、关闭连接。所以这是“请走”而非“打死”。kill -9发送的是SIGKILL信号这个信号无法被捕获或忽略内核会立即强制终止该进程。粗暴但有效可代价是进程连清理的机会都没有。一个安全的终止流程应该是# 1. 先发SIGTERM给进程优雅退出的机会 kill -15 12345 # 2. 等待几秒后检查是否还在 ps -p 12345 # 3. 如果还在再考虑SIGKILL kill -9 12345在脚本中处理时我会加一个倒计时循环kill -15 12345 2/dev/null for i in $(seq 1 10); do if ! ps -p 12345 /dev/null 21; then echo 进程已优雅退出 break fi sleep 1 doneSIGTERM杀不掉的情况多半是进程阻塞在不可中断睡眠状态这时才轮得到SIGKILL上场。还有一种常见误操作直接用kill -9杀数据库或消息队列等有持久化需求的服务可能造成数据文件损坏。这类服务尽量先通过自身的管理命令或SIGTERM关停不要一上来就重拳出击。3.4 进程优先级与nice值Linux进程调度中最直观的可控参数是nice值。这个名字挺有意思——进程“越谦让nice值越高”系统分配CPU的优先级就越低。nice值范围是-20到19默认0。数值越小优先级越高-20的进程会比19的进程获得更多CPU时间。普通用户只能调高nice值变得更谦让只有root才能给进程更低的nice值。查看和调整的命令# 查看进程优先级 ps -o pid,nice,comm -p 12345 # 启动时指定nice值 nice -n -5 ./my_program # 对已运行进程调整nice值 renice -n 10 -p 12345实际调优的时候如果一台机器同时跑着数据库服务和批量计算任务我会把批处理任务的nice调高一些比如10~15这样高优先级的数据库进程响应用户请求时不容易被计算任务拖累。不过现代多核CPU上用nice的效果比以前单核时代弱化了很多它只是在调度器权重计算中占一个因子不再是“独占CPU”那种概念。4. 串起全流程一个进程从exec到被回收的完整演示4.1 演示环境准备不写完整示例的讲解都是“看着痛快、上手抓瞎”。这里我用一段C代码串起fork、exec、wait和信号处理让大家从代码层面真正理解一个进程完整的一生。准备环境只需要一个Linux系统加gcc编译器。我用最简单的示例不需要第三方库。// proc_demo.c #include stdio.h #include stdlib.h #include unistd.h #include sys/wait.h #include signal.h void handle_sigterm(int sig) { printf([父进程] 收到SIGTERM信号准备清理退出...\n); fflush(stdout); exit(0); } int main() { pid_t pid; int status; // 注册SIGTERM处理优雅退出示例 signal(SIGTERM, handle_sigterm); pid fork(); if (pid 0) { perror(fork失败); exit(1); } if (pid 0) { // 子进程分支 printf([子进程] PID%d, PPID%d即将执行外部程序\n, getpid(), getppid()); srand(getpid()); int sleep_time rand() % 5 1; sleep(sleep_time); printf([子进程] 执行完毕退出码为 %d\n, sleep_time); exit(sleep_time); } else { // 父进程分支 printf([父进程] PID%d已创建子进程 PID%d等待子进程结束...\n, getpid(), pid); wait(status); if (WIFEXITED(status)) { printf([父进程] 子进程正常退出退出码%d\n, WEXITSTATUS(status)); } printf([父进程] 回收完成演示结束\n); } return 0; }4.2 编译运行与输出解读编译并运行这段代码gcc proc_demo.c -o proc_demo ./proc_demo一次典型的输出是[父进程] PID10001已创建子进程 PID10002等待子进程结束... [子进程] PID10002, PPID10001即将执行外部程序 [子进程] 执行完毕退出码为 4 [父进程] 子进程正常退出退出码4 [父进程] 回收完成演示结束这段代码完整展示了几个核心知识点fork()返回两次父进程拿到的是子进程的PID子进程拿到的是0所以用if (pid 0)来区分父子分支。子进程继承父进程的代码段和运行环境但它从fork处往下独立执行printf打印的顺序不能保证父子谁先谁后因为两个进程在竞争同一个终端输出。父进程调用wait(status)后进入阻塞状态直到子进程退出。这正好呼应了前面僵尸进程的成因——如果这里不调用wait子进程退出后就会变成僵尸。WIFEXITED(status)和WEXITSTATUS(status)这对宏是解析子进程退出状态的固定方式它们通过检查status的低8位获取真正的退出码。4.3 综合演示观察进程状态变迁光看输出还不够我在运行时用另一个终端窗口监控这个进程的状态变迁。启动./proc_demo后立刻在另一个会话执行# 在程序运行期间反复执行 ps -o pid,ppid,stat,comm -p 10001,10002你会看到子进程的状态从S睡眠中逐步变化。如果我把子进程的sleep时间加长甚至可以在子进程sleep时用kill -STOP 10002将它暂停再观察状态从S变成T。然后用kill -CONT 10002让它继续跑状态又恢复为S。这种看得见摸得着的状态切换比单纯背状态表印象深刻得多。再验证僵尸进程形态把父进程的wait注释掉重新编译运行子进程退出后立刻在另一个终端执行ps aux | grep proc_demo会看到状态列变成ZCMD列带有defunct标记。我强烈建议读者亲自做一遍这个验证因为只有实际看到进程从S到Z的变化过程才能真正理解“回收子进程”不是一句空话。5. 常见问题与排查技巧5.1 僵尸进程堆积的应急处理问题线上机器发现大量Z状态进程PID逐渐被耗尽新起进程时报Cannot fork。排查路径首先用ps -eo pid,ppid,stat,comm | grep Z罗列出所有僵尸进程及其父进程PID。关键操作是判断父进程是谁如果父进程是1号系统进程基本无解只能等如果父进程是具体业务进程问题多半在业务代码里没有正确调用wait或没有捕获SIGCHLD信号。处理手段并不是杀僵尸进程本身——僵尸进程杀不掉因为它的生命周期已经结束残留的只是内核PCB。正确做法是处理它的父进程让父进程退出或让父进程主动执行wait/waitpid。之前帮一个朋友排查过这个问题他写的守护进程用fork()启动了子进程做任务但完全没写wait回收逻辑时间一长僵尸成堆。修复方式是在父进程里注册一个SIGCHLD信号处理函数void reap_child(int sig) { int status; while (waitpid(-1, status, WNOHANG) 0); }SIGCHLD是子进程状态变化时主动发给父进程的信号父进程在这个handler里用非阻塞方式反复收割子进程彻底解决僵尸堆积。5.2 “kill -9都杀不掉”的D状态进程这个现象第一次碰到的人很容易慌。用kill -9发信号结果进程还在列表里。其实看状态列就知道原因D 状态是不可中断睡眠进程在内核态里等待某个IO事件完成比如磁盘读写卡住、NFS挂载点无响应。这种情况下信号无法被处理因为进程还没有回到用户态。处理步骤是用ps -eo pid,stat,wchan查看进程在内核里的等待函数wchan列能告诉你它阻塞在哪个内核函数上比如wait_on_page_bit说明在等磁盘页缓存回写。检查系统IO状态iostat -x 1看磁盘的%util和await指标、dmesg看有没有IO错误。如果问题出在NFS挂载检查网络或重启NFS服务。如果问题无法解决这台机器的负载可能一直降不下去最直接的物理手段是重启机器——虽然粗暴但确实是最后一招。5.3 不同用户启动进程的管理差异使用top或ps时大家可能注意过有些进程你能看到但杀不掉。这是因为Linux对信号发送有权限控制普通用户只能向自己拥有的进程发信号向其他用户的进程发起kill会收到Operation not permitted。所以想让一个普通用户能管理某个特定服务进程比较稳妥的做法是用setuid或者通过systemd服务来管理让服务以指定用户身份运行。5.4 进程排查的常用速查表我把日常排查中觉得最实用的一组命令凑成了一张速查表贴在下面方便大家直接抄作业目标命令查看进程的所有状态ps -eo pid,ppid,stat,comm查看进程环境变量cat /proc/PID/environ查看进程打开的fd列表ls -l /proc/PID/fd/查看进程内存映射cat /proc/PID/maps跟踪进程系统调用strace -p PID跟踪进程信号处理gdb -p PID其中/proc/PID/虚拟目录是这个知识点的宝库它存放着每个进程运行时最真实的状态信息内核把进程的内部细节完整映射在这里。读目录比任何文档都真实。6. 几个容易踩的细节坑与终端经验这一段是平时积累下来的“小账本”未必在教科书里能找到但实际用的时候非常容易中招。第一个坑是ps命令查到的PID和pgrep查到的PID可能不完全一致。pgrep默认按进程名匹配而ps -ef里的CMD列显示的是完整命令行。比如你用python3 manage.py runserver启动服务pgrep -f manage.py能找到它但pgrep python3可能匹配到好几条路径下同名的不同程序。第二个坑是top里看到的CPU使用率可能超过100%。在Linux上CPU使用率是按单个核心计算的如果你的服务是多线程进程8个核心全跑满显示出来就是800%。看到这个数字不代表top出bug了反而说明你的多线程并发是充分的单核满载那种0%到100%之间波动的数值才是典型的单线程瓶颈。第三个坑是Shell的管道也有进程模型。你在命令行写cat file | grep pattern | wc -l实际上系统一次性创建了三个进程用管道把它们串起来。管道在进程间传递数据时如果某一端慢了另一端的write操作可能会阻塞这也是为什么某些很“简单”的Shell脚本在某些数据量下突然变慢的原因——管道缓冲区溢出了生产消费速度不匹配。第四个经验是关于PID的变化规律。Linux会顺序分配PID达到上限后回绕。新进程的PID未必总是递增的所以脚本里如果依赖“新进程PID一定更大”这种假设早晚会踩坑。判断父子关系永远是看PPID而不是比PID大小。第五个经验是我在实际使用进程优先级时的体会nice值对IO密集型的进程影响甚微。nice调的是CPU调度权重而进程等待磁盘IO时根本不占用CPU时间。如果你的进程压垮的是磁盘调nice没有明显作用得从限流或换存储设备考虑。7. 结语从命令操作走向机制理解从静态的程序到动态的进程从PCB到CPU调度队列从fork到wait回收很多人学Linux进程这一块最大的障碍是“只见命令不见机制”。ps按出来一行数据背后是一整个进程表的链路kill一个PID实际上是一次信号传递与内核状态机的流转。我个人的体会是进程概念不像学命令敲一敲就会了它更像学电路——命令只是电流表上的读数你要理解的是电流为什么流过、在哪里受阻、最后如何汇合。多写几段fork的C代码多观察几种进程状态在动态变化中的表现甚至故意制造几次僵尸进程再手动清理掉这种刻意的“破坏性实验”往往比顺向学习记忆深刻得多。后续如果你把进程这块理解透了学习线程、信号处理、进程间通信管道、共享内存、消息队列都会顺滑不少因为它们全都建立在“进程是受内核管理的执行实体”这个底层心智模型之上。

相关新闻

Agent Skills实战:从技能设计到模型调用的完整指南

Agent Skills实战:从技能设计到模型调用的完整指南

如果你最近在折腾AI Agent,应该对“Agent Skills”这个词不陌生。我自己的理解很直白:它是一套让智能体真正“动手干活”的能力模块,大模型负责思考,技能模块负责执行——两者配在一起,才是一个完整的Agent。这个标题下…

2026/10/14 8:21:54 阅读更多 →
测量工具信效度报告也要双版本烟测:三列表 + A/B 验收表

测量工具信效度报告也要双版本烟测:三列表 + A/B 验收表

千笔-AIWritePaper https://www.aiwritepaper.com 信效度段最常见的「假完成」有三种:一是空话,即「量表信效度良好」;二是只抄原作者 Cronbach α,读者不知道本样本算出多少;三是删了题却不重算,或把 α …

2026/10/14 8:21:54 阅读更多 →
Agent技能化实战:从Function Calling到多技能编排的完整指南

Agent技能化实战:从Function Calling到多技能编排的完整指南

最近半年,我明显感觉圈子里聊 Agent 的方式变了。去年大家还在比谁的 Agent 能"多轮对话不跑偏",今年已经开始讨论"这个 Agent 拥有哪些技能、技能怎么编排、技能效果怎么评估"——一个叫agent-skills的话题在讨论中反复出现。说白了…

2026/10/14 8:21:54 阅读更多 →

最新新闻

SpringBoot体育馆预约系统设计与性能优化实践

SpringBoot体育馆预约系统设计与性能优化实践

1. 项目背景与核心价值体育馆场地预约系统是体育场馆数字化管理的核心工具。过去三年我参与过7个同类系统的开发实施,发现传统电话预约和纸质登记方式存在三大痛点:预约冲突频发(平均每周3-5起)、场地利用率不足(高峰时…

2026/10/14 9:06:48 阅读更多 →
USN日志+SQLite重写Everything索引:低内存搜索实现与参数优化

USN日志+SQLite重写Everything索引:低内存搜索实现与参数优化

简介:一份针对 Everything 搜索工具内存占用过高问题的优化源码包,面向遇到同类性能困扰的普通用户及软件开发者。资源基于实际使用近四年的场景,通过调整索引设置——排除系统文件、隐藏文件及特定目录,将索引文件压缩至 60K&…

2026/10/14 9:06:48 阅读更多 →
SpringBoot构建诗词平台:技术实现与传统文化融合

SpringBoot构建诗词平台:技术实现与传统文化融合

1. 项目背景与核心价值 中华诗词作为传统文化瑰宝,在数字化时代面临着传承与传播的新挑战。这个基于SpringBoot的诗词文化交流平台,本质上解决的是三个核心问题:一是经典诗词资源的数字化整合难题,二是诗词爱好者互动交流的渠道缺…

2026/10/14 9:06:48 阅读更多 →
SpringBoot诗词平台开发:智能检索与格律校验实践

SpringBoot诗词平台开发:智能检索与格律校验实践

1. 项目概述 中华诗词文化交流平台是一个基于SpringBoot框架开发的传统文化数字化项目,旨在为诗词爱好者构建集创作、鉴赏、学习、互动于一体的专业社区。这个平台解决了传统诗词文化传播中的三个核心痛点:一是线下交流渠道有限,二是初学者缺…

2026/10/14 9:06:48 阅读更多 →
Android开发:接口设计与数据共享最佳实践

Android开发:接口设计与数据共享最佳实践

1. Android接口与数据共享概述在Android应用开发中,接口设计与数据共享机制是构建复杂应用的基础能力。作为一名有五年Android开发经验的工程师,我经常需要处理不同组件间的通信和数据流转问题。合理的接口设计能让代码更易维护,而高效的数据…

2026/10/14 9:06:48 阅读更多 →
Three.js加载Gltf模型并播放动画

Three.js加载Gltf模型并播放动画

老规矩:初始化相机,场景,渲染器等,这里不过多赘述,详情看上一篇文章。 从0搭建一个3D立体场景:光影与交互全解析-CSDN博客 基本使用 只要在文件中引入这个即可: const loader new GLTFLoad…

2026/10/14 9:05:48 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 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/13 22:01:27 阅读更多 →
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/14 7:53:34 阅读更多 →