Linux内核源代码导读 中国科学技术大学计算机系 陈香兰(0551-3606864) xlanchen@ustc.edu.cn http://staff.ustc.edu.cn/~xlanchen Spring 2009
进程(任务管理)
主要内容 进程描述符 进程切换 进程的创建和删除 2019/4/8 Linux内核源代码导读
进程的概念 进程是执行程序的一个实例 进程和程序的区别 几个进程可以并发的执行一个程序 一个进程可以顺序的执行几个程序 2019/4/8 Linux内核源代码导读
进程描述符 为了管理进程,内核必须对每个进程进行清晰的描述。 进程描述符提供了内核所需了解的进程信息 源码include/linux/sched.h定义 struct task_struct 数据结构很庞大 2019/4/8 Linux内核源代码导读
stack 2019/4/8 Linux内核源代码导读
Linux2.6进程的状态 简单过一下,与状态相关的一些宏 1)组合状态 2)状态判断 3)状态设置 2019/4/8
进程状态转换图 EXIT_ZOMBIE或者 EXIT_DEAD 或者 TASK_DEAD 2019/4/8 Linux内核源代码导读
标识一个进程 使用进程描述符地址 使用PID (Process ID,PID) 进程和进程描述符之间有非常严格的一一对应关系,使得用32位进程描述符地址标识进程非常方便 使用PID (Process ID,PID) 每个进程的PID都存放在进程描述符的pid域中 2019/4/8 Linux内核源代码导读
进程和进程的内核堆栈 Linux为每个进程分配一个8KB大小的内存区域,用于存放该进程两个不同的数据结构: Thread_info 进程处于内核态时使用 不同于用户态堆栈 内核控制路径所用的堆栈 很少,因此对栈和描述符 来说,8KB足够了 Thread_info 2019/4/8 Linux内核源代码导读
Thread_union C语言允许用如下的一个union结构来方便的表示这样的一个混合体 2019/4/8 Linux内核源代码导读
Thread_info的分配/回收/访问 进程描述符的分配/回收/访问 Thread_info的分配/回收/访问 alloc_thread_info free_thread_info 2019/4/8 Linux内核源代码导读
2019/4/8 Linux内核源代码导读
从当前内核堆栈获得当前thread_info 根据thread_info描述符和内核态堆栈之间的配对,内核可以很容易的从esp寄存器的值获得当前在CPU上运行的进程的描述符指针 因为这个内存区是8KB=213大小,内核必须做的就是让esp有13位的有效位,以获得thread_info的基地址 2019/4/8 Linux内核源代码导读
current宏进程描述符 2019/4/8 Linux内核源代码导读
把per_cpu__current_task, 取出返回 需要找到该参数的赋值之处!! 2019/4/8 Linux内核源代码导读
考虑对应的x86_write_percpu的使用情况 在__switch_to中被调用 2019/4/8 Linux内核源代码导读
Current宏的使用 Current宏可以看成当前进程的进程描述符指针,在内核中直接使用 比如current->pid返回在CPU上正在执行的进程的PID 2019/4/8 Linux内核源代码导读
进程的PID 进程的pid字段 Pid最大值 2019/4/8 Linux内核源代码导读
Pid的管理和分配 创建一个进程时, Pid名字空间 do_forkcopy_processalloc_pid Struct pid的cache 2019/4/8 Linux内核源代码导读
Pid位图 Pid数据结构 对pid名字空间: 2.6内核为PID专门引入了一个数据结构,Why? 独立的进程;进程组;sessions 考虑进程的删除和创建 2019/4/8 Linux内核源代码导读
最初的pid名字空间 在start_kernel中,调用pidmap_init进行合理的初始化 在kernel_init中,被修改为init进程 2019/4/8 Linux内核源代码导读
分配第一个位图页 初始化struct pid的cache 2019/4/8 Linux内核源代码导读
2.6内核为PID专门引入了一个数据结构 Why? 独立的进程;进程组;sessions 使用pid数字的注意之处 考虑进程的删除和创建 2019/4/8 Linux内核源代码导读
阅读alloc_pid、 alloc_pidmap函数 2019/4/8 Linux内核源代码导读
进程链表 为了对给定类型的进程(比如所有在可运行状态下的进程)进行有效的搜索,内核维护了几个进程链表 所有进程链表 在进程描述符中: 2019/4/8 Linux内核源代码导读
进程链表中的插入和删除 使用常规list数据结构操作 2019/4/8 Linux内核源代码导读
list_add list_add_tail list_del list_move list_empty list_for_each list_for_each_prev list_for_each_safe list_for_each_entry … 2019/4/8 Linux内核源代码导读
例如,在do_fork调用的copy_process中 for_each_process宏扫描整个进程链表 RCU read copy update 2019/4/8 Linux内核源代码导读
const struct sched_class,调度类 TASK_RUNNING状态的进程组织 对可运行队列的一些操作函数 底层:常规的list数据结构操作 入列出列等操作: dequeue_task enqueue_task const struct sched_class,调度类 rt_sched_class fair_sched_class idle_sched_class 每个cpu有一个运行队列 关于调度的描述, 参见sched_coding.txt和 sched-design-CFS.txt 2019/4/8 Linux内核源代码导读
运行队列数据结构 2019/4/8 Linux内核源代码导读
struct cfs_rq 红黑树 2019/4/8 Linux内核源代码导读 ……
struct rt_rq 基于优先级的运行队列 …… 2019/4/8 Linux内核源代码导读
2019/4/8 Linux内核源代码导读
2019/4/8 Linux内核源代码导读
调度类 阅读调度类sched_class的定义源码 找到主要与运行队列有关的 Idle相关:idle_sched_class Fair相关 enqueue_task、dequeue_task Idle相关:idle_sched_class no enqueue/yield_task for idle tasks dequeue_task_idle Fair相关 enqueue_task_fair dequeue_task_fair Rt相关 enqueue_task_rt dequeue_task_rt 2019/4/8 Linux内核源代码导读
Idle类特殊 2019/4/8 Linux内核源代码导读
Fair类 进而查看 1)enqueue_entity 2)__enqueue_entity (红黑树) 3)sched_entity结构 4) struct rq 5)struct cfs_rq Completely Fair Scheduler 完全公平调度 2019/4/8 Linux内核源代码导读
Rt类 进而查看: 1)enqueue_rt_entity 2)__enqueue_rt_entity 每个cpu有一个队列 3)sched_rt_entity 4)struct rq 5)struct rt_rq 6) struct rt_prio_array 2019/4/8 Linux内核源代码导读 优先级队列
激活一个任务 activate_task 相对的:deactivate_task 2019/4/8 Linux内核源代码导读
pidhash表及链接表 在一些情况下,内核必须能从进程的PID得出对应的进程描述符指针。例如kill系统调用 初始化:pidhash_init Task_struct中: 2019/4/8 Linux内核源代码导读
pidhash表及链接表 2019/4/8 Linux内核源代码导读
进程之间的亲属关系 程序创建的进程具有父子关系,在编程时往往需要引用这样的父子关系。进程描述符中有几个域用来表示这样的关系 2019/4/8 Linux内核源代码导读
等待队列 当要把除了TASK_RUNNING状态之外的进程组织在一起时,linux使用了等待队列 TASK_INTERRUPTIBLE和TASK_UNINTERRUPTIBLE状态的进程再分成很多类,每一类对应一个特定的事件。在这种情况下,进程状态提供的信息满足不了快速检索,因此,内核引进了另外的进程链表,叫做等待队列 等待队列在内核中有很多用途,尤其是对中断处理、进程同步和定时用处很大 2019/4/8 Linux内核源代码导读
等待队列使得进程可以在事件上的条件等待,并且当等待的条件为真时,由内核唤醒它们 等待队列由循环链表实现 阅读相关的宏 2019/4/8 Linux内核源代码导读
在等待队列上内核实现了一些操作函数 add_wait_queue add_wait_queue_exclusive remove_wait_queue 2019/4/8 Linux内核源代码导读
进程等待 等待一个特定事件的进程能调用下面几个函数中的任一个 进程等待由需要等待的进程自己进行(调用) sleep_on sleep_on_timeout interruptible_sleep_on interruptible_sleep_on_timeout 进程等待由需要等待的进程自己进行(调用) 2019/4/8 Linux内核源代码导读
sleep_on 相当于 阅读实际的sleep_on代码 2019/4/8 Linux内核源代码导读
此外,还可能按照如下方式进行sleep 2019/4/8 Linux内核源代码导读
例如事件等待wait_event__wait_event 等待,直到事件发生(有效,或…) 2019/4/8 Linux内核源代码导读
进程的唤醒 利用wake_up或者wake_up_interruptible等一系列的宏,都让插入等待队列中的进程进入TASK_RUNNING状态 2019/4/8 Linux内核源代码导读
__wake_up __wake_up_common 间接 default_wake_function try_to_wake_up activate_task 2019/4/8 Linux内核源代码导读
进程切换(process switching) 为了控制进程的执行,内核必须有能力挂起正在CPU上执行的进程,并恢复以前挂起的某个进程的执行,这叫做进程切换,任务切换,上下文切换 2019/4/8 Linux内核源代码导读
进程上下文 包含了进程执行需要的所有信息 用户地址空间 包括程序代码,数据,用户堆栈等 控制信息 进程描述符,内核堆栈等 硬件上下文 2019/4/8 Linux内核源代码导读
尽管每个进程可以有自己的地址空间,但所有的进程只能共享CPU的寄存器。 硬件上下文 尽管每个进程可以有自己的地址空间,但所有的进程只能共享CPU的寄存器。 因此,在恢复一个进程执行之前,内核必须确保每个寄存器装入了挂起进程时的值。这样才能正确的恢复一个进程的执行 硬件上下文: 进程恢复执行前必须装入寄存器的一组数据 包括通用寄存器的值以及一些系统寄存器 通用寄存器 系统寄存器 2019/4/8 Linux内核源代码导读
在linux中 一个进程的上下文主要保存在thread_info, task_struct的thread_struct中,其他信息放在内核态堆栈中 2019/4/8 Linux内核源代码导读
thread_info 2019/4/8 Linux内核源代码导读
Thread_struct …… 2019/4/8 Linux内核源代码导读
Pt_regs 2019/4/8 Linux内核源代码导读
上下文切换 switch_to宏执行进程切换,schedule()函数通过调用context_switch,间接调用这个宏一调度一个新的进程在CPU上运行 switch_to利用了prev和next两个参数: prev:指向当前进程 next:指向被调度的进程 2019/4/8 Linux内核源代码导读
进程切换的关键语句 堆栈的切换 从此,内核对next的内核态堆栈操作,因此,这条指令执行从prev到next真正的上下文切换,因为thread_info和内核态堆栈紧密联系在一起,改变内核态堆栈就意味改变当前thread_info 2019/4/8 Linux内核源代码导读
什么时候next进程真正开始执行呢? 2019/4/8 Linux内核源代码导读
?哪里切换了进程的地址空间 2019/4/8 Linux内核源代码导读