没有合适的资源?快使用搜索试试~ 我知道了~
linux内核源代码分析,自己写的
需积分: 6 3 下载量 6 浏览量
2022-10-12
21:22:35
上传
评论 2
收藏 5.31MB DOCX 举报
温馨提示
试读
467页
5进程管理
资源推荐
资源详情
资源评论
1
第 5 章 进程管理
早期计算机操作系统中按顺序逐个地执行程序,即计算机执行完一个程序后再执行下一个,这样串行
运行程序的操作系统称之为批处理系统。现代计算机操作系统多是多用户多任务系统,系统内可同时存在
多个用户和运行多个程序(进程),由操作系统对它们进行管理和调度。
现代计算机能同时运行多个进程,但这里说的同时并不是真正意义上的同时运行,而是通过处理器程
序指针在不同进程地址空间中来回频繁地切换,使每个进程轮流获取一定的执行时间,由于进程切换的速
度非常快使用户无法察觉到,给用户造成进程同时运行的假象。即使对于多核处理器真正能同时运行的进
程数目也不能多于处理器的核心数(硬件超线程除外)。计算机中同时运行的进程数量要远远多于处理器
核数,处理器何时选择哪个进程运行、运行多长时间等需要操作系统进行管理和调度。另外,操作系统还
需要负责进程的创建、进程资源的管理、进程间通信、进程退出等工作,可以说操作系统就是为进程服务
的。
本章介绍 Linux 内核对进程的管理,主要包括进程在内核中的表示、创建及运行子进程、进程调度、
调度类的实现、负载均衡、进程状态的转换、进程间通信、进程退出等内容。
5.1 概述
计算机采用操作系统的目的是为了更好地运行和管理用户进程,用户进程是用于实际执行用户工作的
处于运行状态的程序。操作系统为用户程序的运行提供基础和操作系统资源的接口,以简化用户程序的实
现。
进程管理和内存管理一样是操作系统内核最基础、最核心的功能。在 Linux 内核中,每个进程(含线
程)由 task_struct 结构体实例表示。task_struct 结构体内包含了进程的各种资源,如进程地址空间、进程
ID、用户 ID、进程文件(外部设备视为文件)等。
内核本身执行路径也视为系统中的进程(内核线程),其 task_struct 实例由内核静态创建,内核路径
最后转换成空闲进程。
这里先解释一下进程和线程的区别。进程是独立拥有所有进程资源的运行中的程序,具有自己独立的
地址空间、可执行文件和文件等。假设某个进程创建了一个子进程,子进程与父进程共用地址空间等资源,
子进程没有独立拥有进程地址空间等资源,则子进程称为线程。
内核线程运行在内核地址空间,所有内核线程共用内核地址空间。用户线程由用户进程(线程)创建,
与其父进程(线程)共用用户地址空间。但是,进程和线程都有自己的 task_struct 实例,对内核来说进程
和线程基本是一样的,都视为一个独立的实例,接受同样的管理和调度。本章后面将不再刻意区分进程和
线程,无特殊情况统一称为进程。
进程由其父进程创建,各进程 task_struct 实例在内核中依父子关系组成层次的树状结构。父进程通过
fork()/clone 系统调用创建子进程或线程,子进程复制或者共用父进程的资源。内核线程由内核代码通过函
数调用创建。
子进程需要运行新的程序代码时,可调用 execve()系统调用从文件系统中加载目标文件至进程用户地
址空间,以使进程运行新的代码。内核线程可通过函数调用(execve()系统调用实现函数)加载运行可执
行文件,转换成用户进程。
内核为每个 CPU 核创建了就绪进程队列,用于管理可运行的进程。每个 CPU 核只能运行其对应就绪
队列中的进程,而不能运行其它 CPU 核就绪队列中的进程(进程可以在就绪队列中迁移)。系统中的进
程具有轻重缓急之分,紧急的进程应当优先运行,而普通的进程暂缓运行也无大碍。因此,内核定义了多
个调度类,用于管理不同类型的进程,各调度类具有不同的优先级,越紧急的进程应当归入优先级越高的
调度类。每个调度类中的就绪进程采用不同的数据结构管理,管理结构嵌入到 CPU 核就绪进程队列中,
也就是说 CPU 核就绪进程队列按调度类分类管理就绪进程。
2
CPU 核某一时刻只能执行一个进程,系统中的进程不能同时运行。CPU 运行某一进程一段时间后,切
换至下一进程运行。内核需要控制何时进行进程切换,切换至哪一个进程,以及如何实现进程切换,这统
称为进程调度。
进程的切换由进程主动发起或由内核周期性地发起,选择下一个运行进程时,先选择优先级最高的调
度类,再在调度类中选择下一个运行的进程,如果本调度类中没有就绪的进程,则从优先级低一级的调度
类中选择进程。也就是说,只要更高优先级调度类中有就绪进程,低优先级的调度类中的进程将得不到运
行。在调度类中选择下一个进程的操作由各调度类实现,各调度类需要对所属进程进行管理。
进程的切换主要由体系架构相关代码实现,进程切换的主要工作是将当前进程的上下文信息(处理器
寄存器信息)存入进程内核栈中,切换当前 task_struct 实例和用户地址空间,从下一进程的内核栈中恢复
下一进程的上下文信息,从而使处理器核运行下一个进程。
在 SMP 系统中,为了使进程在各 CPU 核之间相对公平地分配,内核需要建立进程迁移的机制,即将
进程从较忙的 CPU 核就绪队列迁移到较闲的 CPU 核就绪队列,这称之为负载均衡。负载均衡也是按调度
类进行的,进程只能在不同 CPU 核相同调度类的就绪队列中迁移。
进程并不总是处于可运行状态,当进程在等待某一事件或等待外部设备传递数据时可进入睡眠状态,
主动交出 CPU 控制权。进程睡眠时将从就绪队列中移出,被唤醒时将重新插入就绪队列,接受内核的调
度。另外,进程还具有等待、终止等状态。
进程之间可以相互通信,通信机制包括 sysv 机制、信号、管道、套接字等,本章将对 sysv 机制和信
号进行介绍。
最后,进程在执行完之后将退出,从内核中注销。另外,进程还有其它退出机制,如可以被信号杀死
等。
5.2 进程表示
计算机中运行的用户程序称为进程。进程具有自己独立的地址空间,而与父进程共用地址空间的进程
称为线程,与内核共用地址空间的进程称为内核线程。每个进程和线程在内核中由 task_struct 结构体实例
表示,结构体中包含了进程/线程的所有信息和资源。
5.2.1 thread_union
在介绍 task_struct 结构体之前,先介绍下 thread_union 联合体。内核中每一个进程都有一个对应的
thread_union 联合体实例,联合体定义在/include/linux/sched.h 头文件:
union thread_union {
struct thread_info thread_info; /*进程信息*/
unsigned long stack[THREAD_SIZE/sizeof(long)]; /*进程内核空间栈*/
};
联合体由 thread_info 结构体和内核态栈组成,thread_info 结构体实例位于联合体的下部,上部为进程
进入内核空间运行时使用的栈。内核空间栈的大小由 THREAD_SIZE 常数确定,它决定了联合体的大小。
THREAD_SIZE 常数定义在/arch/mips/include/asm/thread_info.h 头文件:
#if defined(CONFIG_PAGE_SIZE_4KB) && defined(CONFIG_32BIT) /*32 位,页大小为 4KB*/
#define THREAD_SIZE_ORDER (1) /*阶数为 1,表示 2 个内存页面*/
#endif
...
#define THREAD_SIZE (PAGE_SIZE << THREAD_SIZE_ORDER) /*两个页大小,8KB*/
#define THREAD_MASK (THREAD_SIZE - 1UL)
3
THREAD_SIZE 是一个体系架构相关的常数,在 32 位系统且内存页大小为 4KB 时,其大小为两个内
存页,也就是 8KB。内核空间栈是进程通过系统调用或异常进入内核空间运行时使用的栈,由联合体定义
可知内核栈可用大小小于 8KB(thread_info 实例占了部分空间),因此在编写内核函数(包括中断处理函
数)时不能定义太大的局部变量,以免内核栈溢出而产生错误。
thread_union 联合体的底部是一个 thread_info 结构体实例,定义在/arch/mips/include/asm/thread_info.h
头文件:
struct thread_info {
struct task_struct *task; /*指向进程 task_struct 实例*/
unsigned long flags; /*进程底层标记*/
unsigned long tp_value; /*thread pointer(线程指针)*/
__u32 cpu; /*当前 CPU 编号 (运行进程的 CPU 核编号)*/
int preempt_count; /*抢占计数,为 0 表示可抢占, 不为 0 表示不可抢占*/
int r2_emul_return; /* 1 => Returning from R2 emulator */
mm_segment_t addr_limit; /*可用虚拟地址上限,
*用户进程:0x7fffffff,内核线程:0xffffffff 。
*/
struct pt_regs *regs; /*指向内核栈中的 pt_regs 实例,中断处理程序中使用*/
long syscall; /*系统调用编号*/
};
thread_info 结构体表示进程的底层信息,它是一个体系架构相关的结构体,主要成员定义如下:
●task:进程 task_struct 结构体实例指针;
●flags:进程底层标记成员(体系架构相关),取值定义在/arch/mips/include/asm/thread_info.h 头文件:
#define _TIF_SYSCALL_TRACE (1<<TIF_SYSCALL_TRACE) /*系统调用跟踪*/
#define _TIF_SIGPENDING (1<<TIF_SIGPENDING) /*具有挂起的信号*/
#define _TIF_NEED_RESCHED (1<<TIF_NEED_RESCHED) /*需要重调度*/
#define _TIF_SYSCALL_AUDIT (1<<TIF_SYSCALL_AUDIT) /*系统调用审计激活*/
#define _TIF_SECCOMP (1<<TIF_SECCOMP) /* secure computing */
#define _TIF_NOTIFY_RESUME (1<<TIF_NOTIFY_RESUME) /*返回用户空间前执行回调*/
#define _TIF_USEDFPU (1<<TIF_USEDFPU) /*进程使用浮点寄存器*/
#define _TIF_NOHZ (1<<TIF_NOHZ) /*在适应动态钟模式*/
#define _TIF_FIXADE (1<<TIF_FIXADE) /*固定地址错误*/
#define _TIF_LOGADE (1<<TIF_LOGADE)
#define _TIF_32BIT_REGS (1<<TIF_32BIT_REGS) /*32 位通用寄存器*/
#define _TIF_32BIT_ADDR (1<<TIF_32BIT_ADDR) /*32 位地址空间*/
#define _TIF_FPUBOUND (1<<TIF_FPUBOUND)
#define _TIF_LOAD_WATCH (1<<TIF_LOAD_WATCH) /*若置位,装载看门狗寄存器*/
#define _TIF_32BIT_FPREGS (1<<TIF_32BIT_FPREGS) /*32 位浮点寄存器*/
#define _TIF_HYBRID_FPREGS (1<<TIF_HYBRID_FPREGS) /*64 位浮点寄存器*/
#define _TIF_USEDMSA (1<<TIF_USEDMSA) /*使用 MSA*/
#define _TIF_MSA_CTX_LIVE (1<<TIF_MSA_CTX_LIVE)
#define _TIF_SYSCALL_TRACEPOINT (1<<TIF_SYSCALL_TRACEPOINT)
/*进入系统调用设置的标记位*/
4
#define _TIF_WORK_SYSCALL_ENTRY (_TIF_NOHZ | _TIF_SYSCALL_TRACE | \
_TIF_SYSCALL_AUDIT | _TIF_SYSCALL_TRACEPOINT | _TIF_SECCOMP)
/* work to do in syscall_trace_leave() */
#define _TIF_WORK_SYSCALL_EXIT (_TIF_NOHZ | _TIF_SYSCALL_TRACE | \
_TIF_SYSCALL_AUDIT | _TIF_SYSCALL_TRACEPOINT)
/*中断/异常返回时设置的标记位*/
#define _TIF_WORK_MASK \
(_TIF_SIGPENDING | _TIF_NEED_RESCHED | _TIF_NOTIFY_RESUME)
/*返回用户空间设置的标记位*/
#define _TIF_ALLWORK_MASK (_TIF_NOHZ | _TIF_WORK_MASK | \
_TIF_WORK_SYSCALL_EXIT |_TIF_SYSCALL_TRACEPOINT)
注:flags 成员最低位 bit0 置位表示挂起工作已被处理。
以下两个函数定义在/include/linux/sched.h 头文件,用于设置/清零 flags 成员标记位:
set_tsk_thread_flag(struct task_struct *tsk, int flag):用于设置 tsk 表示进程 thread_info 实例 flags 成员
中 flag 参数表示的标记位,例如:flag 为 TIF_SIGPENDING 表示设置挂起信号标记位_TIF_SIGPENDING。
clear_tsk_thread_flag(struct task_struct *tsk, int flag):用于清除相应的标记位。
●preempt_count:抢占计数,为 0 表示进程可被抢占,不为 0 表示不可被抢占,详见本章下文。
●addr_limit:进程可用地址空间上限,用户进程为 0x7fffffff,内核线程为 0xffffffff。
●regs:pt_regs 结构体指针,只在中断处理程序中使用。
pt_regs 结构体用于进程进入异常(含中断)处理程序时保存 CPU 寄存器信息(进程上下文信息)。
在进程内核栈顶部保存了该结构体的一个实例,此实例保存的是进程从用户空间进入内核空间运行时的
CPU 寄存器信息。从异常处理程序返回用户空间时将从此 pt_regs 实例中恢复寄存器信息。进程进入内核
空间运行时发生的异常,寄存器信息保存在内核栈的当前位置,也是由 pt_regs 实例保存。
regs 成员只在中断处理程序中使用,当中断嵌套时,在内核栈中将有多个 pt_regs 实例,regs 成员指向
最近一次中断处理程序中保存寄存器信息的 pt_regs 实例。
pt_regs 是一个体系架构相关的结构体,MIPS 体系架构定义在/arch/mips/include/asm/ptrace.h 头文件:
struct pt_regs {
#ifdef CONFIG_32BIT
unsigned long pad0[8]; /*保存参数*/
#endif
unsigned long regs[32]; /*保存 32 个通用寄存器 */
/*以下是保存特殊寄存器*/
unsigned long cp0_status; /*cp0 状态寄存器*/
unsigned long hi; /*乘法器结果寄存器*/
unsigned long lo;
#ifdef CONFIG_CPU_HAS_SMARTMIPS
unsigned long acx;
5
#endif
unsigned long cp0_badvaddr; /* cp0 错误地址寄存器*/
unsigned long cp0_cause; /* cp0 原因寄存器*/
unsigned long cp0_epc; /* cp0 异常返回地址寄存器*/
#ifdef CONFIG_CPU_CAVIUM_OCTEON
...
#endif
} __aligned(8);
pt_regs 结构体内保存了处理器主要的寄存器信息,但是并没有保存浮点寄存器等信息,因为进程进
入内核空间运行时,内核代码不使用浮点寄存器,因此不需要保存。也就是说如果 CPU 在运行一个进程 A
时产生了异常,进入内核空间,内核异常处理完成后返回用户空间,继续运行进程 A 则不需要保存浮点寄
存器。
如果进程在内核空间运行时(返回用户空间前)发生了进程调度,将运行与进入异常前不同的进程,
则在进程切换操作中需要保存处理器更多的寄存器信息,因为用户进程可能会使用浮点寄存器。发生进程
切换时(总是在内核空间进行),当前进程在内核空间运行的上下文信息保存在 task_struct 实例 thread 成
员中(thread_struct 结构体)。CPU 从下一进程的 task_struct 实例 thread 成员中恢复寄存器信息,以运行
下一个进程(详见 5.7 节)。
thread_info 结构体 regs 成员指向内核栈中的 pt_regs 实例。下图示意了以上数据结构的关系:
thread_info
regs
...
task_struct
pt_regs
sp
内核
态栈
低地址
高地址
thread_union
stack
thread
thread_struct
sp
task
内核空间
用户空间
用户态栈
代码/数据
堆
内存映射区
current_pt_regs()宏定义在/arch/mips/include/asm/ptrace.h 头文件,用于在内核代码中获取当前进程的
内核栈顶 pt_regs 实例指针:
#define current_pt_regs() \
({ \
unsigned long sp = (unsigned long)__builtin_frame_address(0); \
(struct pt_regs *)((sp | (THREAD_SIZE - 1)) + 1 - 32) - 1; \
})
内核为自身定义了 thread_union 实例 init_thread_union(/init/init_task.c):
union thread_union init_thread_union __init_task_data ={ INIT_THREAD_INFO(init_task) };
INIT_THREAD_INFO()宏定义在/arch/mips/include/asm/thread_info.h 头文件:
#define INIT_THREAD_INFO(tsk) \ /*初始化 thread_union 实例*/
{ \
剩余466页未读,继续阅读
资源评论
linux源码解析
- 粉丝: 6
- 资源: 10
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功