这里写目录标题

  • L10 用户级线程
  • L11 内核级线程
  • L12 内核级线程实现

进程如何切换?

L10 用户级线程

进程切换时,当前进程的CPU环境要保存,新进程的CPU环境要设置,线程切换时只须保存和设置少量寄存器,并不涉及存储管理方面的操作,可见,进程切换的开销远大于线程切换的开销。

因为资源切换的系统开销很大,若将资源和指令执行分开,只进行指令的切换,开销会很小。
所以将一个进程分为一个资源和多个指令执行序列,实质是映射表不变而PC指针变。

函数结束ret指令干了两件事:先出栈;再将出栈的值放到CPU的PC寄存器中。因为PC寄存器中永远放的是下一次执行指令的地址,所以就顺理成章的在函数调用完之后依旧接着原来的代码继续执行。
参考https://blog.csdn.net/qq_41431406/article/details/96446312

需要知道函数调用CALL 指令会将其返回地址压入堆栈,再把被调用过程的地址复制到指令指针寄存器。当函数结束时, RET 指令从堆栈把返回地址弹回到指令指针寄存器,于是程序接着原来的代码继续执行。
如果两个线程共用一个栈,就会出现返回地址错误的情况,所以有几个线程就要有几个栈。

yield的意思是“屈服、礼让”,在程序中表现为当前线程会尽量让出CPU资源来给其他线程执行。
参考https://blog.csdn.net/ztliduo/article/details/54565504

每个线程拥有一个栈时,线程切换时要先切换栈,通过Yield函数,根据图中yield函数体,可知是通过yield函数其实就是把当前CPU执行的线程的栈保存起来,并且取出需要执行的线程的栈指针赋给寄存器开始执行
PCB是进程控制块
TCB是线程控制块
yield{
当前线程的栈指针 = 寄存器里的栈指针
寄存器里的栈指针 = 将要运行线程的栈指针
}


线程创建函数thread_create的核心是用程序做出线程的PCB、栈、保存在栈中的切换的PC(线程的初始地址)
根据图中函数体:
1.申请内存给TCB
2.申请内存给栈
2.把线程的初始地址 放在栈中
3.栈和TCB关联(把指向 初始地址 的 栈顶指针 赋给 TCB)


为什么用户级线程是用户级线程?因为yield是用户程序,就是用户自己编写的程序,不涉及到内核,即不涉及操作系统。
不管该进程有多少线程,这些线程都是用户自己写的,
多进程并发时,操作系统只看得到进程,所以进程里某线程阻塞,在操作系统看来就是进程阻塞,
所以在进程的某个线程进入内核并阻塞的时候,该进程就会被阻塞。
(这里是多线程模型中的多对一模型,参考https://blog.csdn.net/zxc024000/article/details/78972283)

L11 内核级线程

多核CPU和多CPU的区别主要在于性能和成本。多核CPU性能最好,但成本最高;多CPU成本小,便宜,但性能相对较差。
参考https://zhuanlan.zhihu.com/p/85819786

我本来以为多处理器相当于多个电脑,性能肯定比多核要好。但是正好相反,多核CPU性能最好。我的理解是因为多处理器之间的合作会有开销。

只有内核级线程才能发挥多核性能,多个CPU共用一套MMU设备情况下,可以一个CPU执行一个内核级线程,在内核级线程切换的时候,不需要切换内存映射关系,代价小很多,因为本来一个进程中MMU映射关系就是一样的
进程 无法发挥多核性能,因为只有一套MMU,进程切换时MMU映射关系也得跟着切换,即切换内存映射表,切换内存映射表代价比较大
参考https://blog.csdn.net/nkltc/article/details/73658210

MMU-内存管理单元,是一种负责处理中央处理器(CPU)的内存访问请求的计算机硬件。它的功能包括虚拟地址到物理地址的转换(即虚拟内存管理)、内存保护、中央处理器高速缓存的控制,在较为简单的计算机体系结构中,负责总线的仲裁以及存储体切换。
参考https://baike.baidu.com/item/MMU/4542218

我的理解:
一个CPU(核)可以执行一个进程(内核级线程),多个CPU可以执行多个进程(内核级线程)
但是在多个CPU共用一套MMU设备情况下,进程切换时MMU映射关系也得跟着切换,而内核级线程切换时不需要切换内存映射关系

一个内核级线程有一套栈: 用户栈 + 内核栈
用户栈里存放代码和数据,内核栈里存放TCB

对于栈顶的段地址,其是存放在段寄存器SS中的,而对于栈顶的偏移地址,其则是存放在SP寄存器中的 。在任何时刻,SS:SP 都是指向栈顶元素
参考https://blog.csdn.net/qq_35212671/article/details/52761585

在用户态执行的时候,通过中断,进入核心态
通过硬件,将用户栈的相关信息(该线程的用户栈的栈地址、当前指令的地址等五个寄存器的内容),压入内核栈
SS栈段暂存器 - SP栈指针

这一段一直没听太明白,我觉得刚开始的时候,老师讲的是 上面用户程序的过程,不涉及内核程序,即不涉及系统调用即系统调用sys_read的返回地址1000
忽略内核程序部分,执行完内核程序后,就要弹栈CS、PC也就回到了用户态继续执行。

运行到A()将104压栈;运行到B()将204压栈;运行到read()将304压栈;运行int中断指令,将五个参数压栈,进入内核程序;运行到系统调用 sys_read 将1000压栈;运行系统调用 sys_read()。

11.2 完整的系统调用中断过程:

  1. INT 中断自动压栈的有下一条指令,以及用户级线程SS:SP,共五个参数
  2. _system_call 把寄存器保护压栈是压到内核栈中,需要手动压栈
  3. 系统调用,(有可能是_sys_fork,其实就是根据标号找到的系统调用),结束之后继续执行,要执行reschedule,先push $ret_from_sys_call,让其在_schedule之后返回到ret_from_sys_call, _schedule为c函数,结束右括号会把ret_from_sys_call pop出来,返回到这里执行,即执行ret_from_sys_call;
    这里注意call 和 jmp的区别!!!
  4. 在ret_from_sys_call中pop出_system_call时保护的寄存器内容,然后中断返回!!!
  5. 中断返回是在最后,中断返回会把SS:SP 以及用户态的下一条指令 POP出来,即把5个寄存器pop出来!!!这样就会返回到用户栈,运行用户态的下一条指令!!!

原文链接:https://blog.csdn.net/nkltc/article/details/73658210


内核级线程的切换 是在内核栈中 切换的
系统调用 sys_read 会启动磁盘读,将自己变成阻塞,引起调度,找到下个线程的TCB,执行switch_to切换线程。
switch_to:https://blog.csdn.net/chengonghao/article/details/51334705

当使用IRET指令返回到相同保护级别的任务时,IRET会从堆栈弹出代码段选择子及指令指针分别到CS与IP寄存器,并弹出标志寄存器内容到EFLAGS寄存器。
参考https://blog.csdn.net/zhuyi2654715/article/details/8180933


1.进入中断,五参数压栈;
2.进行中断处理,可能会阻塞引起调度;
3.若要调度切换,先找到下一线程的TCB;
4.根据TCB,switch_to完成内核栈的切换;
5.通过iret从内核栈返回用户栈。

参考https://zhuanlan.zhihu.com/p/362263170

这里要注意,中断出口这里已经经过了前面的switch_to,中断的iret已经不是原先的中断返回了,是切换后的新中断的执行返回!!!这样返回以后就来到了引发该新中断的用户态代码来执行


L12 内核级线程实现

参考https://blog.csdn.net/weixin_43135178/article/details/105896431
参考https://blog.csdn.net/nkltc/article/details/73658210


调用main,返回地址exit压栈;调用A,返回地址B的开始地址压栈;调用fork,执行INT中断(INT执行的时候没有进入内核,执行完了才进入内核)CPU找到当前内核栈,在内核栈压入五参数;进入中断处理函数system_call

把用户态现场在内核态保存下来,执行系统调用sys_fork(可能会引起切换,需要检查(中间的三段论))
检查,若当前进程阻塞,需要进行调度,即执行reschedule,执行完后,进行系统调用返回ret_from_sys_call(5段论的最后一段:里面是一堆pop,与push相对应,然后再iread)(从内核栈切换到用户栈)
schedule中找到next(下一个进程的PCB),接下来进行切换switch_to()

Linux 0.11中,进程切换是依靠任务状态段(Task State Segment,简称 TSS)的切换来完成的。

每个任务(进程或线程)都对应一个独立的 TSS。TSS 是内存中的一个结构体,里面包含了几乎所有的 CPU 寄存器的映像
有一个任务寄存器(Task Register,简称 TR)指向当前进程对应的 TSS 结构体

所谓的 TSS 切换就是将 CPU 中的寄存器值都复制到 TR 指向的内存 TSS 结构体中保存起来,同时找到要切换到的下一进程对应的目标TSS,将其中存放的寄存器映像“扣在”CPU 上,这就完成了执行现场的切换。

Intel 架构不仅提供了 TSS 来实现任务切换,而且只要一条指令就能完成这样的切换,即 ljmp 指令。

虽然用一条指令能完成任务切换,但这指令的执行时间很长,这条 ljmp 指令在实现任务切换时大概需要 200 多个时钟周期。

而通过堆栈实现任务切换需要更少的时钟周期,而且采用堆栈的切换还可以使用指令流水等并行优化技术,同时又使 CPU 的设计变得简单。
所以无论是 Linux 还是 Windows,进程或线程的切换都没有使用 Intel 提供的这种 TSS 切换手段,都是通过堆栈实现的。

这一部分我也没捋清楚,大概知道了过程,没有死扣代码细节,等到实验的时候在回看这一部分,看看是不是能更明白。
这里推荐两个讲的挺清楚的文章:
https://blog.csdn.net/williamgavin/article/details/83062645

https://blog.csdn.net/qq_42518941/article/details/119145575

进一步学习操作系统 - 哈工大李治军老师 - 学习笔记 L10L11L12相关推荐

  1. 进一步学习操作系统 - 哈工大李治军老师 - 学习笔记 L26L27

    学习笔记-摘抄自老师书本内容 L26 I/O与显示器 L27 键盘 L26 I/O与显示器 使用外设: (1)向外设对应的端口地址发送 CPU 命令: (2)CPU 通过端口地址发送对外设的工作要求, ...

  2. 进一步学习操作系统 - 哈工大李治军老师 - 学习笔记 L28L29L30L31L32

    学习笔记 L28 生磁盘的使用 L29 从生磁盘到文件 L30 文件使用磁盘的实现 L31 目录与文件系统 L32 目录解析代码实现 完结撒花 L28 生磁盘的使用 要知道:柱面.盘面.磁道.扇区 磁 ...

  3. 进一步学习操作系统 - 哈工大李治军老师 - 学习笔记 L22L23L24L25

    学习笔记 L22 多级页表与快表 总结 L23 段页结合的实际内存管理 L24 内存换入-请求调页 L25 内存换出 L22 多级页表与快表 总结 32位地址,最大内存为232 = 4G 页面尺寸为4 ...

  4. 【操作系统-哈工大李治军】---学习笔记(下)---操作系统管理内存

    # 操作系统-内存 ----------------操作系统如何管理CPU------>操作系统如何管理内存----------------------------------- 1 内存使用 ...

  5. Linux0.11操作系统(哈工大李治军老师)实验楼实验1-引导

    Linux0.11操作系统(哈工大李治军老师)实验楼实验1-引导 实验源地址: https://www.lanqiao.cn/courses/115/learning/ 1.完成bootsect.s屏 ...

  6. Linux0.11操作系统(哈工大李治军老师)实验楼实验2-系统调用

    Linux0.11操作系统(哈工大李治军老师)实验楼实验2-系统调用 在 Linux 0.11 上添加两个系统调用iam()和whoami(),并编写两个简单的应用程序测试它们. 原理 1. 应用程序 ...

  7. 哈工大李治军老师的操作系统学习笔记

    文章目录 1 什么是操作系统 2 操作系统启动 3 操作系统接口 命令行发生了什么? 图形按钮怎么回事? 操作系统接口(系统调用) 4 操作系统调用 不应该随意访问内核 怎么不让你访问内核 不让我访问 ...

  8. 哈工大李治军老师操作系统笔记【10】:内核级线程实现(Learning OS Concepts By Coding Them !)

    文章目录 0 回顾 1 实现 1.1 int 0x80 fork(中断入口) 1.2 进入核心态 1.3 system_call(中断切换中间三段) 1.4 中断出口 1.3 switch_to 1. ...

  9. 哈工大李治军老师操作系统笔记【27】:从生磁盘到文件(Learning OS Concepts By Coding Them !)

    文章目录 0 回顾 1 引入文件 1.1 映射 1.2 链式结构实现文件 1.3 索引结构实现文件 2 总结 0 回顾 盘块号就是连续的扇区 得到盘块号就能进行下列操作 1 引入文件 普通用户使用生磁 ...

最新文章

  1. [亿能测试_www.gdtesting.com]测试技术资料网盘共享
  2. python数据趋势算法_Python数据拟合与广义线性回归算法学习
  3. Nachos3.4系列-1 安装与环境配置 【转】
  4. ROM微型计算机是什么,在微型计算机中,ROM是().
  5. PHP5.2至5.6的新增功能详解
  6. Win10系列:JavaScript 模板绑定
  7. GIT命令行的一些基本操作
  8. python自学网站-分享干货:三个新手自学Python的网站!
  9. 面向对象及os模块、socket模块
  10. 实战-Android开机时间优化
  11. 介绍一种计算机病毒并如何清理,如何清除顽固的计算机病毒和木马
  12. 【gp数据库】十条实用数据库SQL优化建议
  13. 开博尔智能android播放器,高端安卓播放器的选择——开博尔Q10Plus 二代 4K高清播放器...
  14. hbase2.1.6 mr
  15. TCP的三次握手和四次断开
  16. 双目立体视觉(3)- ZED2 ROS Melodic 发布RGB图像及深度信息
  17. 【Python CUDA版】河北工业大学计算机图像处理实验四:频域平滑与锐化
  18. IT项目管理那些事儿读书笔记
  19. 营销革命4.0 从传统到数字
  20. 【实操】如何安装及查看云监控

热门文章

  1. 炉石传说怎么修改服务器,云服务器 炉石传说
  2. picpick截图工具自动保存自动命名设置的方法
  3. Facebook投资者提议废除扎克伯格董事长职位,美国将退出万国邮政联盟 | 雷锋早报...
  4. 启动Mac电脑出现黑屏的原因和解决方法
  5. 很久以前的 -- 关于23种设计模式的有趣见解
  6. 2018年9月发布——Autobetsoft_v3.6_2018_09_免费版
  7. org.graalvm.polyglot 依赖哪个maven
  8. 模拟客户在银行存取款
  9. 【Python】[…,0,0]和[:,0,0]是什么意思呢
  10. 2020年第三方支付接口讲解流程分享