操作系统
计算机组成原理
在将操作系统前我们讲一下有关计算机组成的硬件知识
寄存器是CPU内部的原件,寄存器拥有非常高的读写速度,所以在寄存器之间的数据传送非常快。电脑的运行,实际上就是CPU和相关寄存器以及RAM之间的事情
寄存器作用:
- 将寄存器内的数据执行算术即逻辑运算。因为CPU最后执行运算时都是对寄存器进行操作,寄存器的速度决定了CPU的速度。我们最后在对数据操作的时候,需要根据相关地址寄存器去内存中获取数据,然后加载到数据寄存器中,执行算术运算。数据在内存上是没法进行运算的,必须读入寄存器
- 存于寄存器的地址可用来指向内存中的某个位置
- 80806有14个16位的寄存器,可分为通用寄存器,标志寄存器,段寄存器等。8086设定了8个8位的数据寄存器以及4个段寄存器
当一个程序执行的时候,需要知道程序代码、数据和堆栈各要用到内存中的哪些位置。通过设定段寄存器CS,DS,SS来指向这些起始位置。
进程与线程
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,是系统进行资源分配的一个独立单位。
用户运行程序的时候,会为它分配除了CPU的各种资源,如内存、磁盘和I/O设备等,然后进程进入进程就绪队列(注意不是进入运行状态)。然后进程调度程序选中他,为它分配CPU及其他相关资源,然后该进程开始运行。
有了进程为什么要引入线程:
- 易于调度
- 提高并发性
- 开销小。创建线程比创建进程要快,所需要的开销也小
- 有利于发挥多处理器的性能。通过创建多线程,每个线程都在一个处理器上运行,从而实现应用程序的并行,使得每个处理器都得到充分运行。
不同点
- 一个线程必定属于也只能属于一个进程;而一个进程可以拥有多个线程并且至少拥有一个线程。
- 属于一个进程的所有线程共享该进程的所有资源,包括进程打开的文件,创建的socket
- 进程有进程控制块,线程有线程控制块。但线程控制块比进程控制块小很多。线程间切换代价小,进程切换代价大
- 进程是一次程序的执行,线程可以理解为程序中一段程序片段的运行。
- 每个进程都有独立的内存空间,而线程共享其所属的内存空间。
线程具体提出方式
多个任务如何执行?逐一分配CPU,在CPU看来每个任务都是轮着来。一个进程在得到cpu占用之前,相关内存资源以及外设必须全部就位,除了cpu之外的其他资源称之为上下文环境。对于程序A与B,如何交替执行:
先加载程序A的上下文,然后开始执行A,保存程序A的上下文,调入下一个要执行的程序B的程序上下文,然后开始执行B,保存程序B的上下文……….
进程包括上下文运行环境切换的程序执行时间总和=cpu加载上下文+cpu执行+cpu保存上下文
由上可以看出,每次切换进程将涉及程序频繁的上下文调入和保存,假设将进程比喻为一个正在运行的程序软件,那么一个软件的执行不可能是一条逻辑执行,一定有多个分支和程序段。即进程A,实际分为a,b,c三个程序段,那么变为程序A得到CPU———cpu加载上下文——–开始执行程序A的a小段———执行程序A的b小段———–执行程序A的c小段,做后保存A的上下文。
这里的a,b,c的执行是没有上下文切换的,共享了进程A的上下文。这里的a,b,c就是线程。总结一下:线程abc共享了进程的上下文环境,为更细小的时间段。进程与线程都是一个时间段的描述,是cpu工作时间段的描述,不过是颗粒大小不同。
线程同步
线程同步互斥的控制机制:临界区,互斥量,信号量和事件
- 临界区:通过对多线程的串行化来访问公共资源或者一段代码。速度快,适合控制数据访问。
- 互斥量:只有拥有互斥锁的线程才有权访问系统的公共资源,因为互斥对象只有一个,所以可以保证同一时间公共资源不会被多个线程访问。互斥不仅能够实现统一应用程序的公共资源安全共享,还能实现不同应用程序的公共资源安全共享
- 信号量:为控制一个具有有限数量的用户资源而设计。它允许同一时刻有多个线程访问同一资源,但一般需要限制同一时刻访问此资源的最大线程数目。
- 事件:用来通知线程有一些事件已经发生,从而启动后继续任务的开始。
临界区和互斥量与信号量的区别:
互斥量和信号量在系统中任何进程里都是可见的。一个进程创建了一个互斥量和信号量,另一个进程可以试图去获取该锁。然而,临界区的作用范围仅在本进程,其他的进程无法获取该锁。
内核线程和用户线程的区别以及系统进程和用户进程的区别
根据对操作系统内核是否对线程感知,可以把线程分为内核线程和用户线程
内核线程建立和销毁都是由操作系统负责、通过系统调用完成的。操作系统在调度时,参考各进程的线程运行情况做出调度决定。如果一个进程中没有就绪态的线程,那么这个进程也不会被调度占用CPU。
用户线程:用户线程指不需要内核支持而在用户程序中实现的线程,其不依赖于操作系统的核心,用户进程利用线程库提供创建、同步、调度和管理线程的函数来控制用户线程。
用户线程相关的缺点:
- 同一进程中只能同时有一个线程在运行,如果一个线程使用了系统调用而阻塞,那么整个进程就会被挂起。
系统进程与用户进程:
- 系统进程:可以执行内存资源分配和进程切换等管理工作。而且,该进程的运行不受用户的干预,即使root用户也不能干预系统进程的运行
- 用户进程:通过执行用户程序、应用程序或者内核之外的系统程序而产生的进程,此进程可以在用户的控制下运行和关闭
内核栈与用户栈的区别
每个进程一般会有两个栈,一个用户栈,一个内核栈,存在于内核空间。
当进程在用户空间运行时,cpu堆栈指针寄存器里面的内容时用户堆栈的地址,使用的是用户栈。
当进程在内核空间时,cpu堆栈指针寄存器里的内容是内核栈空间的地址,使用内核栈。
内核栈是内存中属于操作系统空间的一块区域,主要用途为:
- 保护中断现场
- 保护操作系统子程序间相互调用的参数、返回值、返回点以及子程序函数的局部变量
用户栈是用户进程空间中的一块区域,用于保存用户进程的子程序间相互作用的参数、返回值以及相关局部变量
当进程因为中断或者系统调用而陷入内核态,进程所使用的堆栈也要从用户栈转到内核栈。进程陷入内核态后,先把用户态堆栈的地址保存在内核栈中,然后设置堆栈指针寄存器的内容为内核栈的地址,这样就完成了用户栈到内核栈的转换。当进程从内核态回复为用户态时,在内核态之后的最后将保存在内核栈里面的用户栈地址恢复到堆栈指针寄存器即可。这样就实现了内核栈和用户栈的互转。
注意:每次进程从用户态陷入内核的时候得到的内核栈都是空的,所以在进程陷入内核的时候,直接把内核栈的栈顶地址给堆栈指针寄存器即可。
从资源调度理解操作系统作用
处理器管理:哪个程序占有处理器运行?
内存管理(主存储器管理):程序/数据在内存中如何分布?
设备管理:如何分配调度程序将要使用的设备,设备管理
设备管理:驱动程序的定义,最底层的、直接控制和监视各类硬件资源的部分。
信息资源管理(文件管理):如何访问文件信息?
信号量资源管理:如何管理进程之间的通信?
资源的共享与分配方式
资源共享方式
- 独占使用
- 并发使用
资源分配策略
静态分配方式
程序在进入内存前所分配的内存资源相关资源,缺点,效率低
动态分配方式
谁用资源就分给谁,容易造成死锁
资源抢占方式
问题:抢占后如何处理资源回滚
从程序控制的角度理解操作系统
CPU与IO速度相差较大,为了使得cpu和外围设备充分并行,让多道程序同时进入内存争抢cpu资源。进程A在使用外围设备的时候将cpu分配给程序B使用。
多道程序设计优点:
cpu与外围设备充分并行;外围设备间充分并行
为了实现多道程序系统设计,需要为进入内存执行的程序建立管理实体:即我们的进程。那么操作系统应该负责:
- 如何调度管理进程
- 内存等资源如何分配
- 其他资源比如外设的管理和分配
开机时操作系统做的事情
读取并执行引导扇区的代码
- 计算机硬件组成:输入设备、输出设备、存储器、运算器、控制器
- 寄存器是CPU内部组件
该结构与计算机非常相似。设置控制器动作,即将控制逻辑读入控制器。对于计算机而言,将QQ程序读入计算机控制器,相当于将QQ程序运行逻辑读入了控制器。(具体的是,将QQ程序存到内存里,将QQ程序加载到CPU又叫控制器中解释执行)
该图显示的是一些列取指令执行的操作。左边存储器相当于存储的一个程序,将该程序的运行逻辑一步步读入内存执行。
- 硬盘的引导扇区:存储着开机后执行的第一段我们可以控制的程序,即启动设备的第一个扇区。
计算机的执行是取指令执行的过程,而操作系统是放在硬盘上的,所以开机需要将硬盘上的操作系统读入内存。具体方法是,开机时会自动执行引导扇区的代码(bootsect.s汇编程序),将操作系统读入内存(控制器只能与内存数据交互,不能与硬盘数据交互)。读入内存后,再进行初始化。
- 对于电脑应用程序而言,如何使用底层硬件是通过操作系统提供的接口完成。操作系统向应用程序提供调用接口,实现应用程序对硬件的控制。
红色部分为操作系统内核,蓝色部分为用户程序。操作系统放在内存中低地址部分,用户程序放在高地址。用户程序通过系统调用操作系统,不能通过直接在内存中直接寻址jump内存任意地址。即引出内核态和用户态的区别。
- 内核(用户)态,内核(用户)段
- 用于将内核程序和用户程序隔离
- 内核态可以访问任何数据,用户态不能访问内核数据
系统调用:为用户程序提供进入操作系统内核的方法。通过中断进入操作系统内核。所以用户程序一般包含int指令中断代码。
进程与线程
操作系统在管理CPU的时候引出了多进程设计
操作系统管理CPU,即尽量让CPU运行效率最高。
CPU工作原理:程序放在内存,CPU从内存取指令执行。即程序的执行是CPU与内存的交互结果。
IO指令非常慢,当有程序执行IO时,可以让出CPU给其他程序(多程序并发)。
PCB进程控制块
多道程序切换,比如程序1切换为程序2,需要保存程序1切换时的状态信息,包括程序1下一条指令地址以及中间运算结果。即每个程序都有一个存放信息的结构,称之为PCB。
每个读入内存的进程都有PCB控制块。
操作系统只需把多个进程记录好,并对每个进程分配资源、进行调度
计算机启动,实际上起了一个进程即1号进程,基于1号进程可以让计算机切换到其他用户程序。
在Windows任务管理器,可以看到开启的进程。用户使用计算机就是起了很多的进程
多进程如何组织(多线程组织、进程切换、分离多进程(映射表)、多进程同步)
通过PCB组织管理多个进程。(进程队列(就绪队列,阻塞队列),PCB)
进程调度:FIFO,优先级调度
- 进程切换:涉及PCB的修改。当运行的进程1要切换到进程2时,将当前CPU的状态用于更新进程1的PCB,即记录进程1运行的上下文。然后将进程2的PCB读入CPU,执行进程2.
多进程的内存冲突
进程1与进程2都放置在内存中,进程1需要修改内存某个地址的数据,但这个地址是存放进程2的代码的。这样就会导致进程2的执行崩溃。
多进程同步
给counter上锁,使得生产者进程执行到一定程度才能去执行消费者进程。
进程切换(用户级线程)
每个进程=资源+指令执行序列
那么进程切换可以采用分治的策略:分为指令序列切换(即线程切换)+映射表切换(进程数据以及资源通过映射表的方式存于内存中,避免冲突)
多线程的引出:对于一个浏览器进程而言,打开一个网页,有从服务器下载数据的线程,有负责显示数据的线程。数据的下载与数据显示应该交替执行,这就是线程的切换。对于上述两个线程而言,下载数据线程将数据下载到缓冲区,读取数据线程从缓冲区读取数据,这两个线程共享一个缓冲区。假如将上述两个线程改为不同的进程,那么程序访问的是不同的缓冲区地址(因为不同进程的资源区的映射表不同,在内存中肯定是互不重叠的)。
- 线程的切换只是指令的切换,而进程的切换还包括资源区的切换。
- esp是全局变量,每个线程都会维护各自的栈,当线程切换的时候,esp需要被赋值为相应线程栈的地址。
- 线程控制块TCB
2个线程:包含两个TCB、两个栈、切换的PC在各自栈中。线程的内部结构如下:
其中执行序列如左图所示。每次切换线程,都需要切换线程对应的指令栈。
- 执行yield时:需要切换栈(esp放到当前TCB中,并从下个TCB中取出esp),执行完弹栈切换线程
用户级线程创建没有进入操作系统创建,核心级线程创建的时候是系统调用,需要进入操作系统内核,操作系统知道每个线程对应的TCB。与用户级线程不同的是:核心级线程的创建使用Schedule来创建。
用户级线程
每个线程都需要创建一个自己的栈。在切换线程的时候需要切换栈。这就需要一个数0据结构TCB(线程控制块)来存储栈的指针;每一个线程都有一个TCB
https://blog.csdn.net/qq_38038480/article/details/80437350
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源. 一个进程中的所有线程共享该进程的地址空间,但它们有各自独立的(/私有的)栈(stack)。线程切换的时候实际上切换的是一个可以称之为线程控制块的结构TCB。里面保存所有将来用于恢复线程环境所必须的信息,包括所有的寄存器值,线程的状态等等
核心级线程
多核要想发挥作用,必须要有核心级线程。
MMU:内存映射表。
多核与多处理器的区别:
多核:多个CPU共享Cache和MMU映射表。这个有点像多线程,多个执行序列共用一套映射,多个线程共享资源空间。
多个线程到内核里才能充分利用多核,每个指令序列(线程)进入内核会被分配到一个核(CPU)上。
并发和并行的区别。解释了为什么不是多进程对应多核?
如果是用户级线程,操作系统内核是无法看见这些线程的,也就不能为这些指令序列分配内核。
内核管理TCB,内核负责切换线程
用户级线程的切换:TCB切换,用户栈跟着切换
核心级线程的切换:TCB的切换对应两套栈的切换,线程切换的时候,不仅需要切换核心栈,还需要切换用户栈。
首先,需要明确,用户程序进入内核的唯一方法:通过中断int的方法。如果启用中断指令,那么从用户栈进入内核栈。并且记录用户栈的指针SS与SP记录在内核栈中。
核心线程的切换(5段论):
用户态下通过中断进入内核
执行中断函数,当遇到磁盘读写等线程S 阻塞。
在线程队列中找到next,已确定下一个将要执行的线程。
通过swich_to完成内核栈的切换。线程S的内核栈切换到线程T的内核栈
中断返回。从内核栈到用户栈的切换。从内核栈中弹出对应的数据
内核级线程的代码实现
当调用中断的时候,内核栈的存储数据如上图所示。
上述为核心级线程的创建过程,包括:申请内存空间,创建TCB,创建内核栈和用户栈;初始化栈;关联栈和TCB
操作系统的那棵“树”
总结之前所有内容
CPU调度(schedule)策略:将CPU分配给哪个进程
将CPU分配给就绪队列中哪个进程
吞吐量:进程切换次数多,系统内耗大,那么吞吐量变小。(有很多时间被用于进程调度,切换栈等)
想在响应时间上表现好的调度算法:轮转调度(时间片调度)
前台任务使用时间片轮转调度,后台任务使用短作业优先调度。前台进程的优先级大于后台任务的优先级
需要解决的问题:
- 前后台任务都用时间片轮转,但无法体现前后台任务的优先级
- 后台任务在时间片轮转上如何体现短作业优先
- 在创建任务的时候,我们不知道哪个是前台任务,哪个是后台任务
一个实际的CPU调度函数Schedule函数
既有前台任务,又有后台任务
counter是优先级的意思,针对一个变量counter,既实现了时间片轮转,也实现了优先级调度
优先值越低,优先级越高。重置优先级时,执行IO的那些进程重置后比之前就绪态counter魏0的优先级大。IO时间越长,他的counter优先级重置后越高。那么,即被阻塞的时间越久,那么以后它的优先级越高。而IO进程正是前台进程的特征。
进程同步与信号量
进程同步:进程走走停停相互通知对方执行
- 信号:信号有还是没有,但光有信号的是否是不够的。所以需要信号量
如何解决P2不能唤醒的问题:引入信号量,不仅需要知道缓冲区的空闲个数,还需要知道等待的进程的个数。、
信号量:记录等待的进程的数量
value为资源对应的信号量
其中mutex为互斥信号量,允许只允许一个进程操作缓冲区。empty与full定义有多少个进程在等待
信号量临界区保护
什么是信号量:整形变量,通过对这个变量的修改和访问,让各个线程有序推荐。
竞争错误:
- 错误由多个进程并发操作共享数据发生
- 解决方法:给共享变量上锁,执行原子操作
临界区:一次只允许一个进程进入该进程的那一段代码。比如P1和P2的临界区是empty–,一次只允许其中一个进程执行empty–。
注意:读写信号量的代码一定是临界区
互斥原则:如果一个进程在临界区中执行,则其他进程不允许进入临界区
2个进程临界区保护的算法:PeterSon算法
多个进程临界区保护的算法:面包店算法
信号量值为1的就是锁,修改信号量需要临界区保护
进程死锁
因为信号量的问题造成死锁
mutex,empty。这个mutex是什么意思?
- mutex的意思在于:对信号量的修改与访问涉及到临界区的问题,mutex就是设置这样一个标志,包以一个时间只允许一个进程访问临界区(互斥信号量,这个表示一次只有一个进程进去)
- 我们的缓冲区就是信号量,mutex为互斥信号量,表示同一时间只能有一个进程需改缓冲区资源
进程死锁的原因:
- 资源互斥使用,一旦资源被占有别人将无法使用
- 进程占有了一些资源不释放,再去申请其他资源
- 造成环路等待
- 资源不可抢占
死锁避免:判断此次请求是否引起死锁(银行家算法)
找安全序列的银行家算法:Dijkstra提出