谈谈守护进程与僵尸进程

C++八股文分享---进程 C++八股文分享—进程 前言 本文主要分享的是关于进程与线程的八股文,既是对自己基础知识的巩固,也是与大家分享。文章中如果观点错误的地方肯定大家予以指正。文章中黑体部分文字为相关问题的要点,各位看官一定要牢牢背下来啊。 1、什么是进程? 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。 2、时间片的概念 时间片(timeslice)又称为“量子(quantum)”或“处理器片(processor slice)”是操作系统分配 阅读详情

转载自:http://blog.youkuaiyun.com/russell_tao/article/details/7090033


04年时维护的第一个商业服务就用了两次fork产生守护进程的做法,前两天在网上看到许多帖子以及一些unix书籍,认为一次fork后产生守护进程足够了,各有道理吧,不过多了一次fork到底是出于什么目的呢?


进程也就是task,看看内核里维护进程的数据结构task_struct,这里有两个成员:

[cpp]  view plain copy
  1. struct task_struct {  
  2.     volatile long state;  
  3.     int exit_state;  
  4.     ...  
  5. }  

看看include/linux/sched.h里的value取值:

[cpp]  view plain copy
  1. #define TASK_RUNNING        0  
  2. #define TASK_INTERRUPTIBLE  1  
  3. #define TASK_UNINTERRUPTIBLE    2  
  4. #define __TASK_STOPPED      4  
  5. #define __TASK_TRACED       8  
  6. /* in tsk->exit_state */  
  7. #define EXIT_ZOMBIE     16  
  8. #define EXIT_DEAD       32  
  9. /* in tsk->state again */  
  10. #define TASK_DEAD       64  
  11. #define TASK_WAKEKILL       128  
  12. #define TASK_WAKING     256  
  13. #define TASK_STATE_MAX      512  

可以看到,进程状态里除了大家都理解的running/interuptible/uninterruptible/stop等状态外,还有一个ZOMBIE状态,这个状态是怎么回事呢?


这是因为linux里的进程都属于一颗树,树的根结点是linux系统初始化结束阶段时启动的init进程,这个进程的pid是1,所有的其他进程都是它的子孙。除了init,任何进程一定有他的父进程,而父进程会负责分配(fork)、回收(wait4)它申请的进程资源。这个树状关系也比较健壮,当某个进程还在运行时,它的父进程却退出了,这个进程却没有成为孤儿进程,因为linux有一个机制,init进程会接管它,成为它的父进程。这也是守护进程的由来了,因为守护进程的其中一个要求就是希望init成为守护进程的父进程。


如果某个进程自身终止了,在调用exit清理完相关的内容文件等资源后,它就会进入ZOMBIE状态,它的父进程会调用wait4来回收这个task_struct,但是,如果父进程一直没有调用wait4去释放子进程的task_struct,问题就来了,这个task_struct谁来回收呢?永远没有人,除非父进程终止后,被init进程接管这个ZOMBIE进程,然后调用wait4来回收进程描述符。如果父进程一直在运行着,这个ZOMBIE会永远的占用系统资源,用KILL发任何信号量也不能释放它。这是很可怕的,因为服务器上可能会出现无数ZOMBIE进程导致机器挂掉。


来看看内核代码吧。进程在退出时执行sys_exit(C程序里在main函数返回会执行到),而它会调用do_exit,do_exit首先清理进程使用的资源,然后调用exit_notify方法,将进程置为僵尸ZOMBIE状态,决定是否要以init进程做为当前进程的父进程,最后通知当前进程的父进程:

kernel/exit.c

[cpp]  view plain copy
  1. static void exit_notify(struct task_struct *tsk)  
  2. {  
  3.     int state;  
  4.     struct task_struct *t;  
  5.     struct list_head ptrace_dead, *_p, *_n;  
  6.   
  7.     if (signal_pending(tsk) && !tsk->signal->group_exit  
  8.         && !thread_group_empty(tsk)) {  
  9.         /* 
  10.          * This occurs when there was a race between our exit 
  11.          * syscall and a group signal choosing us as the one to 
  12.          * wake up.  It could be that we are the only thread 
  13.          * alerted to check for pending signals, but another thread 
  14.          * should be woken now to take the signal since we will not. 
  15.          * Now we'll wake all the threads in the group just to make 
  16.          * sure someone gets all the pending signals. 
  17.          */  
  18.         read_lock(&tasklist_lock);  
  19.         spin_lock_irq(&tsk->sighand->siglock);  
  20.         for (t = next_thread(tsk); t != tsk; t = next_thread(t))  
  21.             if (!signal_pending(t) && !(t->flags & PF_EXITING)) {  
  22.                 recalc_sigpending_tsk(t);  
  23.                 if (signal_pending(t))  
  24.                     signal_wake_up(t, 0);  
  25.             }  
  26.         spin_unlock_irq(&tsk->sighand->siglock);  
  27.         read_unlock(&tasklist_lock);  
  28.     }  
  29.   
  30.     write_lock_irq(&tasklist_lock);  
  31.   
  32.     /* 
  33.      * This does two things: 
  34.      * 
  35.      * A.  Make init inherit all the child processes 
  36.      * B.  Check to see if any process groups have become orphaned 
  37.      *  as a result of our exiting, and if they have any stopped 
  38.      *  jobs, send them a SIGHUP and then a SIGCONT.  (POSIX 3.2.2.2) 
  39.      */  
  40.   
  41.     INIT_LIST_HEAD(&ptrace_dead);  
  42.     <strong><span style="color:#ff0000;">forget_original_parent(tsk, &ptrace_dead);</span></strong>  
  43.     BUG_ON(!list_empty(&tsk->children));  
  44.     BUG_ON(!list_empty(&tsk->ptrace_children));  
  45.   
  46.     /* 
  47.      * Check to see if any process groups have become orphaned 
  48.      * as a result of our exiting, and if they have any stopped 
  49.      * jobs, send them a SIGHUP and then a SIGCONT.  (POSIX 3.2.2.2) 
  50.      * 
  51.      * Case i: Our father is in a different pgrp than we are 
  52.      * and we were the only connection outside, so our pgrp 
  53.      * is about to become orphaned. 
  54.      */  
  55.        
  56.     t = tsk->real_parent;  
  57.       
  58.     if ((process_group(t) != process_group(tsk)) &&  
  59.         (t->signal->session == tsk->signal->session) &&  
  60.         will_become_orphaned_pgrp(process_group(tsk), tsk) &&  
  61.         has_stopped_jobs(process_group(tsk))) {  
  62.         __kill_pg_info(SIGHUP, (void *)1, process_group(tsk));  
  63.         __kill_pg_info(SIGCONT, (void *)1, process_group(tsk));  
  64.     }  
  65.   
  66.     /* Let father know we died  
  67.      * 
  68.      * Thread signals are configurable, but you aren't going to use 
  69.      * that to send signals to arbitary processes.  
  70.      * That stops right now. 
  71.      * 
  72.      * If the parent exec id doesn't match the exec id we saved 
  73.      * when we started then we know the parent has changed security 
  74.      * domain. 
  75.      * 
  76.      * If our self_exec id doesn't match our parent_exec_id then 
  77.      * we have changed execution domain as these two values started 
  78.      * the same after a fork. 
  79.      *   
  80.      */  
  81.       
  82.     if (tsk->exit_signal != SIGCHLD && tsk->exit_signal != -1 &&  
  83.         ( tsk->parent_exec_id != t->self_exec_id  ||  
  84.           tsk->self_exec_id != tsk->parent_exec_id)  
  85.         && !capable(CAP_KILL))  
  86.         tsk->exit_signal = SIGCHLD;  
  87.   
  88.   
  89.     /* If something other than our normal parent is ptracing us, then 
  90.      * send it a SIGCHLD instead of honoring exit_signal.  exit_signal 
  91.      * only has special meaning to our real parent. 
  92.      */  
  93.     if (tsk->exit_signal != -1 && thread_group_empty(tsk)) {  
  94.         int signal = tsk->parent == tsk->real_parent ? tsk->exit_signal : SIGCHLD;  
  95.         <span style="color:#ff0000;">do_notify_parent(tsk, signal);</span>  
  96.     } else if (tsk->ptrace) {  
  97.         do_notify_parent(tsk, SIGCHLD);  
  98.     }  
  99.   
  100.     <span style="color:#ff0000;"><strong>state = EXIT_ZOMBIE;</strong></span>  
  101.     if (tsk->exit_signal == -1 && tsk->ptrace == 0)  
  102.         state = EXIT_DEAD;  
  103.     tsk->exit_state = state;  
  104.   
  105.     /* 
  106.      * Clear these here so that update_process_times() won't try to deliver 
  107.      * itimer, profile or rlimit signals to this task while it is in late exit. 
  108.      */  
  109.     tsk->it_virt_value = 0;  
  110.     tsk->it_prof_value = 0;  
  111.   
  112.     write_unlock_irq(&tasklist_lock);  
  113.   
  114.     list_for_each_safe(_p, _n, &ptrace_dead) {  
  115.         list_del_init(_p);  
  116.         t = list_entry(_p,struct task_struct,ptrace_list);  
  117.         release_task(t);  
  118.     }  
  119.   
  120.     /* If the process is dead, release it - nobody will wait for it */  
  121.     if (state == EXIT_DEAD)  
  122.         release_task(tsk);  
  123.   
  124.     /* PF_DEAD causes final put_task_struct after we schedule. */  
  125.     preempt_disable();  
  126.     tsk->flags |= PF_DEAD;  
  127. }  

大家可以看到这段内核代码的注释非常全。forget_original_parent这个函数还会把该进程的所有子孙进程重设父进程,交给init进程接管。


回过头来,看看为什么守护进程要fork两次。这里有一个假定,父进程生成守护进程后,还有自己的事要做,它的人生意义并不只是为了生成守护进程。这样,如果父进程fork一次创建了一个守护进程,然后继续做其它事时阻塞了,这时守护进程一直在运行,父进程却没有正常退出。如果守护进程因为正常或非正常原因退出了,就会变成ZOMBIE进程。

如果fork两次呢?父进程先fork出一个儿子进程,儿子进程再fork出孙子进程做为守护进程,然后儿子进程立刻退出,守护进程被init进程接管,这样无论父进程做什么事,无论怎么被阻塞,都与守护进程无关了。所以,fork两次的守护进程很安全,避免了僵尸进程出现的可能性。

IEC61850客户端连接实战:CMS vs MMS协议抓包对比与性能初探(基于YX-PIS协议栈) 本文深入探讨了IEC61850客户端连接中CMS与MMS协议的抓包对比与性能优化。通过Wireshark抓包分析、协议栈配置实战和性能基准测试,揭示了两种协议在连接建立、数据交互和性能调优方面的关键差异,为智能电网自动化系统的协议选择提供了实用指南。 阅读详情

相关推荐

Android Qcom USB Driver学习(五)

在前面的第二期中,我们知道一些非标的USB的充电类型Floatingcharger,其具有和普通DCP相似的大电流能力,但在最初检测的时候会限制Inputcurrentlimit到100mA,来进行检测是否是SDP类型,所以要支持大电流输入就需要进行vote操作,前面的几篇都有涉及,所以本文学习一下pmicusbcharger都相关的vote机制。...

Android驱动开发 2901

经典2fork解决僵死进程

目的是父进程异步等待,子进程不变僵尸进程,(让子进程属于init) 。如果一个进程要f o r k一个子进程,但不要求它等待 子进程终止,也不希望子进程处于僵死状态直到父进程终止,实现这一要求的诀窍是调用fork 两次。 在第二个子进程中调用s l e e p以保证在打印父进程I D时第一个子进程已终止。在f o r k之后, 父、子进程都可继续执行——我们无法预知哪一个会先执行。如...

走在左边 149

2025年度工业级3D全景相机技术解析选型参考:国产品牌的技术突围与市场格局

本文分析了工业级3D相机在智慧城市和建筑数字化中的应用。通过对比国内外主流设备的技术参数和应用场景,发现国产品牌如众趣科技在性价比和本地化服务方面具有优势,尤其适合大型空间扫描项目;而国际品牌如Faro、Leica则在精度和可靠性上保持领先。建议根据项目需求(如测距范围、精度要求、预算)进行设备选型,综合考虑扫描效率、数据处理能力和系统兼容性。国产品牌已在国内大型基建项目中展现出竞争力,国际品牌则更适合高精度逆向工程和国际化项目。

gxdtgsy的博客 1073

僵尸进程、孤儿进程、守护进程、守护线程、join

1)     守护进程、守护线程:守护进程会等待主进程代码运行完毕后被销毁,然后主进程会一直等非守护的子进程都运行完毕后回收子进程的资源(否则会产生僵尸进程),才会结束。守护线程会等待主线程所在的进程内所有非守护线程统统运行完毕后被销毁。守护线程的例子,垃圾回收器,只要JVM启动,他始终在运行,实时监控和管理系统中可以被回收的资源。2)     孤儿进程:一个父进程退出,而它的一个或多个子进程还在...

qiuchaoxi的博客 1350

YOLOv8【第九章:模型部署篇·第2节】一文搞懂,模型剪枝与压缩技术!

🏆 本文收录于 《YOLOv8实战:从入门到深度优化》 专栏。该专栏系统复现并梳理全网各类 YOLOv8 改进与实战案例(当前已覆盖分类 / 检测 / 分割 / 追踪 / 关键点 / OBB 检测等方向),坚持持续更新 + 深度解析,质量分长期稳定在 97 分以上,可视为当前市面上 覆盖较全、更新较快、实战导向极强 的 YOLO 改进系列内容之一。

**My Coding Family** 1527

【数据湖仓】:Iceberg、Hudi 和 Paimon 对比

适用于需要多计算引擎支持和复杂查询的离线批处理场景。其中立性和灵活性使其成为构建新型数据平台的理想选择。:在需要高频数据更新和高效小文件管理的场景中表现出色,适合实时数据处理和增量数据更新的业务需求。:专为实时流式数据处理设计,适用于对数据实时性要求高的业务场景,如实时数据分析和实时指标监控。通过以上分析,可以看出 Apache Iceberg、Apache Hudi 和 Apache Paimon 各有其独特的优势和适用场景。企业应根据自身业务需求和技术栈选择合适的数据湖仓方案,以充分发挥其价值。

欢迎来到我的博客,一起探索代码里的世界! 1610

创建守护进程为什么要fork两次

1、守护进程的概念与特点 2、创建守护进程 3、创建守护进程为什么需要fork两次

TerryZjl的博客 7254

FLACS-CFD 气云爆炸模拟全流程工况构建指南:从泄漏到爆炸

摘要: 本文详细介绍了使用FLACS-CFD模拟气云爆炸的全流程构建方法,重点解决泄漏模拟到爆炸模拟的气云文件传递问题。核心步骤包括: 项目规划:明确泄漏源(甲烷管道腐蚀泄漏)和爆炸场景,建立标准目录结构; 网格设计:泄漏与爆炸模拟需采用兼容的网格,爆炸域应为泄漏域的子区域且网格划分一致; 泄漏模拟配置:通过.inp文件定义几何、网格、风场、燃料及泄漏源参数,确保输出爆炸所需的气云文件(*.uni/*.fla); 关键设置:统一燃料名称、时间点匹配、路径正确性,避免版本兼容性问题。

huanghm88的专栏 762

守护进程与僵尸进程

守护进程   在linux或者unix操作系统中在系统的引导的时候会开启很多服务,这些服务就叫做守护进程。为了增加灵活性,root可以选择系统开启的模式,这些模式叫做运行级别,每一种运行级别以一定的方式配置系统。   守护进程是脱离于终端并且在后台运行的进程。守护进程脱离于终端是为了避免进程在执行过程中的信息在任何终端上显示并且进程也不会被任何终端所产生的终端信息所打断。   守

haijiaoqihao20160106的博客 526

从基础到前沿:深度剖析光束整形与扫描技术

光束扫描的核心在于通过特定手段改变光束的传播方向,从而实现对目标区域的扫描。在这个过程中,振镜、声光偏转器、电光偏转器等元件发挥着关键作用。振镜是一种常见的光束扫描元件,它主要由电机、反射镜和透镜组成。当电机驱动反射镜旋转时,根据光的反射定律,反射镜会改变入射光线的反射方向。假设初始时光束垂直入射到反射镜上,当反射镜绕某一轴旋转一定角度时,光束的反射方向就会相应改变,从而实现光束在空间中的扫描。在激光打标设备中,通过控制振镜的快速摆动,能够使激光束在工件表面快速移动,完成各种图案和文字的标记。

远程部署调试 运行安装 项目调试 二次开发 项目技术新 持续迭代 部分源码免费分享 2098

实战优化!C#上位机串口通信卡顿问题:超时机制+数据缓存方案

串口名</param>/// <param name="baudRate">波特率

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 1064

强上Linux内核3--一个程序从开始运行到结束的完整过程,你能说出来多少?

目录一个程序从开始运行到结束的完整过程,你能说出来多少?加餐部分进程树谈谈你对动态链接库和静态链接库的理解? 一个程序从开始运行到结束的完整过程,你能说出来多少? 写代码:用系统调用创建进程 写代码 进行编译:程序的二进制格式 编译成 ELF(可执行与可链接格式) 格式的二进制文件, 有三种格式(可重定位 .o 文件; 可执行文件; 共享对象文件 .so) 可重定位 .o 文件(ELF 第一种格式) .h + .c 文件, 编译得到可重定位 .o 文件 .o 文件由: ELF 头, 多个节(s

weixin_45785536的博客 1202

【毕业设计之python系列】基于Flask的在线音乐网设计与实现

本论文旨在通过使用Python的Web框架Flask,设计和实现一个具有完整功能的在线音乐网站。该网站包括用户注册、登录、注销、歌曲上传、编辑、删除、歌单创建、编辑、删除、评论、搜索、音乐播放等功能,并针对不同用户角色(如普通用户、管理员)实现了不同的权限管理。同时,本论文还介绍了如何使用第三方API(如网易云音乐API)获取歌曲信息等技术实现。在设计上,本论文采用了MVC(Model-View-Controller)架构模式,将后端数据处理与前端展示分离,以便于代码的管理和维护。在数据库设计方面,本论文设

m0_56073435的博客 3244

谈谈kubernetes-Runtime

大家一看这么搞可不行, 今天能有 rkt, 明天就能有更多幺蛾子出来, 这么搞下去我们小组也不用干活了, 整天搞兼容性的 bug 就够呛. 于是乎, k8s 1.5 推出了 CRI 机制, 即容器运行时接口(Container Runtime Interface), k8s 告诉大家, 你们想做 Runtime 可以啊, 我们也资瓷欢迎, 实现这个接口就成, 成功反客为主.标准的好处就是方便搞创新, 反正只要我符合标准, 生态圈里的其它工具都能和我一起愉快地工作(…

运维老生常谈 907

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

操作系统常规面试题(三玖天下第一)(二)

锁是用于同步多个线程或进程访问共享资源的一种机制,他确保同一时间只有一个线程可以执行受保护的资源区域的代码段,从而防止出现竞争状态和数据紊乱。基于数据在大部分时间不会发生并发冲突的前提下,因而在操作数据时不是直接加锁,而是在数据提交更新时检查数据是否被其他事务(原子层面的数据库操作)修改过。通过版本号和时间戳控制。他相对于悲观锁减少了加解开销,提高了系统的并发性。但是在高并发时,频繁的检查数据又会降低效率。基于数据在大部分时间会发生冲突的前提下,因而在操作数据时直接加锁,防止其他事务修改。

chengxuyuanxiaok的博客 896

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

子进程和父进程的结论_linux基本概念(二)----进程编程

先给自己打个广告,本人的微信公众号正式上线了,搜索:张笑生的地盘,主要关注嵌入式软件开发,足球等等,希望大家多多关注,有问题可以直接留言给我,一定尽心尽力回答大家的问题一 why 我们要学习进程编程呢?在实际项目开发过程中,一个软件项目肯定要实现各种不同的功能,这些功能放在一个进程中实现肯定是不现实的,这会造成项目十分冗余和繁杂,不适合项目的维护,也不适合项目的后续框架升级。因此,在实...

weixin_35828992的博客 730

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

下一篇: linux内核调度算法(1)--快速找到最高优先级进程
社区艺术家
博客等级 码龄18年 9粉丝 4原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值