进程与子进程

讨论几种父、子进程退出时相互产生的影响,同时也整理一下进程与子进程之间的关系。

孤儿进程

(没爹)
孤儿进程,顾名思义,子进程还在世的时候父进程却结束了。那么孤儿进程没了父进程,是不是就被孤立了呢?不会的,我们还需要了解到1号进程——init进程,在初始化unix系统的时候,会创建一个init进程。然后由init进程创建终端,而终端进程随着用户的接入,会启动更多的进程,以此类推。在这整个系统中,所有的进程都属于以init为根的一棵树。当某个父进程终止,子进程就会被init进程收养。在这些孤儿进程结束时,init进程会回收他们的退出信息,保证他们不一直成为僵尸进程。

以下是如何创建孤儿进程的例子

def create_orphan():
    """  
    :return:
    """
    cur_pid = os.getpid()
    pgid = os.getpgrp()
    print('parent group id', pgid)
    print('-----fork before-----')
    # 在父进程的堆栈中,c_id 为父进程的编号,在子进程的堆栈中,值为0。
    # fork将进程信息写入进程信息表,写时复制父进程的各种数据,同时设置好新进程的各种数据
    # 其中子进程的执行内容设置为fork的后一句,并将进程推送到就绪态的队列中,等待调度器调度执行。
    c_id = os.fork()

    print('-----fork after-----')

    if c_id == 0: # 子进程
        ppid = os.getppid()
        print(os.getpid(), 'kill', ppid)
        os.kill(ppid, 9)
        os.system('ps aux|grep {}'.format(ppid))
        print(os.getpid(), '[killed p] ', ppid)  # 杀死父进程以后被init进程接管。
        print('[killed p] now ppid is ', os.getppid())
        now_ppid = os.getpgrp()
        print('child group id {}'.format(now_ppid))
        os.killpg(now_ppid, 9)
        print(os.getpid(), '[kill group]', now_ppid)  # 整个组死掉了
        print('end...')
    else: # 父进程
        print('i am ', os.getpid())

流程输出

        parent group id 45381
        -----fork before-----
        -----fork after-----
        i am  45381
        -----fork after-----
        45382 kill 45381
        [1]    45381 killed     python process.py
        (dl) (dl) async ⍉ ➜ echoocking       45387   0.0  0.0  4268036    808 s003  S     1:20PM   0:00.00 grep 45381
        echoocking       45383   0.0  0.0  4268616   1112 s003  S     1:20PM   0:00.00 sh -c ps aux|grep 45381
        45382 [killed p]  45381
        [killed p] now ppid is  1
        child group id 45381

流程解释

    获取group id
    打印fork前的提示语
    执行fork
    由于当前是父进程在执行,所以进入c_id != 0 的流程。执行完后父进程等待子进程,父进程被挂起,
    子进程被调度执行,子进程执行fork后的语句
    进入c_id == 0 的流程
    执行kill 父进程,此时子进程的父进程id已经变为1,表示该进程已由init进程进行接管
    查看orphan进程的父进程,其父进程号依旧可以查询到,并且与进程组号相同
    使用进程组号 执行killpg,向整组发送kill信号
    可以看到整个进程组全部退出,后续的打印也就没有输出。

multiprocessing Process popen_fork

以下是multiprocessing的popen_fork的实现,和上述例子非常相似。


    def _launch(self, process_obj):
        code = 1
        parent_r, child_w = os.pipe()  # 不过为什么要创建pipe呢? pipe获得了两个文件描述符。
        self.pid = os.fork()
        if self.pid == 0:
            try:
                os.close(parent_r) 
                if 'random' in sys.modules:
                    import random
                    random.seed()
                code = process_obj._bootstrap()
            finally:
                os._exit(code)
        else:
            os.close(child_w)
            util.Finalize(self, os.close, (parent_r,))
            self.sentinel = parent_r

进程创建过程

在Unix,子进程是父进程的拷贝,其地址空间是父进程地址空间的副本,不可写的内存部分是共享的,例如程序代码。被修改的变量等通过写时复制进行修改。父子进程拥有相同的内存映像、打开的文件描述符,环境变量等。

子进程执行fork后的程序代码。所以multiprocessing里的进程创建后设置执行的内容,就是在fork之后,程序计数器值为fork 后一句的语句编号。

孤儿进程总结

父进程被终止,子进程转为孤儿进程, 结束整组进程可以杀死孤儿进程。或者等待子进程自己结束,结束后的数据回收由init进程接管,所以孤儿进程不会对系统造成过多问题。

ps:此时如果你用ctrl+c,是无法结束子进程的,因为他的终端已经成了1号进程,必须找到其进程号,kill 进程号,来结束。

僵尸进程🧟‍♂️

(有爹,爹不管)
父进程创建,由父进程创建子进程,当子进程退出以后,大部分的资源被释放,但是还是会有例如pid, 存在时间的记录等资源没有被释放。所以当子进程退出后,子进程会先变成僵尸进程,然后由父进程进行剩余的清理工作。当父进程没有对子进程进行清理工作的话,子进程就会维持僵尸进程的状态。

过多僵尸进程会 pid 不够用。。以及系统资源会一直被占用。

僵尸进程可以用 ps aux 这个命令来观察。

[root@linux ~]# ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.1   1740   540 ?        S    Jul25   0:01 init [3]
root         2  0.0  0.0      0     0 ?        SN   Jul25   0:00 [ksoftirqd/0]
root         3  0.0  0.0      0     0 ?        S<   Jul25   0:00 [events/0]
.....中間省略.....
root      5881  0.0  0.3   5212  1204 pts/0    S    10:22   0:00 su
root      5882  0.0  0.3   5396  1524 pts/0    S    10:22   0:00 bash
root      6142  0.0  0.2   4488   916 pts/0    R+   11:45   0:00 ps aux

STAT:该程序目前的状态,主要的状态有:
R :该程序目前正在运作,或者是可被运作;
S :该程序目前正在睡眠当中(可说是idle 状态啦!),但可被某些讯号(signal) 唤醒。
T :该程序目前正在侦测或者是停止了;
Z :该程序应该已经终止,但是其父程序却无法正常的终止他,造成zombie (疆尸) 程序的状态

守护进程 (自动孤儿)

在linux里,守护进程其实就是服务对应的 默默的在后台跑着的程序。
一般来说 守护进程没有任何存在的父进程(即PPID=1),成为守护进程的方式是父进程创建完子进程以后,立即退出,由init接管子进程(碰瓷init, init内心也是崩溃的)。上述也叫脱壳。

这里是python 的守护进程的实现:Sander Marechal's code sample is superior to the original

python multiprocessing daemon vs linux daemon

父进程退出,kill所有daemon进程。和linux的守护进程是俩概念。这里的daemon模式 只是普通的子进程,当非守护进程(父进程)退出的时候,daemon进程也会被退出的。在python里,daemonic processes are not allowed to have children。

以下是python设置子进程daemon的例子;

   flush_key_process = Process(target=self.flush_redis_key_gap_time, name='{}_monitor'.format(self.key_name))
   flush_key_process.daemon = True
   flush_key_process.start()

总结: 子进程 vs 父进程

  • 当父进程意外退出时,子进程会如何
    子进程会变成孤儿进程,被init接管,子进程退出后的clean工作也由init进程完成。
  • 当唯一的子进程退出时,父进程会如何
    父进程清理子进程退出后的资源。如果父进程是阻塞等待的话,那么父进程会解除阻塞,继续执行。
  • 当python multiprocessing 里以守护进程运行的时候,父进程退出,子进程会如何
    会被kill。

其他

kill -9 能否在程序中被捕捉,然后执行一些清理操作?

The SIGKILL signal is sent to a process to cause it to terminate immediately (kill). In contrast to SIGTERM and SIGINT, this signal cannot be caught or ignored, and the receiving process cannot perform any clean-up upon receiving this signal.

答案是并不能,原因👆。

程序与资源管理
什麼是 daemon 與服務 (service)
僵尸进程与孤儿进程
multiprocessing.Process.daemon
python sigkill catching strategies
现代操作系统
linux系统编程之进程:父进程查询子进程的退出,wait,waitpid

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,607评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,239评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,960评论 0 355
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,750评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,764评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,604评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,347评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,253评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,702评论 1 315
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,893评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,015评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,734评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,352评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,934评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,052评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,216评论 3 371
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,969评论 2 355

推荐阅读更多精彩内容

  • 进程 操作系统背景知识 顾名思义,进程即正在执行的一个过程。进程是对正在运行程序的一个抽象。 进程的概念起源于操作...
    go以恒阅读 950评论 0 2
  • 一. 操作系统概念 操作系统位于底层硬件与应用软件之间的一层.工作方式: 向下管理硬件,向上提供接口.操作系统进行...
    月亮是我踢弯得阅读 5,971评论 3 28
  • 1.内存的页面置换算法 (1)最佳置换算法(OPT)(理想置换算法):从主存中移出永远不再需要的页面;如无这样的...
    杰伦哎呦哎呦阅读 3,252评论 1 9
  • 又来到了一个老生常谈的问题,应用层软件开发的程序员要不要了解和深入学习操作系统呢? 今天就这个问题开始,来谈谈操...
    tangsl阅读 4,127评论 0 23
  • 听弦断,断那三千痴缠。坠花湮,湮没一朝风涟。花若怜,落在谁指尖…… 一花一世界,一叶一追寻。一曲一场叹,一生为一人!
    无音额阅读 1,308评论 5 13