0. 僵尸进程/孤儿进程
一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵尸进程。
一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
1.进程三状态
核心的状态是运行,就绪,阻塞。另外还加入了新建和退出态。只有就绪态和运行态可以相互转换,其它的都是单向转换。
就绪状态的进程通过调度算法从而获得 CPU 时间,转为运行状态;而运行状态的进程,在分配给它的 CPU 时间片用完之后就会转为就绪状态,等待下一次调度。阻塞状态是缺少需要的资源从而由运行状态转换而来,但是该资源不包括 CPU 时间,缺少 CPU 时间会从运行态转换为就绪态。
2.进程调度算法
2.1批处理系统
先来先服务 first-come first-serverd(FCFS)
非抢占式的调度算法,按照请求的顺序进行调度。有利于长作业,但不利于短作业,因为短作业必须一直等待前面的长作业执行完毕才能执行,而长作业又需要执行很长时间,造成了短作业等待时间过长。
短作业优先 shortest job first(SJF)
非抢占式的调度算法,按估计运行时间最短的顺序进行调度。长作业有可能会饿死,处于一直等待短作业执行完毕的状态。因为如果一直有短作业到来,那么长作业永远得不到调度。
最短剩余时间优先 shortest remaining time next(SRTN)
最短作业优先的抢占式版本,按剩余运行时间的顺序进行调度。 当一个新的作业到达时,其整个运行时间与当前进程的剩余时间作比较。如果新的进程需要的时间更少,则挂起当前进程,运行新的进程。否则新的进程等待。
2.2交互式系统
时间片轮转
将所有就绪进程按 FCFS 的原则排成一个队列,每次调度时,把 CPU 时间分配给队首进程,该进程可以执行一个时间片。当时间片用完时,由计时器发出时钟中断,调度程序便停止该进程的执行,并将它送往就绪队列的末尾,同时继续把 CPU 时间分配给队首的进程。
优先级调度
为每个进程分配一个优先级,按优先级进行调度。为了防止低优先级的进程永远等不到调度,可以随着时间的推移增加等待进程的优先级。
多级反馈队列
时间片轮转调度算法和优先级调度算法的结合。
2.3实时系统
分为硬实时和软实时。
3.进程间通信
共享内存
允许两个不相关的进程访问同一个逻辑内存。共享内存并未提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取,所以我们通常需要用其他的机制来同步对共享内存的访问,例如信号量。
信号量
是一个计数器,用来控制多个进程对资源的访问。
管道pipe/命名管道FIFO
管道是一种半双工的通信方式,数据只能单向流动,通常在父子进程的进程间使用。后者去除了管道只能在父子进程中使用的限制。
消息队列
消息队列是消息的链表,存放在内核中并由消息队列标识符标识。(消息队列可以独立于读写进程存在,避免了 FIFO 的同步阻塞问题,读进程可以根据消息类型有选择地接收消息。)
4.线程间通信
互斥量
采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问。
信号量
它允许同一时刻多个线程访问同一资源,但是需要控制同一时刻访问此资源的最大线程数量。
临界区
一个访问共用资源的程序片段,而这些共用资源又无法同时被多个线程访问的特性。当有线程进入临界区段时,其他线程或是进程必须等待。
5.进程死锁
发生的必要条件
互斥:资源不能共享,只能一个进程用
占有与等待:已经得到资源的进程可以再次申请新的资源
不可抢占:已经分配的资源不能从相应进程中强制剥夺
环路等待:系统中若干进程形成环路,环路中的每个进程都在等待相邻进程正占用的资源
处理方式
鸵鸟处理:把头埋在沙子里,假装根本没发生问题。因为解决死锁问题的代价很高,因此鸵鸟策略这种不采取任务措施的方案会获得更高的性能。
预防:破环四个原因中的一个或多个,但会影响到资源利用率及吞吐量。(如:规定所有进程在开始执行前请求所需要的全部资源。给资源统一编号,进程只能按编号顺序来请求资源。)
避免:在资源的动态分配中防止系统进入不安全状态。
检测和恢复:不试图阻止死锁,而是当检测到死锁发生时,采取措施进行恢复。利用抢占恢复,利用回滚恢复,通过杀死进程恢复。
6.内存管理
虚拟内存
让物理内存扩充成更大的逻辑内存,从而让程序获得更多的可用内存。(操作系统将需要的部分调入内存,并把暂时不适用的内容换到外存上,腾出内存空间。让应用程序认为他用了一个比实际内存大得多的存储器。虚拟内存是计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续的可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。)
分页
把主存空间划分为大小相等且固定的块,作为主存的基本单位,每个进程也以块为单位进行划分,进程执行时,以块为单位逐个申请主存中的块空间。用页表记录分散的内存分布情况。
内存管理单元(MMU)管理着地址空间和物理内存的转换,其中的页表(Page table)存储着页(程序地址空间)和页框(物理内存空间)的映射表。
快表机制:访问内存数据的时候先在快表里查询,如果查到了就可以直接读取相应的物理块号,如果每找到再访问页表,得到物理地址并访问,同时把该页表中的该映射项添加到快表中。
分段
每个段内部连续内存分配,长度可以不同,并且可以动态增长,但段与段之间是离散的。因此会用到段表,记录每段在内存中的起始地址和该段长度。
分页分段比较
1.分页主要用于实现虚拟内存,出于系统内存利用率的角度从而获得更大的地址空间;分段主要是为了使程序和数据可以被划分为逻辑上独立的地址空间,出于用户角度。
2.页的大小是固定的,由系统决定;段的大小是不确定的,由用户决定。
页面置换算法
地址映射的过程中,如果页面中发现要访问的页面不在内存中,会产生缺页中断。此时操作系统必须在内存里选择一个页面把他移出内存,为即将调入的页面让出空间。
最佳置换算法(理想);先进先出;最近最久未使用 LRU;最近未使用算法NRU;最少使用算法LFU
7.用户态/内核态
内核态:cpu可以访问内存的所有数据,包括外围设备,例如硬盘,网卡,cpu也可以将自己从一个程序切换到另一个程序。
用户态:只能受限的访问内存,且不允许访问外围设备,占用cpu的能力被剥夺,cpu资源可以被其他程序获取。
最大的区别就是权限不同,在运行在用户态下的程序不能直接访问操作系统内核数据结构和程序。
为什么要有这两态?
需要限制不同的程序之间的访问能力,防止他们获取别的程序的内存数据,或者获取外围设备的数据,并发送到网络,CPU划分出两个权限等级——用户态和内核态。
什么时候转换?
系统调用:用户进程主动发起的。用户态进程通过系统调用申请使用操作系统提供的服务程序完成工作,比如fork()就是执行一个创建新进程的系统调用用户程序使用系统调用,系统调用会转换为内核态并调用操作系统。
异常:会从当前运行进程切换到处理次此异常的内核相关程序中。
外围设备的中断:所有程序都运行在用户态,但在从硬盘读取数据、或从键盘输入时,这些事情只有操作系统能做,程序需要向操作系统请求以程序的名义来执行这些操作。这个时候用户态程序切换到内核态。