美文网首页
Linux内核设计与实现 进程管理1: 基本数据结构

Linux内核设计与实现 进程管理1: 基本数据结构

作者: 虹桥过夜好无聊 | 来源:发表于2017-12-21 00:09 被阅读0次

    进城描述符及基本数据结构 

            内核把各个进程存储在一个双向链表(图3-1)之中。在该链表里,每个进程由进程描述符(process descriptor)代表。进程描述符与进程一一对应。一个进程描述符实际上就是一个数据结构,其原型存储在<linux/sched.h>中,声明为:struct task_struct{};其包含了足以描述特定进程的全部信息,例如:打开文件, 进程地址空间, 等待信号队列和进程状态等等。

    进程描述符构成的双向链表

            因为内核会为每个进程同时在内核空间用户空间分配一定的内存。通常在内核空间分配的大小为两个页大小(4KB ~ 8KB),且是固定不变的。而上述的 task_struct 结构则存储在每个进程的内核栈的末端。提供该结构的好处是便于寻址,操作系统不必使用额外的寄存器去存储进城描述符的内存地址,而简单地使用栈指针即可。关于task_struct的详解内容,可阅读Linux进程描述符task_struct结构体详解--Linux进程的管理与调度(一)这篇博客。

            除了该数据结构以外,Linux系统还在<asm/thread_info>中定义了一个新的数据结构:struct thread_info,用于进程管理。当进城运行时,这个数据结构存储在内核栈的下方,代码如下:

    struct thread_info;

            那么在源代码中,内核是如何表示内核栈的呢?在<linux/sched.h> 定义了一个联合:

    union thread_union;

            这个联合就是代码级的内核栈表示方式。值得注意的是,因为联合的特性,thread_info 与 stack 两个成员分配在同一个内存区段中,之间并无明显的界限。通常来说,内核栈中的数据不会太多,但是如果用户的行为或是某些恶意行为,导致对进程内核栈的利用率过高,最终将会覆盖thread_info中的数据,将导致不可预知的后果。以下图片仅供参考。

    内核栈布局

            Q: 那么 task_struct 存在于内存中的什么地方呢?在内核栈还是用户地址空间?

    进程描述符内容

          PID

          操作系统为每个进程都分配了一个独一无二的进程ID,类似于人类的身份证。在内核中这个ID被称为PID,数据类型为pid_t,通常情况下等同于int。但为了兼容以往的Unix和Linux版本,PID的最大值为32768,相当于short int。也就是说系统中的进程数最多为32768。当然你也可以通过修改/proc/sys/kernel/pid_max来修改这个值,不过也许会带来兼容性问题。

          Current

          有时候内核需要快速地寻找到当前进程的task_struct结构,已完成许多工作。所以提供了一个宏(macro)叫做current,这个宏在任何运行Linux的体系上都应被实现。

          在x86机器上,current宏通过计算栈指针的13个标志位去获得thread_info结构。进行实际寻找工作的是current_thread_info()函数:

                movl  $-8192,%eax

                andl  %esp, %eax

          转而再得到task_struct:

                  current_thread_info()->task;

    进程状态

            Linux为每个进程提供了五种状态,也就是说进程必须为五种状态之一。

            TASK_RUNNING:    在这种状态下的进程是可运行的,不是正在运行中,就是处于进程调度的运行队列里等待调度。如果一个进程在用户空间中运行,那么该状态是它唯一可能处于的状态。当然,对于在内核空间运行的进程,也可能处于该状态。

            TASK_INTERRUPTIBLE:  进程处于睡眠中,也可以说正在阻塞(Block),并且是可唤醒的,当其接收到一个恰到的信号就回切换到TASK_RUNNING状态。

            TASK_UNINTERRUPTIBLE:    与TASK_INTERRUPTIBLE类似,但区别在于不可被信号唤醒。该状态被应用于一个进程试图不被中断地等待某一事件的发生。 ???

            TASK_ZOMBLE:    进程已经终止,但其父进程没有调用wait4()系统调用去获取其相关信息。所以内核将保留其进程标识符与内核空间中,直到父进程做出一定动作。这也是我们俗称的僵尸进程

            TASK_STOPPED:    进程的执行停止了,既不执行也不适合去执行。(???) 当进程接收到SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOUT信号时就会陷入这状态。(??? 回去查阅一下信号章节)

    进程状态间转换

    相关文章

      网友评论

          本文标题:Linux内核设计与实现 进程管理1: 基本数据结构

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