美文网首页
Linux学习-进程管理与调度(二)-进程的创建与销毁

Linux学习-进程管理与调度(二)-进程的创建与销毁

作者: Stan_Z | 来源:发表于2019-07-28 09:16 被阅读0次

    一、进程与线程的创建

    Linux创建进程方式
    fork():以复制的方式创建子进程,然后直接把资源复制给新创建的进程。Linux的fork()使用copy-on-write实现,即copy本身两个进程是共享一份地址拷贝,只有在需要写时,资源才会被复制,让两个进程拥有各自的拷贝。
    fork过程复制资源包括代码段,数据段,堆,栈。fork调用者所在进程便是父进程,新创建的进程便是子进程;在fork调用结束,从内核返回两次,一次继续执行父进程,一次进入执行子进程。父进程fork返回值子进程的pid,子进程返回值是0 。

    copy-on-write

    vfork():在无mmu 的cpu里面是不可能执行copy-on-write的,即没有fork ,所以使用vfork,vfork有个特点是资源复制中内存资源是共享的,其他资源对拷一份。

    Linux创建线程的方式
    pthread_create():用户线程创建。
    kthread_create():内核线程创建。

    父进程与复制的进程完全共享他们的系统资源,这就是线程的实现。

    内核线程:没有独立的地址空间,即mm指向NULL。这样的线程只在内核运行,不会切换到用户空间。 所有内核线程都是由kthreadd作为内核线程的祖师爷,衍生而来的。

    总结一张fork、vfork、pthread_create的进程复制系统资源copy操作对比图:

    fork、vfork、pthread_create对比

    对于用户空间来说,线程是进程内部的一个执行路径,它们共享进程的系统资源,与进程的pid也保持一致,只是tid不同。但是在内核空间来看,每一个线程其实也是一个单独的进程,只是共享父进程的系统资源而已,问题来了,既然是单独的进程,那么每个线程应该都得有自己唯一的pid,那么为什么用户空间getPid是相同的呢?

    posix标准要求:一个进程有多个线程,向上要看起来像一个整体,即getPid要获取到相同的pid。
    Linux障眼法:创造了一个TGID,让又p1 fork出来的线程类型的进程TGID与父进程保持一致,getPid实际上获取的是TGID。

    通过命令来查看:
    top 是进程视角,查看的id实际上是各个进程(线程)的TGID
    top -H 是线程视角,查看的是各个线程的自己独有的id即PID

    二、进程销毁

    进程销毁通过系统调用exit(),大部分任务都靠do_exit()来完成,总结起来无非就是重置标志位、释放系统资源、以及给子进程找养父等操作。

    这里所谓的给子进程找养父是这么一个场景:
    如果父进程在子进程之前退出,必须有机制来保证子进程能找到新的父进程,否则这些成为孤儿的进程会再退出时没有父进程来收尸,永远处于僵尸状态而白白耗费内存。

    托孤机制
    给子进程在找一个subreaper作为父进程,如果找不到,就让init(pid=1)成为他的父进程。

    subreaper进程:

    subreaper进程实现 托孤参考

    如果干掉p2和p4,那么p3只能托孤给init,而p5则托孤给subreaper。(pstree命令可看树状结构)

    总结:
    Linux进程都是子死父清场,如果父死,子未死,那就托孤。如果子死,父未死但是也不清场,那么子就一直是僵尸。

    参考:
    宋宝华Linux的进程、线程以及调度
    《 Linux内核设计与实现》

    相关文章

      网友评论

          本文标题:Linux学习-进程管理与调度(二)-进程的创建与销毁

          本文链接:https://www.haomeiwen.com/subject/onnerctx.html