一、进程的通信方式
1.管道:
管道是单向的、先进先出的、无结构的、固定大小的字节流,它把一个进程的标准输出和另一个进程的标准输入连接在一起。写进程在管道的尾端写入数据,读进程在管道的道端读出数据。数据读出后将从管道中移走,其它读进程都不能再读到这些数据。管道提供了简单的流控制机制。进程试图读空管道时,在有数据写入管道前,进程将一直阻塞。同样地,管道已经满时,进程再试图写管道,在其它进程从管道中移走数据之前,写进程将一直阻塞。
2.信号量:
信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其它进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
3.消息队列:
是一个在系统内核中用来保存消息的队列,它在系统内核中是以消息链表的形式出现的。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
4.共享内存:
共享内存允许两个或多个进程访问同一个逻辑内存。这一段内存可以被两个或两个以上的进程映射至自身的地址空间中,一个进程写入共享内存的信息,可以被其他使用这个共享内存的进程,通过一个简单的内存读取读出,从而实现了进程间的通信。如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程。共享内存是最快的 IPC 方式,它是针对其它进程间通信方式运行效率低而专门设计的。它往往与其它通信机制(如信号量)配合使用,来实现进程间的同步和通信。
5.套接字:
套接字也是一种进程间通信机制,与其它通信机制不同的是,它可用于不同机器间的进程通信。
二、上下文切换
对于单核单线程 CPU 而言,在某一时刻只能执行一条 CPU 指令。上下文切换 (Context Switch) 是一种将 CPU 资源从一个进程分配给另一个进程的机制。从用户角度看,计算机能够并行运行多个进程,这恰恰是操作系统通过快速上下文切换造成的结果。在切换的过程中,操作系统需要先存储当前进程的状态 (包括内存空间的指针,当前执行完的指令等等),再读入下一个进程的状态,然后执行此进程。
三、线程和进程
1.概念:
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。
线程是进程的一个实体,是 CPU 调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。
2.进程与线程的区别:
(1)进程有自己的独立地址空间,线程没有
(2)进程是资源分配的最小单位,线程是 CPU 调度的最小单位
(3)进程和线程通信方式不同 (线程之间的通信比较方便。同一进程下的线程共享数据(比如全局变量,静态变量),通过这些数据来通信不仅快捷而且方便,当然如何处理好这些访问的同步与互斥正是编写多线程程序的难点。而进程之间的通信只能通过进程通信的方式进行。)
(4)进程上下文切换开销大,线程开销小
(5)一个进程挂掉了不会影响其他进程,而线程挂掉了会影响其他线程
(6)对进程进程操作一般开销都比较大,对线程开销就小了
四、进程调度
(一)概念
非抢占式:分派程序一旦把处理机分配给某进程后便让它一直运行下去,直到进程完成或发生进程调度进程调度某事件而阻塞时,才把处理机分配给另一个进程。
抢占式:操作系统将正在运行的进程强行暂停,由调度程序将 CPU 分配给其他就绪进程的调度方式。
CPU 任务可以分为交互式任务和批处理任务,调度最终的目标是合理的使用 CPU,使得交互式任务的响应时间尽可能短,用户不至于感到延迟,同时使得批处理任务的周转时间尽可能短,减少用户等待的时间。
响应时间(交互式任务): 从用户输入到产生反应的时间
周转时间(批处理任务): 从任务开始到任务结束的时间
(二)进程调度算法
批处理系统:
- 先来先服务 (FCFS)
- 短作业优先(SJF)
- 最短剩余时间优先(SRTN)
- 优先权调度
交互式:
- 时间片轮转
- 多级调度队列
- 多级反馈队列
1.FIFO 或 First Come, First Served (FCFS) 先来先服务
-
调度的顺序就是任务到达就绪队列的顺序。
-
公平、简单 (FIFO 队列)、非抢占、不适合交互式。
-
未考虑任务特性,平均等待时间可以缩短。
2.Shortest Job First (SJF)最短作业优先
-
最短的作业 (CPU 区间长度最小) 最先调度。
-
SJF 可以保证最小的平均等待时间。
3.Shortest Remaining Job First (SRJF)最短剩余作业优先
-
SJF 的可抢占版本,比 SJF 更有优势。
-
SJF (SRJF): 如何知道下一 CPU 区间大小?根据历史进行预测:指数平均法。
4.优先权调度
-
每个任务关联一个优先权,调度优先权最高的任务。
-
优先权太低的任务一直就绪,得不到运行,出现 “饥饿” 现象。
5.Round-Robin (RR) 轮转调度算法
-
设置一个时间片,按时间片来轮转调度(“轮叫” 算法)
-
优点:定时有响应,等待时间较短;缺点:上下文切换次数较多;
-
时间片太大,响应时间太长;吞吐量变小,周转时间变长;当时间片过长时,退化为 FCFS。
6.多级队列调度
-
按照一定的规则建立多个进程队列
-
不同的队列有固定的优先级(高优先级有抢占权)
-
不同的队列可以给不同的时间片和采用不同的调度方法
-
存在问题 1:没法区分 I/O bound 和 CPU bound;
-
存在问题 2:也存在一定程度的 “饥饿” 现象;
7.多级反馈队列
-
在多级队列的基础上,任务可以在队列之间移动,更细致的区分任务。
-
可以根据 “享用” CPU 时间多少来移动队列,阻止 “饥饿”。
-
最通用的调度算法,多数 OS 都使用该方法或其变形,如 UNIX、Windows 等。
-
算法详解:
-
进程在进入待调度的队列等待时,首先进入优先级最高的 Q1 等待。
-
首先调度优先级高的队列中的进程。若高优先级中队列中已没有调度的进程,则调度次优先级队列中的进程。例如:Q1,Q2,Q3 三个队列,只有在 Q1 中没有进程等待时才去调度 Q2,同理,只有 Q1,Q2 都为空时才会去调度 Q3。
-
对于同一个队列中的各个进程,按照时间片轮转法调度。比如 Q1 队列的时间片为 N,那么 Q1 中的作业在经历了 N 个时间片后若还没有完成,则进入 Q2 队列等待,若 Q2 的时间片用完后作业还不能完成,一直进入下一级队列,直至完成。
-
在低优先级的队列中的进程在运行时,又有新到达的作业,那么在运行完这个时间片后,CPU 马上分配给新到达的作业(抢占式)。
-
五、僵尸进程和孤儿进程
(一)僵尸进程
1.定义:
一个进程使用 fork 创建子进程,如果子进程退出,而父进程并没有调用 wait 或者 waitpid 获取子进程的状态信息,那么子进程的进程描述符等一系列信息还会保存在系统中。这种进程称之为僵死进程。
2.危害:
在 Unix 系统管理中,当用 ps 命令观察进程的执行状态时,经常看到某些进程的状态栏为 defunct,这就是所谓的 “僵尸” 进程。“僵尸” 进程是一个早已死亡的进程,但在进程表(processs table)中仍占了一个位置(slot)。由于进程表的容量是有限的,所以,defunct 进程不仅占用系统的内存资源,影响系统的性能,而且如果其数目太多,还会导致系统瘫痪。
3.处理方法:
-
改写父进程,在子进程死后要为它收尸。具体做法是接管 SIGCHLD 信号。子进程死后,会发送 SIGCHLD 信号给父进程,父进程收到此信号后,执行 waitpid () 函数为子进程收尸。这是基于这样的原理:就算父进程没有调用 wait,内核也会向它发送 SIGCHLD 消息,尽管默认处理是忽略,如果想响应这个消息,可以设置一个处理函数。
-
把父进程杀掉。父进程死后,僵尸进程成为” 孤儿进程”,过继给 1 号进程 init,init 始终会负责清理僵尸进程.它产生的所有僵尸进程也跟着消失。
(二)孤儿进程
1.定义:
父进程运行结束,但子进程还在运行 (未运行结束) 的子进程就称为孤儿进程。
孤儿进程最终会被 init 进程 (进程号为 1) 所收养,因此 init 进程此时变成孤儿进程的父进程,并由 init 进程对它们完成状态收集工作。(linux 下,init 是内核启动的第一个用户级进程,init 有许多很重要的任务,比如像启动 getty(用于用户登录)、实现运行级别、以及处理孤立进程。)