android软件设计 - staff.ustc.edu.cn

70
Android软件设计 朱宗卫 [email protected]

Upload: others

Post on 05-Dec-2021

9 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Android软件设计 - staff.ustc.edu.cn

Android软件设计

朱宗卫

[email protected]

Page 2: Android软件设计 - staff.ustc.edu.cn

Linux的进程

Page 3: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 3/65

主要内容

❖进程描述符

❖进程切换

❖进程的创建和删除

❖进程调度

Page 4: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 4/65

主要内容

❖进程描述符

❖进程切换

❖进程的创建和删除

❖进程调度

Page 5: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 5/65

进程和线程

❖多道程序对操作系统的需求➔进程❖进一步提高并发度,对操作系统的需求➔线程

❖进程是执行程序的一个实例❖进程和程序的区别

➢ 几个进程可以并发的执行一个程序➢ 一个进程可以顺序的执行几个程序

❖线程和进程的区别❖ Linux 2.4内核以及之前的版本都不支持线程

➢ 2.6内核中有thread,但仍不是线程

❖ Linux中的线程是在用户态实现的,不是本课程的内容❖但对用户态线程有一定的辅助支持

Page 6: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 6/65

进程描述符

❖为了管理进程,内核必须对每个进程进行清晰的描述。

❖进程描述符提供了内核所需了解的进程信息

➢源码include/linux/sched.h定义struct task_struct

➢数据结构很庞大⚫基本信息

⚫管理信息

⚫控制信息

Page 7: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 7/65

Page 8: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 8/65

Linux2.6进程的状态

简单过一下,与状态相关的一些宏1)组合状态2)状态判断3)状态设置

Page 9: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 9/65

进程状态转换图

EXIT_ZOMBIE

或者EXIT_DEAD

或者TASK_DEAD

Page 10: Android软件设计 - staff.ustc.edu.cn

Linux2.6进程的状态

运行状态(TASK_RUNNING)

指正在被CPU运行或者就绪的状态。这样的进程被成为runnning进程。运行态的进程可以分为3种

情况:内核运行态、用户运行态、就绪态。

可中断睡眠状态(TASK_INTERRUPTIBLE)

处于等待状态中的进程,一旦被该进程等待的资源被释放,那么该进程就会进入运行状态。

不可中断睡眠状态(TASK_UNINTERRUPTIBLE)

该状态的进程只能用wake_up()函数唤醒。

暂停状态(TASK_STOPPED)

当进程收到信号SIGSTOP、SIGTSTP、SIGTTIN或SIGTTOU时就会进入暂停状态。可向其发送

SIGCONT信号让进程转换到可运行状态。

僵死状态(TASK_ZOMBIE)

当进程已经终止运行,但是父进程还没有询问其状态的情况。

11/23/09

Page 11: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 11/65

标识一个进程

❖使用进程描述符地址

➢进程和进程描述符之间有非常严格的一一对应关系,使得用32位进程描述符地址标识进程非常方便

❖使用PID (Process ID,PID)

➢每个进程的PID都存放在进程描述符的pid域中

Page 12: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 12/65

进程的PID

❖进程的pid字段

Pid最大值,参见kernel/pid.c顺序使用&&

循环使用

include/linux/types.h

include/asm-XXX/posix_typesYYY.h

include/linux/threads.h

Page 13: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 13/65

stuct pid

❖阅读include/linux/pid.h中

➢“What is struct pid?”

Page 14: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 14/65

Pid的管理和分配

❖创建一个进程时,

❖Pid名字空间

do_fork→copy_process→alloc_pid

Struct pid的cache

Pid位图

关于名字空间的更多信息,参见:Nsproxy.c以及pid_namespace.ch

Page 15: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 15/65

Init_pid_ns

在kernel_init中,被修改为init进程

在start_kernel中,调用pidmap_init进行合理的初始化

Page 16: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 16/65

分配第一个位图页

初始化struct pid的cache

分配一个页作为位图

Page 17: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 17/65

❖阅读alloc_pid、 alloc_pidmap函数

❖掌握基于位图的pid分配方法

❖last_pid+1,或者下一个bit0,或者一个新的map的第一个bit或者从头开始(300以后)

❖内核既要分配唯一的pid还要对已经分好的pid进行跟踪,内核使

用了一个大的位图,其中每个pid由一个比特标识。分配一个空闲

的pid,本质上等同于找出位图中第一个值为0的比特,接下来将

其置为1,释放时由1置为0.

Page 18: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 18/65

用户如何获得一个进程的pid

❖系统调用getpid

❖关于进程组

➢使用组链表

➢所有进程共享组内第一个进程的pid

⚫数据:tgid

❖单独一个进程可以看成只有一个进程的组

❖getpid返回组pid

Page 19: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 19/65

进程和进程的内核堆栈

❖Linux为每个进程分配一个8KB大小的内存区域,用于存放该进程两个不同的数据结构:

➢Thread_info

➢进程的内核堆栈

进程处于内核态时使用,不同于用户态堆栈

内核控制路径所用的堆栈很少,因此对栈和Thread_info

来说,8KB足够了

Thread_info

Page 20: Android软件设计 - staff.ustc.edu.cn

进程和进程的内核堆栈

11/23/09

Page 21: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 21/65

Thread_union

❖C语言允许用如下的一个union结构来方便的表示这样的一个混合体

thread_info由体系结构相关部分定义阅读include/asm-x86/thread_info.h

以及include/asm-x86/thread_info_32.h

include/linux/sched.h

Page 22: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 22/65

❖进程描述符的分配/回收/访问

❖Thread_info的分配/回收/访问

➢ alloc_thread_info

➢ free_thread_info

Kmem_cache_alloc会加速获取内存页

Page 23: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 23/65

Page 24: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 24/65

current_thread_info

❖从刚才看到的thread_info和内核态堆栈之间的配对,内核可以很容易的从esp寄存器的值获得当前在CPU上运行的进程的描述符指针

❖因为这个内存区是8KB=213大小,内核必须做的就是让esp有13位的有效位,以获得进程描述符的基地址

8191=8192-1=0x2000-1=0x1fff

取反:0xffffe000(最后13位为0)

Page 25: Android软件设计 - staff.ustc.edu.cn

11/23/09

Current宏

作业:请简述stack、threadinfo、task_struct之间的关系。

Page 26: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 26/65

Current宏的使用

❖Current宏可以看成当前进程的进程描述符指针,在内核中直接使用

❖举例:

➢比如current->pid返回在CPU上正在执行的进程的PID

Page 27: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 27/65

进程链表

❖为了对给定类型的进程(比如所有在可运行状态下的进程)进行有效的搜索,内核维护了几个进程链表

❖所有进程链表

在进程描述符中:

Page 28: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 28/65

❖进程链表中的插入和删除

➢使用常规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

参见include/linux/list.h或者lib/list_debug.c

Page 29: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 29/65

❖例如,在do_fork调用的copy_process中

❖for_each_process宏扫描整个进程链表

Page 30: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 30/65

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

Page 31: Android软件设计 - staff.ustc.edu.cn

TASK_RUNNING状态的进程组织

11/23/09

Page 32: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 32/65

运行队列数据结构

Page 33: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 33/65

❖struct cfs_rq

……

红黑树

Page 34: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 34/65

❖struct rt_rq

……

基于优先级的运行队列

Page 35: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 35/65

基于P的sched_class类型进行相应动作

Page 36: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 36/65

Page 37: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 37/65

调度类

❖阅读调度类sched_class的定义源码

❖找到主要与运行队列有关的➢ 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

在当前linux版本中(3.11.1),使用了四个调 度 器 类 : stop_sched_class 、rt_sched_class 、 fair_sched_class 、idle_sched_class。在最新的内核中又添加了一个调度类dl_sched_class

dl_sched_class:和RT调度器不同,DL调度器是按照任务的deadline进行调度的。当产生一个调度点的时候,DL调度器总是选择其Deadline距离当前时间点最近的那个任务并调度它执行。

Page 38: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 38/65

Idle类特殊

无任务可调度时才会选择idle任务

Page 39: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 39/65

Fair类

进而查看1)enqueue_entity

2)__enqueue_entity

(红黑树)

3)sched_entity结构4) struct rq

5)struct cfs_rq

Completely Fair Scheduler

完全公平调度

Page 40: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 40/65

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

优先级队列

Page 41: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 41/65

❖激活一个任务

➢ activate_task相对的:deactivate_task

Page 42: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 42/65

pidhash表及链接表

❖在一些情况下,内核必须能从进程的PID得出对应的进程描述符指针。例如kill系统调用

❖为了加速查找,引入了pid_hash散列表

初始化:pidhash_init

Hash函数的使用情况

hash_long便是hash函数

Page 43: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 43/65

Task_struct中:

可以从进程描述符得到进程的pid相关信息

Page 44: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 44/65

Pid数据结构

❖2.6内核为PID专门引入了一个数据结构

➢独立的进程;进程组;sessions

➢使用pid数字的注意之处⚫考虑进程的删除和创建

Page 45: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 45/65

pidhash表及链接表

Page 46: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 46/65

pidhash表及链接表

find_pid_ns() 在pid_hash的哈希表中根据哈希函数得到pid所在的链表头部,然后遍历得到 id 和 ns 都匹对的pid结构体指针;

Page 47: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 47/65

进程之间的亲属关系

❖程序创建的进程具有父子关系,在编程时往往需要引用这样的父子关系。进程描述符中有几个域用来表示这样的关系

Page 48: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 48/65

等待队列

❖当要把除了TASK_RUNNING状态之外的进程组织在一起时,linux使用了等待队列➢ TASK_INTERRUPTIBLE和TASK_UNINTERRUPTIBLE状态的

进程再分成很多类,每一类对应一个特定的事件。在这种情况下,进程状态提供的信息满足不了快速检索,因此,内核引进了另外的进程链表,叫做等待队列

❖等待队列在内核中有很多用途,尤其是对中断处理、进程同步和定时用处很大

Page 49: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 49/65

❖等待队列使得进程可以在事件的条件等待,并且当等待的条件为真时,由内核唤醒它们

❖等待队列由循环链表实现

阅读相关的宏

Page 50: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 50/65

等待队列的链表

Page 51: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 51/65

❖在等待队列上内核实现了一些操作函数

➢ add_wait_queue

➢ add_wait_queue_exclusive

➢ remove_wait_queue

Page 52: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 52/65

进程等待

❖等待一个特定事件的进程能调用下面几个函数中的任一个

➢ sleep_on

➢ sleep_on_timeout

➢ interruptible_sleep_on

➢ interruptible_sleep_on_timeout

❖进程等待由需要等待的进程自己进行(调用)

Page 53: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 53/65

sleep_on

阅读实际的sleep_on代码

Page 54: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 54/65

❖例如事件等待wait_event→__wait_event

等待,直到事件发生(有效,或…)

让出CPU

Page 55: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 55/65

进程的唤醒

❖利用wake_up或者wake_up_interruptible等一系列

的宏,都让插入等待队列中的进程进入TASK_RUNNING状态

Page 56: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 56/65

❖__wake_up

❖__wake_up_common

间接

default_wake_function

activate_task

try_to_wake_up

函数指针curr->func

Page 57: Android软件设计 - staff.ustc.edu.cn

2021/10/19 Linux操作系统分析 57/65

❖/proc文件系统中的进程子目录

❖以进程的PID为目录名

Page 58: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 58/65

主要内容

❖进程描述符

❖进程切换

❖进程的创建和删除

❖进程调度

Page 59: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 59/65

进程切换(process switching)

❖为了控制进程的执行,内核必须有能力挂起正在CPU上执行的进程,并恢复以前挂起的某个进程

的执行,这叫做进程切换,任务切换,上下文切换

Page 60: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 60/65

进程上下文

❖包含了进程执行需要的所有信息

➢用户地址空间包括程序代码,数据,用户堆栈等

➢控制信息进程描述符,内核堆栈等

➢硬件上下文

Page 61: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 61/65

硬件上下文

❖尽管每个进程可以有自己的地址空间,但所有的进程只能共享CPU的寄存器。

❖因此,在恢复一个进程执行之前,内核必须确保每个寄存器装入了挂起进程时的值。这样才能正确的恢复一个进程的执行

❖硬件上下文:进程恢复执行前必须装入寄存器的一组数据➢包括通用寄存器的值以及一些系统寄存器

⚫通用寄存器如eax,ebx等

⚫系统寄存器如eip,esp,cr3等等

Page 62: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 62/65

❖在linux中

➢ 一个进程的上下文主要保存在thread_info,task_struct的thread_struct中,其他信息放在内核态堆栈中

➢ thread_struct参见include/asm_x86/processor.h

在task_struct中

Page 63: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 63/65

上下文切换

❖schedule()函数选择一个新的进程来运行,并调用context_switch进行上下文的切换,这个宏先调用switch_mm 切换内存地址空间(内核线程不切换),再调用switch_to来进行关键上下文切换

❖switch_to利用了prev和next两个参数:

➢prev:指向当前进程

➢next:指向被调度的进程

➢阅读相关代码

Page 64: Android软件设计 - staff.ustc.edu.cn

switch_to

11/23/09

Page 65: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 65/65

❖什么时候next进程真正开始执行呢?

➢ call=保存返回地址+跳转到target处执行

➢ ret=从堆栈上获得返回地址,并跳转到该返回地址处执行

➢?当__switch_to正常返回时,发生了什么事情?

Page 66: Android软件设计 - staff.ustc.edu.cn

2021/10/19 Linux操作系统分析 66/65

标号为1的执行代码处

❖一个进程被正常切换出时,保存的eip总是标号为1的那个位置

❖当这个进程再次被调度运行时,恢复在堆栈上的返回地址总是这个1

__switch_to

用的谁的堆栈呢?

Page 67: Android软件设计 - staff.ustc.edu.cn

2021/10/19 Linux操作系统分析 67/65

__switch_to

❖__switch_to用来处理其他上下文的切换

❖此时,使用的堆栈是next进程的堆栈,这个堆栈上没有__switch_to需要的参数prev和next

❖怎么传参呢?

➢__switch_to函数采用FASTCALL调用模式,利用eax

和edx传入两个参数的值。

➢__fastcall调用的主要特点就是快,因为它是通过寄存器来传送参数的

Page 68: Android软件设计 - staff.ustc.edu.cn

2021/10/19 Linux操作系统分析 68/65

__switch_to的关键操作

❖unlazy_fpu()

处理数学协处理器

❖关于per_cpu(init_tss, cpu)和esp0

❖保存和恢复fs、gs

❖Current的变化

❖等等

关于任务状态段和内核堆栈参见cpu_init看TSS初始化

Page 69: Android软件设计 - staff.ustc.edu.cn

CPU

FS,GSCR3

ESIEDI

EIP

EDX

eflag

EFLAG

ECX

ESP

EBX

ebp

EBP

EAX

pdg

内核堆栈

内核堆栈

pgd

eip

Fs,gs

pdg

esp

eip

Fs,gs

context_switch()

调用前,硬件保存着进程A的相

关信息

现在开始执行context_switch()

函数,首先切换地址空间

pgd

开始执行swith_to宏,

将A寄存器入栈,保存相关

信息

eax ebx ecx edx edi esi

ebp esp eflag eip

将B进程的eip

入栈,调用__swith_to()

函数

首先,保存进程A的段寄存器,将进程B的fs,gs复制到相关寄存器

Fs,gs

Fs,gs

esp

__switch_to()

函数执行完毕后返回进程B

的eip,将进程B栈中的值加

载到相关寄存器中

ebp

esi

eflag

edi

edx

ecx

ebx

eax

此时,已经完成进程A到进程B的上下文

切换

Page 70: Android软件设计 - staff.ustc.edu.cn

11/23/09 Android软件设计 70/65

谢谢