哈工大操作系统的笔记

操作系统笔记

L1.什么是操作系统?
引入:如何在屏幕上打印出"hello,world"呢?

(1)是计算机在底层通过一些汇编指令,cpu将"hello,world"在0x68的内存中存储,然后通过汇编指令将0x68写入777显存地址,然后到图形控制器,就可以输出了。如下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ogx699C6-1684932615725)(D:\笔记图片\Snipaste_2023-05-07_17-33-44.png)]

然而,这样的步骤太繁琐,太复杂了,如何简化这种操作呢?就需要操作系统了,方便我们更简单的处理一些问题,如打印 “hello,world”

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QboqaYp0-1684932615726)(D:\笔记图片\Snipaste_2023-05-07_17-34-11.png)]

(2)所以,操作系统是介于计算机硬件与应用软件之间的一层软件,为我们提供了一些接口,方便我们操作,如打印"hello,world",只需printf(“hello,world”)即可,所以类似这样的函数还有很多。以及更便于对硬件的操作,还有对如下一些硬件的管理。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mnhwaCdR-1684932615728)(D:\笔记图片\Snipaste_2023-05-07_17-34-28.png)]

1 我们该如何学习操作系统呢?

(1)现阶段,应该从应用软件出发进入到操作系统中去,能够知道文字如何写到磁盘上去的,最后能够对操作系统进行修改。当能力提升后,就要开始能够从头写一个操作系统(CMU学校就是这样的教学方式),这样就会更加深入。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UgMNozW8-1684932615728)(D:\笔记图片\Snipaste_2023-05-07_17-52-30.png)]

(2)提升能力,参考其他顶尖大学的学习方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lV2YHbLg-1684932615729)(D:\笔记图片\Snipaste_2023-05-07_17-52-44.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d3nIci4L-1684932615729)(D:\笔记图片\Snipaste_2023-05-07_17-53-08.png)]

L2.打开钢琴的盖子(open the OS)
1 探寻计算机开启电源后的故事

(1)计算机如何工作的就要从图灵机开始说起,比如3+5在我们的认知里,就是从纸上获取3、5、+,然后通过人脑的一些规则计算出8,然后再写到纸上。而图灵机也是如此,从纸片上读取3、5、+,发送给控制器,然后控制器通过已有的规则,算出结果再打印到制片上。但是此时的图灵机就比较单一,只能处理一种,所以距离计算机较远。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C64q86P5-1684932615730)(D:\笔记图片\Snipaste_2023-05-07_20-14-21.png)]

(2)从图灵机到通用图灵机:即类似一个厨师,看过一个菜谱然后就可以做出 一道菜。通用图灵机就是从纸带上读取控制机的操作命令,然后读取数据,进行相应的操作。如:就是将PPT的程序放进去就执行PPT的操作等这就相当于程序。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-biMRnyno-1684932615730)(D:\笔记图片\Snipaste_2023-05-07_20-14-37.png)]

(3)从通用图灵机到计算机:冯诺依曼提出存储程序思想,就是将相应的程序存储到内存中,然后通过指针进行取指执行,就是将指针指向的那个程序交由控制器处理,然后指针往下走,再指向下一个程序。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5mqmtaTe-1684932615730)(D:\笔记图片\Snipaste_2023-05-07_20-14-54.png)]

2 开启电源计算机执行的第一条指令是什么?(关注IP指针和IP指针指向的内容)

(1)要探寻开启电源后计算机执行的操作,就需要弄清楚IP指针首先指向的是什么内容,这是由硬件设计者决定的。例如X86的PC,在刚开机后CPU处于实模式(实模式的寻址CS:IP(CS左移4位+IP)),如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4xNPPrKq-1684932615731)(D:\笔记图片\Snipaste_2023-05-07_20-25-54.png)]

如上,开机时CS为(这是固定的)0xFFFF向左移动四位+0,即寻址的位置就为FFFF0在**基本的输入输出系统(ROM BIOS)**中,然后开始检查键盘,显示器,软硬盘等,若此时出现问题就说明硬件出现了问题。然后开始读取内存,从磁盘的0磁道0扇区开始读(一个扇区512字节,其中0磁道0扇区就是操作系统的引导扇区)。读到0x7c00处,设置指针cs=0x07c0,ip=0x0000,此时就要从0x07c0开始执行了。此时,操作系统的一切都从引导扇区开始的(如果我们要修改或重写操作系统就要在这里开始)。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tsejeddb-1684932615732)(D:\笔记图片\Snipaste_2023-05-07_20-45-36.png)]

3 引导扇区的代码(汇编)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NGuH9sqQ-1684932615732)(D:\笔记图片\Snipaste_2023-05-07_20-54-38.png)]

L3.操作系统的启动

回顾内容:上节课讲到bootsect.s代码,当计算机按下电源键时,操作系统在硬盘中而计算机的工作是取指执行,所以bootsect.s首先的工作就是将操作系统从硬盘中读取到内存中,否则就不会按照取指执行的方式执行(因为没有地址可以取)。然后执行下一块代码setup.s,也就是这节课将要学的。

1、setup模块

(1)此模块主要是完成OS在启动前的一些设置,检查内存等等。代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1Q1eKqul-1684932615733)(D:\笔记图片\Snipaste_2023-05-08_22-48-21.png)]

L8 CPU管理的直接想法

操作系统的主要作用就是对硬件的管理,其中最主要的就是CPU。

1 CPU的工作原理:

(1)冯诺依曼表示,程序需要存储在内存中,然后设置一个地址。如PC=50,CPU就会根据就这个地址放在地址总线上,然后取指执行将这个地址对应的命令在传回总线上,CPU就获得了这条指令。如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Xigbbg9j-1684932615734)(D:\笔记图片\Snipaste_2023-05-09_18-50-11.png)]

内存自然而然就会做这样一件事,即把 PC = 50 对应的指令通过地址总线发给CPU去执行,执行的结果是 ax = 0,因为100的地址对应的就是0。,然后将获得的地址在返回给CPU,如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1FHO6jO6-1684932615734)(D:\笔记图片\Snipaste_2023-05-09_18-54-18.png)]

(2)然后CPU就会读取执行返回来的内容,将地址100的内容赋给ax,则此时ax就为0.如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IzufIQIp-1684932615735)(D:\笔记图片\Snipaste_2023-05-09_18-59-47.png)]

所以,CPU的工作方式就是自动的取指执行。

(3)管理CPU的方式,就是刚开始就给PC设置一个初值,然后CPU就会自动的取指执行。如下图,完成一个加法运算:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F8EyrJRQ-1684932615739)(D:\笔记图片\Snipaste_2023-05-09_19-08-50.png)]

刚开始取指执行,ax=0,接着执行下一条指令bx=1,最后执行加法指令ax+bx=1,这样就完成了一个加法运算。

但是,这样的工作方式CPU会有什么问题呢?

2 进程的引出

(1)当CPU按上述的工作方式工作的话,就会有一个问题,当执行到如下代码时:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-juxrPRDW-1684932615740)(D:\笔记图片\Snipaste_2023-05-09_19-16-19.png)]

首先,将fprintf函数换位普通的运算语句(加法等),计算该程序运行的时间:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ll6JY8bL-1684932615741)(D:\笔记图片\Snipaste_2023-05-09_19-18-32.png)]

而执行该函数(该函数的IO操作的函数)所花费的时间为:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kbGBg6yt-1684932615741)(D:\笔记图片\Snipaste_2023-05-09_19-18-46.png)]

这样可以计算出,普通的运算语句与IO操作的函数相比时间为:,这样的差距非常的大,说明IO指令非常的费时。如果要执行如下的指令:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rgyf7zIw-1684932615742)(D:\笔记图片\Snipaste_2023-05-09_19-27-24.png)]

PC从上面开始,就开始取指执行,当执行到IO操作时IO可能要读取一个东西,而此时CPU就要一直等到,等到IO操作完成之后才可以运行下面的指令。如果按照上述等的时间,cpu的利用率就只有%50。而当运算指令较少时,CPU的利用率就几乎为0.所以这样的设计就很不合理。

(2)依上面所述,比如我们在烧水时,将水放在火上就去忙其他的事情,当水烧好之后会发出一个声响(这就类似与中断),然后再回来。

这样就是CPU的管理,[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fapzup8n-1684932615743)(D:\笔记图片\Snipaste_2023-05-09_19-41-40.png)]

所以多到程序交替执行,就可以提高CPU的利用率,如下图的操作:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XVcBfXrZ-1684932615743)(D:\笔记图片\Snipaste_2023-05-09_19-44-07.png)]

(3)所以一个CPU面对多个程序时,交替执行,这就叫做并发。

但是,当面对多个程序时,只是修改PC寄存器就可以吗?答案:肯定不是。因为每个程序都有相对应的东西,是为独立的。如下图:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5ghvTj5a-1684932615744)(D:\笔记图片\Snipaste_2023-05-09_19-51-17.png)]

当程序1切过去时,ax=1,bx=1,而在程序2时ax=10,bx=10,如果不保存的话,当再切回程序1时ax与bx的值就会被修改,导致后面的程序就会发生变化,所以在切到下一个程序之前要先保存的一个结构中-----PCB。如下图:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ho4XWHRj-1684932615745)(D:\笔记图片\Snipaste_2023-05-09_19-54-49.png)]

(4)所以一个运行的程序与静态的程序就不一样了,因为只有运行中的程序必须要记录当前程序的样子。所以给这样的程序起了一个名字:进程(运行中的程序)[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DvUzxVAe-1684932615746)(D:\笔记图片\Snipaste_2023-05-09_20-08-12.png)]

总结:

首先,由探究CPU是如何工作的(取指执行),引出CPU的利用率(就是在面对一个需要长时间等待CPU的程序应该怎么弄),接着想到CPU在面对多个运行的程序是如何处理?(面对多个程序时,有中断提醒让其执行其他程序)然后想到当CPU在处理多个程序时应当保存哪些程序的哪些信息?(要保存当前进程的地址,一些变量值等,保存在PCB中),进而提到了进程的概念是如何而来的。

L9 多进程图像
1 什么是多进程图像

(1)如何使用CPU呢?让程序执行起来,即使用了CPU。

(2)如何让CPU高效的运行呢?答:启动多个程序,让其交替运行。

(3)多个进程交替运行的过程就是多进程图像。在用户层面只是启动多个进程,而在操作系统层面,就要管理这些进程,按照合理的次序进行推进(分配资源,进行调度) [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yVKbjFdD-1684932615747)(D:\笔记图片\Snipaste_2023-05-09_23-10-23.png)]

2 多进程如何组织

(1)PCB:是用来记录进程的一些信息、状态的数据结构。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qFfioKpy-1684932615747)(D:\笔记图片\Snipaste_2023-05-09_23-15-04.png)]

(2)进程可以按照不同的状态分为不同的进程,主要有:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fyme9Qe8-1684932615747)(D:\笔记图片\Snipaste_2023-05-09_23-15-51.png)]

  1. 新建态:系统完成创建进程的一系列工作。只能转换到就绪态

  2. 就绪态:拥有除过CPU之外的其他所需的所有资源。当拥有CPU时就可以转换到运行态

  3. 运行态:用于CPU和所需的所有资源

    1. 当时间片到或者处理机被抢占了,就转换到就绪态;
    2. 当进程用【系统调用】的方式申请某种系统资源或者请求等待某个事件的发生,则进入阻塞态(主动)
  4. 阻塞态:没有所需要的资源。当所需要的资源得到分配时,进入就绪态(被动)

  5. 终止态:进程运行结束或者于大盘不可修复的错误时,由运行态转到终止态

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z7xNplX8-1684932615748)(D:\笔记图片\Snipaste_2023-05-09_23-19-20.png)]

其中:多个进程不是但处于一个状态,而是在单CPU中,正在执行一个程序,其他的程序有的在就绪队列等待,有的在磁盘等待队列中等等。

(3)多进程的组织,主要依靠PCB+进程状态+队列,用PCB放在不同的队列中,用状态来推进这多个进程,进行状态转化

3 多进程如何交替

(1)通过PCB中记录的进程的状态信息,然后通过调度函数(调度算法),对进程进行安排放入该放入的队列。如下图代码:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kPJ5LmP8-1684932615748)(D:\笔记图片\Snipaste_2023-05-09_23-28-39.png)]

在启动磁盘读写时,将当前进程的状态设置为等待(waitting),然后放入磁盘等待队列中,接着通过schedule()函数从就绪队列中获取一个新的进程,接着执行其他操作。

(2)其中,进程调度是一个深刻的话题,因为有很多调度算法并且每年都会有人提出新的调度算法,所以可以自己去私下学习。

(3)进程切换:把物理CPU的信息保存到PCB1中(即当前进程的现场),然后就可以切换到下一个进程(即恢复下一个进程的现场)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B5HlEC6b-1684932615749)(D:\笔记图片\Snipaste_2023-05-09_23-34-50.png)]

(4)所以进程的组织包括:修改进程状态进程调度进程切换

(5)进程的切换包括:切换指令,切换指令流,切换内存资源

4 多进程如何影响

(1)由于进程都是存在内存中的,当运行多个程序时难免会产生内存的访问冲突。比如,当执行程序1时将as移动到地址100的位置,而此时发现程序2的地址就是100,这样就会产生冲突。ax寄存器存储的内容为10100b,进程1按顺序执行汇编代码,执行mov[100],ax指令的时候,切换到进程2中去执行,ax寄存器存储的内容变为00101,也就是说在没有任何限制的前提下,ax寄存器的内容被修改了,可能导致进程1崩溃,相当危险,因为后面的汇编代码可能都是依靠ax寄存器原来的值为参考的。如下图:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-urg0HRx5-1684932615749)(D:\笔记图片\Snipaste_2023-05-10_11-01-03.png)]

(2)如何避免这种影响呢?方法就是,限制对100内存的读写,就是将多进程的地址空间分离。通过映射表来解决(映射表:进程中的地址映射到表中,然后该表在投射到内存中,在这样就避免了对一个已经有数据的内存在进行访问。)如下图:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M2mhm0Ek-1684932615750)(D:\笔记图片\Snipaste_2023-05-10_11-07-37.png)]

当多个程序运行时,也是按照映射表来限制访问范围,如下:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PeoCEGLJ-1684932615750)(D:\笔记图片\Snipaste_2023-05-10_11-07-56.png)]

这样两个进程访问相同的地址,但是通过映射表映射到内存就是不同的地址,这样就避免了内存冲突。

5 多进程如何合作

(1)打印任务的队列分析,[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hNzpUQs7-1684932615750)(D:\笔记图片\Snipaste_2023-05-10_11-14-38.png)]

进程1和进程2发现打印队列7是空闲的,在没有沟通的前提下,进入队列7中,导致打印的内容错乱,这显然是不合理的,于是提出两个概念:

进程同步也是进程之间直接的制约关系,是为完成某种任务而建立的两个或多个线程,这个线程需要在某些位置上协调他们的工作次序而等待、传递信息所产生的制约关系。进程间的直接制约关系来源于他们之间的合作

进程互斥是进程之间的间接制约关系。当一个进程进入临界区使用临界资源时,另一个进程必须等待。只有当使用临界资源的进程退出临界区后,这个进程才会解除阻塞状态。

(2)例如:生产者消费者模型,[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r2LzhyuQ-1684932615751)(D:\笔记图片\Snipaste_2023-05-10_11-16-29.png)]

当对connter进行操作时,可能会出现错误,如下:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vkHob852-1684932615751)(D:\笔记图片\Snipaste_2023-05-10_11-16-42.png)]

如果按照左上角的序列执行,到最后counter会变为4,而实际情况是,调用了一次生产者,调用了一次消费者使得counter还是5,所以这样就会导致问题。如何解决呢?就要加锁如下图:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UPVZvhCz-1684932615751)(D:\笔记图片\Snipaste_2023-05-10_11-16-56.png)]

这样就完成了,进程之间的合作。

总结:如何实现多进程图像?
  1. 读写PCB
  2. 要操作寄存器完成切换
  3. 要写调度程序
  4. 要有进程同步与合作
  5. 要有地址映射

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bhhbAGFH-1684932615752)(D:\笔记图片\Snipaste_2023-05-10_11-23-05.png)]

L10 用户级线程
1 由进程切换引出线程的概念

(1)上节课说到,多进程运行的时候需要来回切换,并且映射表也要对应的切换。而现在能不能有一种直接在当前内存中使用,直接切换运行指令的方法呢?因为这样会节约资源,效率更高一点。这就引出了线程。即在一个进程中依旧可以执行不同的操作,比如浏览器就是这样,浏览器的工作方式就是,你打开一个浏览器显示网页,一个作者的设计显示规则是先把所有资源下载完毕,然后才可以显示。这样就会导致效率极差,并且等待的时间也是很长的。

(2)所以就要利用线程,在一个浏览器进程中使用多个线程来执行不同的操作,如下图:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lDFPOjA4-1684932615752)(D:\笔记图片\Snipaste_2023-05-10_14-24-25.png)]

这样就会使这个进程的效率大大的提升了。比如下面是一个浏览器的大致代码:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OPEvdoVV-1684932615752)(D:\笔记图片\Snipaste_2023-05-10_14-26-52.png)]

首先,进入该函数创建一个线程用来获取数据,再创建一个线程用来显示文本,如上的执行方式大大节约了时间。所以,由此可知,没有CPU就没有进程可言,没有进程就没有线程可言。

2 线程之间的来回切换的方式

(1)线程切换的核心是:Yield(),而Create就是要制造出第一次切换时应该的样子(Yield实现切换的基础)。如下图执行Yield()函数,两个执行序列对应一个栈:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ntSkkRa7-1684932615752)(D:\笔记图片\Snipaste_2023-05-10_14-41-56.png)]

首先,执行(1)操作调用B函数,然后将B函数的返回地址压栈,然后执行(3)时调用了Yield()函数,Yield()函数写了要跳转的下一个执行序列的地址,跳转到(2)处,此时再执行C函数,执行时将跳转到D函数,将D函数的返回地址压栈,最后执行D时又要执行Yield(),此时Yield()函数指出要跳转到204的地址去将404压栈,如上图压栈的顺序。但是,当接着执行时发现当第二次执行Yield()时,就跳到了B函数,然后B函数就要返回了,而B应该返回到A函数中去,但是此时从栈中弹出来的确实D函数的地址,这样就导致了错误。为什么会产生这个问题呢?是因为该程序再执行两个不同的操作,就应该有两个栈来分别管理对应的执行序列。

(2)这次是两个执行序列,对应两个栈。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v8ywBUL2-1684932615753)(D:\笔记图片\Snipaste_2023-05-10_14-49-18.png)]

那就让两个执行序列拥有一个属于自己的栈,所以执行Yield函数的时候要先切换栈,而且还要能够回到这些栈中,我们需要存储这个栈的地址。其中线程也有类似于存储进程信息的数据结构TCB。上述的操作就是在切换线程执行时,首先先切换线程对应的栈,其中该栈就是存储在TCB中,就是上述Yield()函数的代码。

(3)线程的核心就是用程序实现下面三样东西(TCB,栈,TCB和栈建立联系),下面看看代码[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0qUoxy07-1684932615753)(D:\笔记图片\Snipaste_2023-05-10_19-55-14.png)]

如上述创建线程的函数,threadCreate()

void ThreadCreate(A)
{
	TCB *tcb=malloc();		//申请TCB
	*stack=malloc();		//申请栈
	*stack = A;				//向栈中压入数据
	tcb.esp=stack;			//将栈和TCB建立联系
}
3 为什么叫用户级线程

(1)由于Yield()是用户级的程序,执行在用户层面上。用户级线程只是在用户界面上进行操作,内核感受不到它的存在。既然有用户级线程,那么是否有内核级线程呢?答案是有的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fhC03ykM-1684932615753)(D:\笔记图片\Snipaste_2023-05-10_20-14-19.png)]

(2)当然用户级线程也是有缺点的,例如,在浏览器中请求url访问时,要访问网卡,但是网卡是硬件,想要访问硬件就要通过内核,当浏览器卡了之后,内核感受不到线程的存在,就会切换到其他的进程上面,所以浏览器就会一直卡着不动。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VQby242N-1684932615754)(D:\笔记图片\Snipaste_2023-05-10_20-14-39.png)]

(3)与用户级线程不同的是,核心级线程是通过内核创建的线程,线程的TCB实在内核中记录的,当一个线程阻塞之后,可以检测到其他的线程,所以还是可以运行的。所以核心级线程的并发性会更好。

L11 内核级线程
1 多核处理器(一个CPU)与多CPU处理器

(1)一个CPU多核处理器,是每个核公用一套MMU(映射表),与多线程一样只是移动PC不改变映射内存。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MPNQREoo-1684932615754)(D:\笔记图片\Snipaste_2023-05-10_20-26-06.png)]

(2)而多CPU则是每个CPU都是用的自己的一套映射,都是独立的。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lms9I3lo-1684932615754)(D:\笔记图片\Snipaste_2023-05-10_20-25-51.png)]

2 开始内核级线程

**注:进程实际在内核中,没有用户级进程的说法。实际上切换进程就是切换内核级线程。**以及用户级线程是内核级线程的一个部分。

以及为什么只有内核级进程没有用户级进程?是因为进程是要消耗资源的,要访问内存,这些都是计算机硬件,所以要到内核才可以。

(1)内核级线程正好对应的多核处理器,共用一个MMU(映射区)。如下图

在用户区进程的每个事件处理由内核的线程进行操控,每一个线程就对应着一个核若没有内核级多线程处理,则多核处理器是没作用的),所以多个线程到内核中才可以利用多个核实现并行,每个核的MMU(内存映射区)不变,这不就是多线程嘛。

注:并发:同时出发,交替执行。并行:同时出发,同时执行。以及用户级线程不被内核知道,所以系统无法分配给硬件资源(核),所以就无法实现并行,无法充分使用多核。(用户级缺点)

3 核心级线程是如何实现的?
(1)由用户级线程引出内核级线程

由前一讲可知,用户级线程在切换时,需要属于自己线程的,因此,内核级线程也是如此,但不同的是内核级线程需要的是一套栈(其中要含有用户栈和内核栈)。因为在内核进行切换线程时,程序执行的代码还在用户,所以用户对应栈的也要进行切换,所以就需要两个栈。

所以,用户级线程与核心级线程的最大的不同就是:用户栈用的单个栈,而核心级线程用的是一套栈。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gQWMh5rC-1684932615755)(D:\笔记图片\Snipaste_2023-05-10_22-17-50.png)]

并且内核级线程进行切换是根据TCB将内核栈与用户栈都要进行切换。

(2)用户栈与内核栈之间的关联

a 在之前讲过,用户区只有通过中断才可以进入到内核区,所以当程序在用户栈中执行时,只有遇到中断命令(可以是int指令)才可以进入到内核中。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ejoT73Xc-1684932615755)(D:\笔记图片\Snipaste_2023-05-10_22-40-57.png)]

如上用户栈在执行时遇到了int指令,进入了内核栈然后将用户栈的一些信息 (用户栈的地址,执行命令的地址等如下)压入内核栈中。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JauRfLiS-1684932615755)(D:\笔记图片\Snipaste_2023-05-10_22-38-55.png)]

再然后,当遇到IRET时就一次将内核栈中的5条指令弹出,根据信息返回到用户态去

具体执行一个例子:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7oRZYpsa-1684932615755)(D:\笔记图片\Snipaste_2023-05-10_23-21-04.png)]

过程:首先执行A函数,然后跳到B函数将B函数的返回地址(104)压入用户栈,在B函数执行read()函数,也将read()函数的返回地址(204)压入用户栈,最后执行read()函数。在执行read()函数时,遇到int的中断,跳到内核中去执行。由内核将SS,SP连接好,并且在内核栈中记录read()函数的返回地址(304)。然后再内核中执行相应的代码

(3)内核中switch_to五段切换

a 在上述的执行中,最后执行到sys_read(),进入到内核中执行带段代码,read()在执行时会启动磁盘读,就会引起磁盘的阻塞,当遇到阻塞时,就会引起调度。找下一个可执行的线程。其中switch_to()就是切换线程的函数,cur表示当前线程的TCB,而next表示下一个线程的TCB。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8PvNyDcT-1684932615756)(D:\笔记图片\Snipaste_2023-05-11_22-38-37.png)]

如上图所述,就是在线程S阻塞时,内核要切换到线程T中去。也是如上述的例子一样,先执行用户栈中的代码遇到中断后进入到内核中,然后执行内核中的相应的代码,所以线程T中的PC与CS就是上述例子中的发生中断的函数返回地址。而四个问号(???)就是一小块代码包括iret的返回指令。注:所以内核级线程间的切换就是两个进程间的两套栈之间进行切换。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KTAav6j6-1684932615756)(D:\笔记图片\Snipaste_2023-05-11_22-39-04.png)]

b switch_to()的五段论 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4jpkSPmg-1684932615756)(D:\笔记图片\Snipaste_2023-05-11_22-56-20.png)]

首先,先在用户栈中执行该线程,当遇到中断时进入到内核中,进行中断处理时如阻塞则就会进行线程的切换,最后一个iret就完成了用户栈的切换。就如上述图片中展示的过程一样。(图中右上角的是讲述进程切换时还需要一个东西)

补充TCB 存储线程的上下文信息,包括线程的状态堆栈指针以及 CPU 寄存器的所有值。当操作系统进行线程切换时,需要从 TCB 中读取线程的上下文信息并将 CPU 寄存器的值恢复到其之前保存的状态,以确保线程的正确执行。

内核栈与 TCB 不同,它用于存储与系统调用或中断相关的临时数据,例如参数、状态以及各种寄存器的值。当进程执行系统调用或触发中断时,操作系统需要将当前线程的执行上下文信息保存在内核栈中,然后转换到内核态来处理中断或系统调用。在处理完成中断或系统调用后,操作系统会从内核栈中恢复之前的执行上下文信息,以便恢复之前的执行状态并返回到用户态。

你可能感兴趣的:(笔记,linux)