一、进程描述符及任务结构:
1、内核把进程的列表放在叫做任务队列(task_list)的双向循环链表中
2、链表中的每一项都是类型为task_struct,称为进程描述符的结构
该结构定义在<linux/sched.h>中
进程描述符包含一个具体进程的所有信息:打开的文件,进程的地址空间,挂起的信号,进程的状态等
3、内核通过一个唯一的进程标识值或PID来标识每个进程,并把PID存放在各自的进程描述符中
二、进程描述符的分配:
1、linux通过slab分配器分配task_struct结构
2、通过预先分配和重复使用task_struct,可以避免动态分配和释放所带来的资源消耗,达到迅速创建进程的目的
3、每个任务的thread_info结构在它的内核栈的尾端分配,结构中的task域存放指向该任务实际task_struct的指针
三、进程描述符的存放:
内核中大部分处理进程的代码都是直接通过task_struct进行的,因此,通过current宏查找当前正在运行进程的进程描述符的速度尤为重要。
第一种:用一个专门的寄存器来存放指向当前进程task_struct的指针,用于加快访问速度
如:在PowerPC上,current宏只需把r2寄存器中的值返回就行了
第二种:在内核中尾端创建thread_info结构,通过计算偏移间接地查找task_struct结构(一些像x86这样的体系结构,其寄存器并不富余)
如:在x86系统上,current把栈指针的后13个有效位屏蔽掉,用来计算出thread_info的偏移。
该操作通过current_thread_info()函数来完成
四、进程状态:
进程描述符中的state描述了进程的当前状态
状态设置:set_task_state(task, state); 等价于 task->state = state;
五种状态:
1、TASK_RUNNING(运行)
进程可执行;
或正在执行,或在运行队列中等待执行;
是进程在用户空间中执行的唯一可能状态;
可以应用到内核空间中正在执行的进程;
2、TASK_INTERRUPTIBLE(可中断)
进程正在睡眠(阻塞),等待某些条件的达成;
一旦条件达成,状态转为运行;
也会因为接收到信号而提前被唤醒并随时准备投入运行;
3、TASK_UNINTERRUPTIBLE(不可中断)
就算接收到信号也不会被唤醒或准备投入运行;
在进程等待时不受干扰或等待事件很快就会发生时出现;
使用得较少;
4、_TASK_TRACED
被其他进程跟踪的进程
5、_TASK_STOPPED(停止)
进程停止执行,既没有投入运行也没不能投入运行;
发生在接收到SIGSTOP、SIGTSTP、SIGTTIN、SIGTTOU等信号的时候;
此外,在调试期间接收到任何信号,也会进入此状态;
![](https://i-blog.csdnimg.cn/blog_migrate/556371989c9390b165c3951323210a5d.jpeg)
五、进程上下文和家族树:
当一个程序执行了系统调用或者触发了某个异常,它就陷入了内核,此时称内核“代表进程执行”
并处于进程上下文中。
每个进程必有一个父进程,有零个或多个子进程。
拥有同一个父进程的所有进程称为兄弟。
每个task_struct都包含一个指向其父进程task_struct,叫做parent的指针,还包含一个称为children的子进程链表。