Linux下的进程管理
进程与线程概念
进程:可执行代码、数据、地址空间、信号,打开的文件
进程描述符与task_struct循环队列
描述进程的数据结构task_struct和thread_info
task_struct可以表征一个进程,称其为进程描述符,内核中用它来组成循环队列实现进程的调度与管理
task_struct的分配、存储与查找
task_struct由slab分配,一般通过一个指针来存储task_struct的地址,不同架构的CPU处理方式不同,PPC结构用一个寄存器来存储,X86存储在thread_info结构中,所以task_struct的查找也与体系结构有关
进程状态与状态修改
状态
TASK_RUNNING(正在运行、就绪可以运行)
TASK_INTERRUPTIBLE
TASK_UNINTERRUPTIBLE
TASK_ZOMBLE
TASK_STOPPED
设置状态
set_task_state(task, state)’
进程状态机与状态切换
进程间的关系(进程家族树)
由task_struct维护
task_struct task_struct
{
.......
task_struct *parent;//父进程
list * child_list;//子进程列表
......
}
进程创建与写时拷贝(fork)
进程创建过程
fork()
创建内核栈、thread_info、task_struct,此时这三个数据结构和父进程完全相同
通过修改task_struct以区分父进程
设置标志保证子进程处于非运行态
get_pid()得到有效PID
与父进程共享剩余时间片
do_fork()返回子进程的指针
如果do_fork()成功返回,子进程被唤醒执行
进程与子进程的区别(PID/PPID/资源统计量)
写时拷贝fork()
新创建的进程和父进程共享资源,都以只读方式访问,只有在需要写入的时候才会copy,这样会避免资源占用
具体通过clone系统调用实现
Linux线程的实现
线程与进程
Linux的线程进而进程差不多,进程和线程最终创建都是通过clone()系统调用实现的,区别只是创建进程和线程时给clone传递的标志不同
内核线程的创建
进程退出do_exit()
do_exit()完成以下任务
设置task_struct的标志变量为PF_EXTTING
删除del_time/exit_mm/exit_sem、files/fs的引用计数
exit_notify通知父进程同时将自己设置成TASK_ZOMBLE
调用shedule()切换到其他进程执行
do_exit完成后,线程所占用的资源大多被释放,但仍然占有内核栈、thread_info和task_struct
进程描述符task_struct的删除
wait函数
挂起调用wait的进程,知道有一个子进程退出,退出时候返回子进程的PID
release_task函数
进程退出时,保留task_struct的原因:在进程退出后仍然可以让父进程获取他的信息,所以进程的终结是的清理工作和和删除进程描述符分开进行,task_struct删除具体步骤
减少进程用户的的进程数
回收PID
删除进程
在ptrace上删除对该进程的跟踪
删除内核栈和thread_info所占的的页,删除task_struct所占的slab高速缓存
孤儿进程
产生
父进程在子进程结束之前退出,造成子进程的资源没人回收,一直处于TASK_ZOMBLE状态,占用资源
Linux解决措施
为兄弟进程重新寻找父进程,找不到则以init进程昨晚所有兄弟进程的父进程,父进程调用wait函数回收资源,清楚TASK_ZOMNLE进程
Linux进程调度
调度概念
从一组就绪态(TASK_RUNNING)的进程中根据某种算法选择一个,并计算出执行时间让其执行,就是调度程序的任务
抢占式OS与非抢占式OS
抢占式:进程用完时间片之后,让出资源,调度程序调度另一个任务执行,该过程称之为抢占,相当于另一个任务抢占了当前任务的执行,当前多数OS都是抢占式OS
非抢占式:除非进程自己主动让出资源,否则进程将一直执行下去直到结束,造成的影响是一个任务占用资源时间过长,导致其他任务无法执行,最糟糕的情况是一个绝不让步的悬挂进程可能造成整个系统的崩溃