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

一、进程与线程的创建

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

Linux学习-进程管理与调度(二)-进程的创建与销毁_第1张图片
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操作对比图:

Linux学习-进程管理与调度(二)-进程的创建与销毁_第2张图片
fork、vfork、pthread_create对比

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

Linux学习-进程管理与调度(二)-进程的创建与销毁_第3张图片

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进程:

Linux学习-进程管理与调度(二)-进程的创建与销毁_第4张图片
subreaper进程实现
Linux学习-进程管理与调度(二)-进程的创建与销毁_第5张图片
托孤参考

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

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

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

你可能感兴趣的:(Linux学习-进程管理与调度(二)-进程的创建与销毁)