用户态:当一个进程在执行用户自己的代码时位用户态,可以直接读取用户程序的数据
内核态:控制计算机的硬件资源,并提供上层应用程序运行的环境
系统调用:为了使用户态运行的上层应用能够访问到内核态的资源,内核为上层应用程序开放的接口
CPU时钟:用于同步通信
一、进程管理
1.1 进程
进程是程序的一次执行,是资源分配的基本单位。进程控制块(PCB)描述进程的基本信息和运行状态。创建进程实际上是创建进程实体中的PCB,撤销进程,实质上是撤销进程的PCB。
- 进程的三种基本状态
1.就绪状态:进程只要获得CPU就可以立即执行
2.执行状态:进程已获得CPU,其程序正在执行
3.阻塞状态:由于缺少需要的资源,但不包括缺少CPU时间。 - 进程的创建
1.申请空白PCB
2.为新进程分配资源
3.初始化进程控制块 - 孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么这些子进程将成为孤儿进程。孤儿进程将被init进程收养,并由init进程对它们完成状态收集工作。
- 僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存再系统中。
- 守护进程:在后台运行,不与任何终端关联的进程,通常情况下守护进程在系统启动时就在运行,并处理一些系统级的任务。
1.2 进程同步
- 临界资源:各进程以互斥的方式,实现对临界资源的共享
- 临界区:每个进程中访问临界资源的那段代码
- 信号量
为使多个进程能互斥的访问临界资源,只须为该资源设置一互斥信号量mutex。信号量是一个整型变量,可以对其进行down和up操作。 -
管程机制
一个管程定义了一个数据结构和能为并发进程所执行的一组操作,这组操作能同步进程和改变进程中的数据。管程有一个重要特性:在一个时刻只能有一个进程使用管程。进程无法继续执行的时候不能一直占用管程,否则其他进程永远不能使用管程。
1.3 进程通信
- 管道:用于具有亲缘关系的父子进程间或兄弟进程间的通信。只支持半双工通信(单程交替传输)
- 命名管道(FIFO):去除了管道只能在父子进程中的限制,严格遵循先进先出
- 消息队列:
1.消息队列可以独立于读写进程存在
2.避免了FIFO的同步阻塞问题,不需要进程自己提供同步方法
3.读进程可以根据消息类型有选择地接收消息。 - 信号量
- 共享存储:允许多个进程共享一个给定的存储区。所以数据不需要在进程之间复制。使用互斥锁和信号量等来同步对共享存储的访问
- 套接字:用于不同主机的进程之间进行的通信。
1.4 进程的调度算法
- 先到先服务调度算法:从就绪队列中选择一个最先进入该队列的进程为之分配资源。使它立即执行并一直执行到完成或发生某事件而被阻塞放弃占用CPU时间再重新调度。
- 短作业优先调度算法:从就绪队列中选出一个估计运行时间最短的进程为之分配资源。
- 最短剩余时间优先算法:按剩余运行时间的顺序进行调度。
- 时间片轮转调度算法:每个进程被分配一个时间段,称作它的时间片,即该进程允许运行的时间。
- 优先级调度:为每个进程分配一个优先级,按优先级进行调度
- 多级反馈队列:设置了多个多列,每个队列时间片大小不同,进程在第一个队列没执行完,就会被移到下一个队列。每个队列的优先权不同,最上面的优先权最高。
1.5 线程
线程是独立调度的基本单位,一个进程中可以有很多个线程,它们共享进程资源。
- 与进程的区别
1.进程是资源分配的基本单位,但是线程不拥有资源 ,线程可以访问隶属进程的资源。
2.线程是独立调度的基本单位。同一进程中,线程的切换不会引起进程切换,从一个进程中的线程切换到另一个进程中的线程,会引起进程切换。
3.创建和撤销进程的开销远远大于线程的。 - 线程间的同步方式
1.互斥量:采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。
2.信号量:允许同一时刻多个线程访问同一资源,但是需要控制同一时刻访问此资源的最大线程数量
3.时间:Wait/Notify 通过通知操作的方式来保持多线程的同步。
1.6 fork()函数
fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间 。然后把父进程所有的值都复制到子进程中,只有少数值和原来进程的值不同。在fork函数执行完毕后,如果创建新进程成功,则出现两个进程,一个是子进程,一个是父进程。在子进程中,fork函数返回0,在父进程中,fork返回新创建子进程的进程ID。
1.7 Linux进程管理指令
- ps 查看进程
ps -l #查看自己的进程
ps aux #查看系统所有进程
ps aux | grep xxxx #查看xxxx线程
- top 监控Linux的系统状况
top #查看全部进程
top -p xxx #查看某个进程信息
- netstat 查看端口占用情况
netstat -anp | grep 8080 # 查看8080端口的使用情况
1.8 Java程序cpu占用过高的排查思路
1.用top指令找出cpu占比异常高的进程
2.用ps aux | grep xxx定位某个进程
3.定位到具体的线程 ps -mp 进程编号
4.jstack -l查看输出
1.9 协程
协程是一种比线程更加轻量级的存在,一个线程可以拥有多个协程。协程完全由程序所控制,也就是在用户态执行。这样带来的好处是性能大幅度的提升,因为不会像线程切换那样消耗资源。协程仅仅是一个特殊的函数,和进程、线程不是一个维度的。多个协程在一个线程内是串行执行的。
二、死锁
多个进程在运行过程中因争夺资源而造成的一种僵局
- 产生死锁的原因:
1.资源竞争
2.进程间推进顺序非法 - 产生死锁的必要条件:
1.互斥条件:在一段时间内某资源只由一个进程占用
2.请求和保持条件:已经得到了某个资源的进程可以再请求新的资源
3.不剥夺条件:线程已获得的资源,在未使用完之前,不能被剥夺
4.环路等待条件:有两个或两个以上的进程组成一条环路,该环路中的每个进程都在等待下一个进程所占有的资源。
2.1 处理死锁的基本方法
- 预防死锁
1.破坏互斥条件:
2.破坏请求和保持条件:一次性申请在整个运行过程中需要的资源
3.破坏不剥夺条件
4.破环环路等待条件:给资源统一编号 - 死锁避免
1.安全状态
定义:如果没有死锁发生,并且即使所有进程突然请求对资源的最大需求,也存在某种调度次序能使每一个进程运行完毕,则称该状态是安全的。
2.银行家算法
银行家算法的数据结构:
1.可利用资源向量Available
2.最大需求矩阵Max
3.分配矩阵Allocation
4.需求矩阵Need(尚需的各类资源数)
设Request是某进程i的请求向量,如果Request[j]=K,表明需要K个Rj类型的资源:
1)如果Request[j]<=Need[i,j],转向步骤2,否则出错,因为它需要的资源数已超过它宣布的最大值
2)如果Request[j]<=Available[j],转向步骤3,否则没有足够资源
3)系统尝试着把资源分配给Pi,并修改下面数据结构的数值:
Available[j]:=Available[j]-Request[j]
Allocation[i,j]=Allocation[i,j]+Request[j]
Need[i,j]=Max[i,j]-Allocation[i,j]
4)系统执行安全性算法,检查此次资源分配后系统是否处于安全状态。如果安全才给分配资源。
- 死锁检测
- 死锁恢复
1.利用抢占恢复
2.利用回滚恢复
3.通过杀死进程恢复
三、内存管理
操作系统的内存管理主要负责内存的分配与回收,以及地址转换,即将逻辑地址转换成相应的物理地址。
3.1 内存管理机制
- 块式管理
将内存分为几个固定大小的块,每个块只包含一个进程。如果进程需要内存的话,分配给它一块。 - 页式管理
把主存分为大小相等且固定的一页一页的形式,页较小,相对相比块式管理的划分力度更大,提高了内存利用率,减少了碎片。页式管理通过页表对应逻辑地址和物理地址。 - 段式管理
页式虽然提高了内存利用率,但是页式管理其中的页实际并无任何实际意义。段式管理把主存分为一段段的,每一段的空间又比一页的空间小很多。同时,段是有实际意义的,每个段定义了一组逻辑信息。一个段构成一个独立的地址空间。 - 段页式管理:
是分段和分页原理的结合,先将用户程序分成若干个段,再把每个段分成若干个页。这样即拥有分段系统的共享与保护,又有分页系统的虚拟内存功能。
3.2 分页与分段的比较
- 对程序员的透明性:分页透明,但是分段需要程序员显式划分某个段。
- 地址空间的维度:分页是一维地址空间,分段是二维的。
- 大小是否可以改变:页的大小不可变,段的大小可以动态改变
- 出现的原因:分页主要用于虚拟内存,从而获得更大的地址空间。分段主要是为了使程序和数据可以被划分为逻辑上独立的地址空间并且有助于共享与保护。
- 二者都是为了提高内存利用率,减少内存碎片
- 二者都是离散存储的,但每个页和段中的内存都是连续的
- 页的大小是固定的,由操作系统决定,段的大小不需要固定,取决于当前运行的程序
- 分页仅仅是为了满足操作系统的内存管理要求,段是逻辑信息的单位,在程序中可以体现为代码段,数据段,能够更好满足用户的需要。
3.3 页面置换算法
如果要访问的页面不在内存中,就发送缺页中断,从而将该页调入内存中。此时如果内存已无空闲空间,系统必须从内存中调出一个页面到磁盘对换区来腾出空间。
- 最佳:所选择的被换出的页面将是最长时间内不再被访问,通常可以获得最低的缺页率。是一种理论上的算法,因为无法知道一个页面多长时间不再被访问。
- 最近最久未使用(LRU):将最近最久未使用的页面换出。需要在内存中维护一个所有页面的链表,当一个页面被访问时,将这个页面移到链表的表头,这样保证链表尾的页面是最近最久未访问的。
- 最久未使用(NRU):每个页面都设两个状态位:R和M,当页面被访问时设置页面的R=1,页面被修改时设置M=1。其实R位会定时清零。当发生缺页中断时,随机从编号最小的非空类中挑选一个页面将他换出。NRU优先换出未访问的被修改页面(R=0,M=1)而不是访问过的赶紧页面(R=1,M=0)。
- 先进先出:会使经常被访问的页面被换出,从而使缺页率升高。
- 第二次机会算法:为了对先入先出算法进行改进。设置状态位R,需要替换时先检查最老位,如果R为0,换出,如果R为1,R置零,并放到链表尾端。
- 时钟:对第二次机会算法做出改进,因为第二次机会算法需要在链表中移动页面,降低了效率,时钟算法使用环形链表将页面连接起来,再使用一个指针指向最老的页面。
3.4 虚拟内存
虚拟内存的目的是为了让物理内存扩充成更大的逻辑内存,从而让程序获得更多的可用内存。虚拟内存不用将地址空间中的每一页都映射到物理内存,也就是说一个程序不需要全部装入内存就可以运行,这样操作系统就可以运行比其物理内存更大的程序。
3.5 快表
操作系统在页表的基础之上引入了快表,来加速逻辑地址到物理地址的转换。可以把快表理解为一种特殊的高速缓冲存储器,其中的内容是页表的一部分或全部内容。快表的作用与页表相似,但是提高了访问速率。没有快表时,读写内存数据时CPU要访问两次主存,有了快表,有时只须访问一次高速缓冲存储器,一次主存。
四、设备
4.1 磁盘调度算法
- 先来先服务
优点:简单
缺点:未对寻道做任何优化,使平均寻道时间可能较长 - 最短寻道时间优先:优先调度与当前磁头所在磁道距离最近的磁道,但容易出现饥饿现象
- 电梯算法:总是按一个方向来磁盘调度,直到该方向上没有未完成的磁盘请求,然后改变方向。