8.内核级线程(核心级线程)

【README】

1.本文内容总结自 B站 《操作系统-哈工大李治军老师》,内容非常棒,墙裂推荐;

2.为什么要讲线程呢。实际要讲进程的切换;进程的切换包括切换指令,切换资源;切换指令就是切换线程简单讲,线程就是程序指令,线程的切换就是程序指令的切换);

  • 进程必须在内核中,切换进程实际上是切换内核级线程,而不是用户级线程
  • 切换用户级线程是切换内核级线程的一部分

2)为什么没有用户级进程?

  • 因为进程要分配资源,如访问内存,所以必须进入内核态才能访问这些资源;

【1】开始核心级线程

1) 多核:多个物理cpu,但共用同一个缓存,同一个MMU内存映射单元;
2)多处理器:多个物理cpu,每个cpu有自己的缓存,自己的MMU内存映射单元;

【补充1】内存映射单元

  • 内存逻辑地址 与 内存物理地址 映射表;

【补充2】并发与并行区别

  • 并发:交替执行(单核运行多个线程,相互有影响);
  • 并行:同时执行(多个线程在多个物理核上运行,可以理解一个核运行一个线程,相互独立不干扰);

3)用户级线程与内核级线程

  • 用户级线程(缺点):操作系统看不到,操作系统无法分配硬件,没有发挥出多核价值;
  • 多进程:也无法发挥多核价值,因为只有一套MMU,无法做到多个进程同时执行;
  • 内核级线程(优点): 操作系统可以看到,为每个线程分配一个cpu,发挥出多核价值,多个内核线程可以同时执行(并行执行);

【1.1】内核级线程原理


1)要有核心级线程,需要既在用户态(用户栈)运行,也在内核态(内核栈)运行;
所以 每个内核级线程需要一套栈(包括用户栈,内核栈),而不一个栈
即 内核线程切换,需要TCB切换一套栈,切换用户栈和内核栈;


【1.2】用户栈与内核栈

1)用户栈与内核栈组成同一个线程的一套栈;
2)INT 0x80 中断指令:使得 进程(线程)从用户态切换到内核态;
3)进入内核态前,把线程的用户栈信息(元数据)压入到内核栈,即把同一个线程的用户栈与内核栈关联起来,如下表所示:

内核栈(栈元素)

含义(对应用户栈的寄存器值)

源SS

用户态的ss寄存器值;

ss 指的是堆栈段寄存器;存放栈的段基址;内存是分段使用的;

源SP

用户态的栈指针寄存器值;

sp指的是堆栈指针寄存器;存放栈的偏移地址;

说的直白点,

栈基址(栈起始内存地址)=段基址左移n位 加上偏移地址;如n取4 ;

EFLAGS

用户态的标志寄存器值;

源PC

用户态的pc寄存器值; pc=程序计数器寄存器;

源CS

用户态的cs寄存器值;cs=代码段寄存器;

4)用户栈与内核栈的关联:

5) IRET (从内核态切换到用户态)

  • 出栈:把内存中内核栈的5个寄存器值弹出到cpu的寄存器中,回到用户态;

6)用户态切换到内核态的步骤

6.1) sys_read() :启动磁盘读,把自己变成阻塞状态(等待磁盘控制器响应时,当前线程阻塞,cpu需要切换运行其他线程)

6.2)switch_to(cur, next)
切换到下一个内核线程; cur表示当前线程tcb,next表示下一个线程tcb;具体过程:

  • 通过线程tcb找到内核栈指针;
  • 通过ret切换到某个内核程序;
  • 用cs:pc 切换到用户程序;

上图中, 线程S调用函数A, 线程T调用函数C;

  • PC=?? 表示 线程T的用户态代码;
  • CS=?? 表示  线程T的用户态代码;
  • ???? 表示 iret指令,从中断返回;

6.3)内核线程切换 switch_to 实际上是对两套栈的切换;每套栈包含用户栈和内核栈;


【2】内核线程切换switch_to 五段论(*非常重要)

1)五段论步骤

步骤

描述

1

用户态1切换到内核态1;

把用户态1的物理寄存器的值保存到内核栈1;(完成用户栈切换到内核栈)

2

执行读磁盘等中断操作;触发中断;

3

因为中断,调用 switch_to 切换到其他内核线程,如线程2;

4

Switch_to函数找到内核线程2的tcb,通过tcb找到内核线程2的内核栈2;

切换到内核栈2(完成内核栈间的切换);

5

通过 iret 把内核栈2中保存的用户态2的寄存器值弹出到物理寄存器;

即 ss:sp, cs:ip 通过内核栈2的用户态寄存器值赋值,从而完成从内核态2切换到用户态2;(完成内核栈切换到用户栈

 用户线程与内核线程:用户线程与内核线程实际上同一个线程

  • 当操作的内存空间在用户态,则是用户线程;
  • 当操作的内存空间在内核态,则是内核线程;

2)附加段:进程切换(S,T);

  • 进程切换还需要切换映射表;

3)创建线程代码细节 (tcb 是对线程的结构体抽象)

void threadCreate()

{

  TCB tcb = get_free_page(); // 申请一段内存作为tcb;

  *krlstatck = …; // 申请一段内存作为内核栈;

  *userstack 传入; // 用户栈

  填写两个stack; // 内核栈与用户栈初始化

  tcb.esp = krlstack; // tcb 关联内核栈

  tcb.状态=就绪;// 状态为就绪

  tcb入队;

}

 4)用户级线程与核心级线程对比

用户灵活性, 用户线程大于核心线程;
原因:

  • 用户线程的调度,开发人员可以自己编写调度策略进行控制,如调用 yield让出cpu;
  • 而内核级线程无法修改调度,即内核线程调度策略是操作系统写死的,无法修改;
  • 9
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值