目录
什么是进程?
进程是一个具有独立功能的程序,在一个数据集合上依次动态执行的过程。进程也是一个正在执行的程序的实例,包括程序计数器、寄存器和程序变量的当前值。
进程一般由以下的部分组成:
- 进程控制块,是进程存在的唯一标志。(包含进程标识符PID,进程当前状态,程序和数据地 址,进程优先级、CPU现场保护区(用于进程切换),占有的资源清单等。)
- 程序段
- 数据段
系统中的每个程序都运行在某个进程的上下文(context) 中。 进程上下文指的是一个进程在执行的时候,CPU的所有寄存器中的值、进程的状态以及栈堆上的内容。当内核需要切换到另一个进程时,它需要保存当前进程的所有状态,即保存当前进程的进程上下文,以便再次执行该进程时,能够恢复切换时的状态,继续执行。
进程的基本操作
1. 进程的创建:
主要用fork()函数创建进程,当调用fork时,内核会
(1)分配新的内存块和内核数据结构给子进程。
(2)将父进程部分数据内容拷贝至子进程。
(3)添加子进程到系统进程列表当中。
(4)fork返回,开始调度器调度。
父进程和新创建的子进程之间最大的区别在于它们有不同的 PID。fork函数只被调用一次,却会返回两次:一次是在调用进程(父进程)中,一次是在新创建的子进程中。在父进程中,fork 返回子进程的PID。在子进程中,fork返回0。因为子进程的 PID 总是为非零,返回值就提供一个明确的方法来分辨程序是在父进程还是在子进程中执行。
2. 进程终止:
退出场景:
1)代码运行完毕,结果正确。
2)代码运行完毕,结果不正确。
3)代码异常终止。
退出方法:
1)正常终止:从main返回;调用exit终止。
2)异常退出:ctrl+c,信号终止;用kill终止;
3. 进程等待:
父进程通过进程等待的方式,回收子进程资源。获取子进程退出信息。当一个进程由于某种原因终止时,内核并不是立即把它从系统中清除。相反,进程被保持在一种已终止的状态中,直到被它的父进程回收(reaped)。当父进程回收已终止的子进程时,内核将子进程的退出状态传递给父进程,然后抛弃已终止的进程。一个进程可以通过调用waitpid函数来等待它的子进程终止或者停止。
4. 进程替换:
exec函数不会创建进程,会用参数给定的程序替换当前的进程。
简述进程间通信方法
每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程A把数据从用户空间拷到内核缓冲区,进程B再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信。
不同进程间的通信本质:进程之间可以看到一份公共资源;而提供这份资源的形式或者提供者不同,造成了通信方式不同。 进程间通信主要有7种通信方式。
消息传递
消息传递是进程间实现通信和同步等待的机制,使用消息传递,进程间的交流不需要共享变量,直接就可以进行通信;消息传递分为发送方和接收方
先进先出队列
先进先出队列指的是两个不相关联进程间的通信,两个进程之间可以彼此相互通信,这是一种全双工通信方式
管道
管道用于两个相关进程之间的通信,这是一种半双工的通信方式,如果需要全双工,需要另外一个管道。
直接通信
在这种进程通信的方式中,进程与进程之间只存在一条链接,进程间要明确通信双方的命名。
间接通信
间接通信是通信双方不会直接建立连接,而是找到一个中介者,这个中介者可能是个对象等等,进程可以在其中放置消息,并且可以从中删除消息,以此达到进程间通信的目的。
消息队列
消息队列是内核中存储消息的链表,它由消息队列标识符进行标识,这种方式能够在不同的进程之间提供全双工的通信连接。
共享内存
共享内存是使用所有进程之间的内存来建立连接,这种类型需要同步进程访问来相互保护。
进程的调度策略
调度时机
当进程从一个运行状态到另外一个状态变化的时候,就会触发一次调度。比如,从就绪态到运行态,从运行态到阻塞态,从运行态到结束态。因为这些状态变化的时候,操作需要需要考虑是否要让新的进程给CPU运行,或是否让当前进程从CPU上退出来而换另一个进程运行。根据如何处理时钟中断,可把调度算法分为两类:非抢占式调度与抢占式调度。非抢占式:挑选一个进程,然后让该进程一直运行下去,直到进程完成或被阻塞时,才会调用另一个进程。抢占式挑选一个进程,然后让该进程只运行某段时间,如果在该时段结束时,该进程仍然在运行,就会把它挂起,接着从就绪队列中挑选另外一个进程来运行。
调度原则
CPU利用率
调度程序应确保CPU是始终忙碌的状态,这可提高CPU的利用率。
系统吞吐量
吞吐量表示的是单位时间内CPU完成进程的数量,长作业的进程会占用较长的CPU资源,因此会降低吞吐量,相反,短作业的进程会提升系统吞吐量。
周转时间
周转时间是进程运行和阻塞时间总和,一个进程的周转时间越小越好。
等待时间
这个等待时间不是阻塞状态的时间,而是进程处于就绪队列的时间,等待的时间越长,用户越不满意;
响应时间
用户提交请求到系统第一次产生响应所花费的时间,在交互式系统中,响应时间是衡量调度算法好坏的主要标准。
调度算法:
先来先服务调度算法 (FCFS)
先来先服务调度算法是最简单的非抢占的调度算法。它通过管理渔港FIFO就绪队列来实现,从前到后按顺序将CPU分配给进程。
最高优先级调度算法
由用户或系统按照某种原则给进程一个优先级,系统总是调度优先级最高的进程运行。优先级可分为静态优先级和动态优先级。静态就是指优先级确定后就不再改变;而动态优先级会随着进程的不断执行而发生改变。对优先级动态调整可以改善系统性能,但是动态优先级调度算法会增加系统开销。该算法中会出现“饥饿”现象,就是优先级太低的任务一直就绪,得不到运行。为避免这一问题,可采用”老化“的策略,即按照一定时间间隔提高等待进程的优先级数值。
最短作业优先调度算法(SJF)
最短工作优先调度算法克服了FCFS算法对短进程不利的缺点,它在就绪队列中选择处理时间最短的进程,如果时间相同则可以按照FCFS准则来处理。它提高了系统的吞吐量,但同时对长进程不利。它分为抢占式和非抢占式两种。对于非抢占式的SJF调度算法,可以将其看做是优先级调度算法的一种特例。
最高响应比优先调度算法(HRRN)
最高响应比优先调度算法权衡了短作业和长作业,使用响应比R来表征。R = (W + S)/ S(w等待时间,s预计执行时间)。
在该算法下,等待时间相同的时候,短进程响应比高于长进程,短进程优先被调度;而预计执行时间相同时,等待时间增长,进程的响应比增长,长进程被调度的可能性也会增加。
最短的作业(CPU区间长度最小)最先调度。
SJF可以保证最小的平均等待时间。
轮转调度算法(RR)
轮转调度算法是一种基于抢占的调度策略,在分时系统中,每个进程会被分配一个固定的时间片,就绪队列中的进程按顺序依次调度运行。时间片过短会使进程切换过于频繁,增加系统开销;时间片过长会使进程响应时间增加。20ms~50ms通常为合理值。
多级反馈队列调度算法
多级反馈队列调度算法是轮转算法和最高优先级算法的综合与发展。多级表示有多个队列,每个队列优先级从高到低,同时优先级越高时间片越短;反馈表示如果有新的进程加入优先级高的队列时,立刻停止当前正在运行的进程,转而去运行优先级高的队列。
进程的状态与状态转换
进程在运行时有三种基本状态:就绪态、运行态和阻塞态。
-
运行(running)态:进程占有处理器正在运行的状态。进程已获得CPU,其程序正在执行。在单 处理机系统中,只有一个进程处于执行状态; 在多处理机系统中,则有多个进程处于执行状态。
-
就绪(ready)态:进程具备运行条件,等待系统分配处理器以便运行的状态。 当进程已分配到除 CPU以外的所有必要资源后,只要再获得CPU,便可立即执行,进程这时的状态称为就绪状态。在一个 系统中处于就绪状态的进程可能有多个,通常将它们排成一个队列,称为就绪队列。
-
阻塞(wait)态:又称等待态或睡眠态,指进程不具备运行条件,正在等待某个时间完成的状态。
各状态之间的转换:
就绪→运行
处于就绪状态的进程,当进程调度程序为之分配了处理机后,该进程便由就绪状态转变成运行状态。
运行→就绪
处于运行状态的进程在其执行过程中,因分配给它的一个时间片已用完而不得不让出处理机,于是进程从运行状态转变成就绪状态。
运行→阻塞
正在运行的进程因等待某种事件发生而无法继续执行时,便从运行状态变成阻塞状态。
阻塞→就绪
处于阻塞状态的进程,若其等待的事件已经发生,于是进程由阻塞状态转变为就绪状态。
什么是线程?
线程是进程划分的任务,是一个进程内可调度的实体,是CPU调度的基本单位,用于保证程序的实时性,实现进程内部的并发。 线程是操作系统可识别的最小执行和调度单位。
每个线程都独自占用一个虚拟处理器:独自的寄存器组,指令计数器和处理器状态。
每个线程完成不同的任务,但是属于同一个进程的不同线程之间共享同一地址空间(也就是同样的 动态内存,映射文件,目标代码等等),打开的文件队列和其他内核资源。
为什么需要线程?
(线程产生的原因)
进程可以使多个程序并发执行,以提高资源的利用率和系统的吞吐量;但是其具有一些缺点:
- 进程在同一时刻只能做一个任务,很多时候不能充分利用CPU资源。
- 进程在执行的过程中如果发生阻塞,整个进程就会挂起,即使进程中其它任务不依赖于等待的资源,但进程仍会被阻塞。
引入线程就是为了解决以上进程的不足,线程具有以下的优点:
- 从资源上来讲,创建一个线程所需要的资源要远小于一个进程
- 从切换效率上来讲,运行于一个进程中的多个线程,它们之间使用相同的地址空间,线程间彼此切换所需时间也远远小于进程间切换所需要的时间(这种时间的差异主要由于缓存的大量未命中导致)。
- 从通信机制上来讲,线程间的通信机制更加方便。对不同进程来说,它们具有独立的地址空间,要进行数据的传递,只能通过进程间的通信方式进行。线程则不然,属于同一个进程的不同线程之间共享同一地址空间,所以一个线程的数据可以被其它线程感知,线程间可以直接读写进程数据段(如全局变量)来进行通信(需要一些同步措施)。
简述线程和进程的区别和联系
1.一个线程只能属于一个进程,而一个进程可以有多个,且至少有一个线程。线程依赖于进程而存在。
2.进程在执行过程中拥有独立的地址空间,而多个线程共享进程的地址空间。
(资源分配给进程,同一进程的所有线程共享该进程的所有资源。同一进程中的多个线程共享代码段(代码和常量),数据段(全局变量和静态变量),扩展段(堆存储)。但是每个线程拥有自己的栈段,栈段又叫运行时段,用来存放自己的局部变量和临时变量。)
3.进程是资源分配的最小单位,线程是CPU调度的最小单位。
4.进程和线程通信方式不同(线程之间的通信比较方便。同一进程下的线程共享数据(比如全局变量,静态变量),通过这些数据来通信不仅快捷而且方便,(当然如何处理好这些访问的同步与互斥正是编写多线程程序的难点。)而进程之间的通信只能通过进程通信的方式进行。)
5.创建进程需要操作系统分配新的地址空间,数据资源等,这个开销比较大;线程正相反,创建线程仅仅需要栈指针和程序计数器,开销小,切换速度快。
6.进程间不会相互影响;同一进程内的不同线程可能相互影响(共享数据可能导致并发问题)。