进程管理
3.1进程
进程就是处于执行期的程序(目标码存放在某种存储介质上)
但进程并不仅仅局限于一段可执行程序代码。
执行线程,线程,是进程活动的对象。
两种虚拟机制:虚拟处理器和虚拟内存。
最终,程序通过exit()系统调用退出执行。
3.2进程描述及任务结构
内核把进程的列表存放在叫做任务队列的双向循环链表中。
Task_struct相对较大,在32位机器上,它大约有1.7KB。
3.2.1分配进程描述符
结构中task域中存放的是指向该任务实际task_struct的指针。
3.2.2 进程描述符的存放
内核通过一个唯一的进程标识值或PID来标识每个进程。
这个最大值很重要,因为它实际上就是系统中允许同时存在的进程的最大数目
3.2.3 进程状态
系统中的进程都必然处于五个进程中状态中的一种。
TASK_RUNNING(运行)——进程是可执行的。
TASK_INTERRUPTIBLE(可中断)——进程正在睡眠(也就是说它被阻塞)。
TASK_UNINTERRUPTIBLE(不可中断)——使用的较少。
_TASK_TRACED——被其他进程跟踪的进程。
_TASK_STOPPED(停止)——进程停止执行。
3.2.4 设置当前进程状态
内核经常需要调整某个进程的状态。
参看<linux/sched.h>中对这些相关函数实现的说明。
3.2.5进程上下文
系统调用和异常处理程序是对内核明确定义的接口。
3.2.6进程家族树
Unix系统的进程之间存在明显的继承关系。
Linux系统也是如此
特别提醒:
在一个拥有大量进程的系统中通过重复来遍历所有的进程的代价是很大的。
因此,没有足够的理由,不要这样做。
3.3进程创建
Unix的进程创建很不同
分解到两个单独的函数中去执行:
Fork()
Exec()
3.3.1写时拷贝
传统fork()系统调用直接把所有的资源复制给新创建的进程。
Fork的实际开销就是复制父进程的页表
给子进程创建唯一的进程描述符,这个优化很重要。
3.3.2fork()
1.fork()、vfork()、__clone()都根据各自需要的参数标志调用clone()。
2.由clone()去调用do_fork()。
3.do_fork()调用copy_process()函数,然后让进程开始运行。
4.返回do_fork()函数,如果copy_process()函数成功返回,新创建的子进程被唤醒并让其投入运行。
3.3.3vfork()
1.调用copy_process()是,task_struct的vfor_done成员被设置为NULL。
2.执行do_fork()时,如果给定特定标志,则vfor_done会指向一个特定地址。
3.子进程先开始执行后,父进程不是马上恢复执行,而是一直等待,知道子进程通过vfor_done指针向它发送信号。
4.在调用mm_release()时,该函数用于进程退出内存地址空间,并且检查vfor_done是否为空,如果不为空,则会向父进程发送信号。
5.回到do_fork(),父进程醒来并返回。
3.4线程在linux中实现
3.4.1创建线程
3.4.2内核线程
内核线程没有独立的地址空间,只在内核空间运行,从来不切换到用户空间,可以被调度和被抢占。
3.5进程终结
1.删除进程描述符
2孤儿进程造成进退维谷