Download presentation
Presentation is loading. Please wait.
1
中国科学技术大学计算机系 陈香兰(0512-87161312) xlanchen@ustc.edu.cn Sprint 2011
Linux操作系统分析 中国科学技术大学计算机系 陈香兰(0512- ) Sprint 2011
2
Linux的进程
3
主要内容 进程描述符 进程切换 进程的创建和删除 进程调度 2019/1/14 Linux操作系统分析
4
主要内容 进程描述符 进程切换 进程的创建和删除 进程调度 2019/1/14 Linux操作系统分析
5
进程和线程 多道程序对操作系统的需求进程 进一步提高并发度,对操作系统的需求 线程 进程是执行程序的一个实例 进程和程序的区别
进一步提高并发度,对操作系统的需求 线程 进程是执行程序的一个实例 进程和程序的区别 几个进程可以并发的执行一个程序 一个进程可以顺序的执行几个程序 线程和进程的区别 Linux 2.4内核以及之前的版本都不支持线程 2.6内核中有thread,但仍不是线程 Linux中的线程是在用户态实现的,不是本课程的内容 但Linux内核对用户态线程有一定的辅助支持 2019/1/14 Linux操作系统分析
6
进程描述符 为了管理进程,内核必须对每个进程进行清晰的 描述。 进程描述符提供了内核所需了解的进程信息
源码include/linux/sched.h定义 struct task_struct 数据结构很庞大 基本信息 管理信息 控制信息 2019/1/14 Linux操作系统分析 6
7
2019/1/14 Linux操作系统分析
8
Linux2.6进程的状态 include/linux/sched.h 简单过一下,与状态相关的一些宏 1)组合状态 2)状态判断
3)状态设置 2019/1/14 Linux操作系统分析 8
9
进程状态转换图 EXIT_ZOMBIE或者 EXIT_DEAD 或者 TASK_DEAD 2019/1/14 Linux操作系统分析 9
10
标识一个进程 使用进程描述符地址 使用PID (Process ID,PID)
进程和进程描述符之间有非常严格的一一对应关系, 使得用32位进程描述符地址标识进程非常方便 使用PID (Process ID,PID) 每个进程的PID都存放在进程描述符的pid域中 2019/1/14 Linux操作系统分析
11
进程的PID 进程的pid字段 include/linux/types.h include/asm-XXX/posix_typesYYY.h
Pid最大值,参见kernel/pid.c XXX为体系结构名称 YYY为空,或者_64或者_32 顺序使用 && 循环使用 include/linux/threads.h 2019/1/14 Linux操作系统分析 11 11
12
stuct pid 阅读include/linux/pid.h中 “What is struct pid?” 2019/1/14
12
13
Pid的管理和分配 创建一个进程时, Pid名字空间 Pid位图 do_forkcopy_processalloc_pid
Struct pid的cache 关于名字空间的更多信息,参见: Nsproxy.c以及pid_namespace.ch 2019/1/14 Linux操作系统分析 13
14
Init_pid_ns 在start_kernel中,调用pidmap_init进行合理的初始化
在kernel_init中,被修改为init进程 2019/1/14 Linux操作系统分析 14
15
分配第一个位图页 初始化struct pid的cache 2019/1/14 Linux操作系统分析 15
16
阅读alloc_pid、 alloc_pidmap函数 掌握基于位图的pid分配方法
last_pid+1,或者下一个bit0,或者一个新的map的第 一个bit或者从头开始(300以后) kernel/pid.c kernel/pid.c 2019/1/14 Linux操作系统分析 16
17
用户如何获得一个进程的pid 系统调用getpid 关于进程组 单独一个进程可以看成只有一个进程的组 getpid返回组pid 使用组链表
数据:tgid 单独一个进程可以看成只有一个进程的组 getpid返回组pid 2019/1/14 Linux操作系统分析
18
进程和进程的内核堆栈 Linux为每个进程分配一个8KB大小的内存区域 ,用于存放该进程两个不同的数据结构:
Thread_info 进程的内核堆栈 进程处于内核态时使用, 不同于用户态堆栈 内核控制路径所用的堆栈 很少,因此对栈和Thread_info 来说,8KB足够了 Thread_info 2019/1/14 Linux操作系统分析
19
Thread_union C语言允许用如下的一个union结构来方便的表示 这样的一个混合体 include/linux/sched.h
thread_info由体系结构相关部分定义 阅读include/asm-x86/thread_info.h 以及include/asm-x86/thread_info_32.h 2019/1/14 Linux操作系统分析
20
Thread_info的分配/回收/访问
进程描述符的分配/回收/访问 Thread_info的分配/回收/访问 alloc_thread_info free_thread_info 2019/1/14 Linux操作系统分析
21
2019/1/14 Linux操作系统分析
22
current_thread_info 从刚才看到的thread_info和内核态堆栈之间的配 对,内核可以很容易的从esp寄存器的值获得当 前在CPU上运行的进程的描述符指针 因为这个内存区是8KB=213大小,内核必须做的 就是让esp有13位的有效位,以获得进程描述符 的基地址 8191=8192-1=0x2000-1=0x1fff 取反:0xffffe000(最后13位为0) 2019/1/14 Linux操作系统分析
23
Current宏 2019/1/14 Linux操作系统分析
24
Current宏的使用 Current宏可以看成当前进程的进程描述符指针 ,在内核中直接使用 举例:
比如current->pid返回在CPU上正在执行的进程的PID 2019/1/14 Linux操作系统分析
25
进程链表 为了对给定类型的进程(比如所有在可运行状态 下的进程)进行有效的搜索,内核维护了几个进 程链表 所有进程链表 在进程描述符中:
2019/1/14 Linux操作系统分析
26
进程链表中的插入和删除 使用常规list数据结构操作 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 … 进程链表中的插入和删除 使用常规list数据结构操作 参见include/linux/list.h或者lib/list_debug.c 2019/1/14 Linux操作系统分析
27
例如,在do_fork调用的copy_process中
for_each_process宏扫描整个进程链表 RCU read copy update 2019/1/14 Linux操作系统分析
28
const struct sched_class,调度类
TASK_RUNNING状态的进程组织 入列/出列等操作: 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/1/14 Linux操作系统分析
29
运行队列数据结构 2019/1/14 Linux操作系统分析
30
struct cfs_rq 红黑树 2019/1/14 Linux操作系统分析 ……
31
struct rt_rq 基于优先级的运行队列 …… 2019/1/14 Linux操作系统分析
32
2019/1/14 Linux操作系统分析
33
2019/1/14 Linux操作系统分析
34
调度类 阅读调度类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/1/14 Linux操作系统分析
35
Idle类特殊 2019/1/14 Linux操作系统分析
36
Fair类 进而查看 1)enqueue_entity 2)__enqueue_entity (红黑树) 3)sched_entity结构
4) struct rq 5)struct cfs_rq Completely Fair Scheduler 完全公平调度 2019/1/14 Linux操作系统分析
37
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/1/14 Linux操作系统分析 优先级队列
38
激活一个任务 activate_task 相对的:deactivate_task 2019/1/14 Linux操作系统分析
39
pidhash表及链接表 在一些情况下,内核必须能从进程的PID得出对 应的进程描述符指针。例如kill系统调用
初始化:pidhash_init Hash函数的使用情况 2019/1/14 Linux操作系统分析
40
可以从进程描述符得到进程的pid相关信息 Task_struct中:
2019/1/14 Linux操作系统分析
41
Pid数据结构 2.6内核为PID专门引入了一个数据结构,Why? 独立的进程;进程组;sessions 使用pid数字的注意之处
考虑进程的删除和创建 2019/1/14 Linux操作系统分析
42
pidhash表及链接表 2019/1/14 Linux操作系统分析
43
进程之间的亲属关系 程序创建的进程具有父子关系,在编程时往往需 要引用这样的父子关系。进程描述符中有几个域 用来表示这样的关系
2019/1/14 Linux操作系统分析
44
等待队列 当要把除了TASK_RUNNING状态之外的进程组织在一 起时,linux使用了等待队列
TASK_INTERRUPTIBLE和TASK_UNINTERRUPTIBLE状态的 进程再分成很多类,每一类对应一个特定的事件。在这种情况 下,进程状态提供的信息满足不了快速检索,因此,内核引进 了另外的进程链表,叫做等待队列 等待队列在内核中有很多用途,尤其是对中断处理、进 程同步和定时用处很大 2019/1/14 Linux操作系统分析
45
等待队列使得进程可以在事件的条件等待,并且 当等待的条件为真时,由内核唤醒它们 等待队列由循环链表实现
阅读相关的宏 2019/1/14 Linux操作系统分析
46
等待队列的链表 2019/1/14 Linux操作系统分析
47
在等待队列上内核实现了一些操作函数 add_wait_queue add_wait_queue_exclusive
remove_wait_queue 2019/1/14 Linux操作系统分析
48
进程等待 等待一个特定事件的进程能调用下面几个函数中 的任一个 进程等待由需要等待的进程自己进行(调用) sleep_on
sleep_on_timeout interruptible_sleep_on interruptible_sleep_on_timeout 进程等待由需要等待的进程自己进行(调用) 2019/1/14 Linux操作系统分析
49
sleep_on 相当于 阅读实际的sleep_on代码 2019/1/14 Linux操作系统分析
50
此外,还可能按照如下方式进行sleep 2019/1/14 Linux操作系统分析
51
例如事件等待wait_event__wait_event
等待,直到事件发生(有效,或…) 2019/1/14 Linux操作系统分析
52
进程的唤醒 利用wake_up或者wake_up_interruptible等一系列 的宏,都让插入等待队列中的进程进入 TASK_RUNNING状态 2019/1/14 Linux操作系统分析
53
__wake_up __wake_up_common 间接 default_wake_function try_to_wake_up
activate_task 2019/1/14 Linux操作系统分析
54
/proc文件系统中的进程子目录 以进程的PID为目录名 2019/1/14 Linux操作系统分析
55
主要内容 进程描述符 进程切换 进程的创建和删除 进程调度 2019/1/14 Linux操作系统分析
56
进程切换(process switching)
为了控制进程的执行,内核必须有能力挂起正在 CPU上执行的进程,并恢复以前挂起的某个进程 的执行,这叫做进程切换,任务切换,上下文切 换 2019/1/14 Linux操作系统分析
57
进程上下文 包含了进程执行需要的所有信息 用户地址空间 包括程序代码,数据,用户堆栈等 控制信息 进程描述符,内核堆栈等 硬件上下文
用户地址空间 包括程序代码,数据,用户堆栈等 控制信息 进程描述符,内核堆栈等 硬件上下文 2019/1/14 Linux操作系统分析
58
尽管每个进程可以有自己的地址空间,但所有的 进程只能共享CPU的寄存器。
硬件上下文 尽管每个进程可以有自己的地址空间,但所有的 进程只能共享CPU的寄存器。 因此,在恢复一个进程执行之前,内核必须确保 每个寄存器装入了挂起进程时的值。这样才能正 确的恢复一个进程的执行 硬件上下文: 进程恢复执行前必须装入寄存器的一组数据 包括通用寄存器的值以及一些系统寄存器 通用寄存器如eax,ebx等 系统寄存器如eip,esp,cr3等等 2019/1/14 Linux操作系统分析
59
在linux中 一个进程的上下文主要保存在thread_info, task_struct的thread_struct中,其他信息放在内核态堆栈 中 thread_struct参见include/asm_x86/processor.h 在task_struct中 2019/1/14 Linux操作系统分析
60
上下文切换 schedule()函数选择一个新的进程来运行,并调 用context_switch进行上下文的切换,这个宏调 用switch_to来进行关键上下文切换 switch_to利用了prev和next两个参数: prev:指向当前进程 next:指向被调度的进程 阅读相关代码 2019/1/14 Linux操作系统分析
61
什么时候next进程真正开始执行呢? call=保存返回地址+跳转到target处执行
ret=从堆栈上获得返回地址,并跳转到该返回地址处 执行 ?当__switch_to正常返回时,发生了什么事情? 2019/1/14 Linux操作系统分析
62
标号为1的执行代码处 一个进程被正常切换出时,保存的eip总是标号 为1的那个位置
当这个进程再次被调度运行时,恢复在堆栈上的 返回地址总是这个1 2019/1/14 Linux操作系统分析
63
__switch_to __switch_to用来处理其他上下文的切换
此时,使用的堆栈是next进程的堆栈,这个堆栈 上没有__switch_to需要的参数prev和next 怎么传参呢? 仔细观察switch_to的定义 2019/1/14 Linux操作系统分析
64
__switch_to的关键操作 unlazy_fpu() 处理数学协处理器 关于per_cpu(init_tss, cpu)和esp0
保存和恢复fs、gs Current的变化 等等 关于任务状态段和内核堆栈 参见cpu_init看TSS初始化 2019/1/14 Linux操作系统分析
65
?哪里切换了进程的地址空间 从执行switch_to的位置往前找 2019/1/14 Linux操作系统分析
66
Project:进程的切换 对Linux中进程的切换过程进行分析,提交分析 报告 2019/1/14 Linux操作系统分析
Similar presentations