目录
进程和线程的区别
线程是被CPU调度的基本单位,进程是能独立运行的基本单位,是系统分配资源的基本单位。
创建销毁线程要比创建销毁进程成本低的多。(创建进程要,创建PCB(进程控制块),开辟虚拟地址空间,创建页表,维护映射关系,加载硬盘数据到内存,创建文件描述符,等等,而创建线程只要创建一个PCB指向进程的虚拟地址空间即可)
进程拥有自己独立的虚拟地址空间,而一个进程中的多个线程共享进程的虚拟地址空间
线程占用的资源要比进程少
线程缺乏访问控制,进程中的一个线程出错,会终止掉整个进程,从而导致其他线程也凉凉,而一个进程出错,不会影响另一个进程
线程和进程共享资源,但是有一部分数据是自己私有的
线程和进程共享
同一地址空间 (堆,代码段,数据段,环境变量,页表)
文件描述符表
每种信号的处理方式
当前工作目录
用户id和组id
线程和进程私有
私有栈结构(保存临时数据,保证线程执行时不相互影响)
上下文数据(CPU调度切换数据保存)
调度优先级
errno
信号屏蔽字
进程线程优缺点
线程优点
线程共享进程的地址空间,因此线程间通信比较容易(但是不安全要注意维护),而进程间通信就相比较困难了,因为两个进程看到同一块资源不容易 ------------节约资源,成本低,调度切换快
创建销毁成本低
线程间切换容易
等待慢速I/O的同时,可以干其他事情
进程优点
更强的容错性,安全,一个进程挂了,不影响其他.
多进程比多线程编写,调试容易。
线程应用场景
等待慢速I/O时,交给一个线程等待,接着做其他事情
通信,比较容易(注意加锁)
进程的应用场景
需要安全稳定时用进程,需要速度时用进程,既要速度又要安全。有的书上说,能用多进程解决的尽量不要用多线程
进程同步——经典的同步问题
涉及进程同步的一些概念:
互斥与同步:
临界资源(临界区):指一次只能允许一个进程使用的共享资源称为临界资源;
同步:指为完成某种任务而建立的两个和多个进程,这些进程在合作的过程中需要协调工作次序进行有序的访问而出现等待所产生的制约关系。
互斥:指两个或多个进程访问临界资源时只能一个进程访问,其他进程等待的一种相互制约的关系。
信号量与互斥量:
信号量:本身是一个计数器,使用P,V两个操作来实现计数的减与加,当计数不大于0时,则进程进入睡眠状态,它用于为多个进程提供共享数据对象的访问。
互斥量:如果信号量只存在两个状态,那就不需要计数了,可以简化为加锁与解锁两个功能,这就是互斥量。
一、生产者与消费者问题
问题描述:一组生产者进程和一组消费者进程共享一块初始为空,大小确定的缓冲区,只有当缓冲区为满时,生产者进程才可以把信息放入缓冲区,否则就要等待;只有缓存区不为空时,消费者进程才能从中取出消息,否则就要等待。缓冲区一次只能一个进程访问(临界资源)。
问题分析:生产者与消费者进程对缓冲区的访问是互斥关系,而生产者与消费者本身又存在同步关系,即必须生成之后才能消费。因而对于缓冲区的访问设置一个互斥量,再设置两个信号量一个记录空闲缓冲区单元,一个记录满缓冲区单元来实现生产者与消费者的同步。
二、读者与写者问题
问题描述:有读者与写者两个并发进程共享一个数据,两个或以上的读进程可以访问数据,但是一个写者进程访问数据与其他进程都互斥。
问题分析:读者与写者是互斥关系,写者与写者是互斥关系,读者与读者是同步关系。因而需要一个互斥量实现读与写和写与写互斥,一个读者的访问计数和实现对计数的互斥。
三、哲学家就餐问题
问题描述:一张圆桌上坐着五名哲学家,每两名哲学家之间的桌子摆一根筷子,哲学家只有同时拿起左右两根筷子时才可以用餐,用餐完了筷子放回原处。
问题分析:这里五名哲学家就是五个进程,五根筷子是需要获取的资源。可以定义互斥数组用于表示五根筷子的互斥访问,为了防止哲学家个取一根筷子出现死锁,需要添加一定的限制条件。一种方法是限制仅当哲学家左右筷子均可以用时,才拿起筷子,这里需要一个互斥量来限制获取筷子不会出现竞争。
问题解决:一次仅能一个哲学家拿起筷子,效率比较低。
死锁
如果一个进程集合中的每个进程都在等待只能由该进程集合中的其他进程才能引发的事件,那么该进程集合就是死锁的。
死锁的条件(四个同时满足):
(1)互斥:每个资源要么已经分配给一个进程,要么就是可用的;
(2)占有和等待:已经得到的某个资源的进程请求新的资源;
(3)不可抢占:已经分配的资源不能强制被抢占,只能进程自己显示的释放;
(4)环路等待:存在一种进程资源的循环等待链。
死锁的处理策略:
(1)死锁预防:破坏死锁的四个条件之一
破环互斥条件:允许资源共享
破环占有和等待条件:采用预先静态分配
不可抢占:请求新资源得不到时,释放已经保持占有的资源,待以后重新申请
环路等待:采用顺序资源分配法
(2)死锁避免:死锁避免事先预防策略,但是是采用资源动态分配的过程中,防止系统进入不安全状态,以避免死锁。
银行家算法:可利用资源矢量Available,请求矢量Request
最大需求矩阵Max,分配矩阵Allocation,需求矩阵Need
通过Need=Max-Allocation获得每个进程需要的各类资源数Need矩阵
一般每个进程请求矢量应该小于等于Need的值
试探分配:Available=Avaliable-Request
Allocate相对应的项=Allocate相对应的项+Request
Need相对应的项=Need相对应的项-Request
安全性算法:检查资源分配后,系统是否属于安全状态,如果安全才正式分配资源,否则作废。一般通过安全性算法推算一个安全序列(核心)。
(3)死锁检测与解除:
检测死锁:利用死锁原理化简资源分配图检测死锁的存在
死锁解除:资源剥夺、撤销进程、进程回退
进程间通信及使用场景
进程间通信(IPC,InterProcess Communication)的主要方式包括:管道、FIFO、消息队列、信号量、共享内存、socket。
管道
这一节中所说的管道为无名管道。
1、管道具有以下两种局限性:
· 管道为半双工的;
· 管道只能在具有公共祖先的两个进程之间使用,通常,一个管道由一个进程创建,在进程调用fork之后,这个管道就能在父进程和子进程之间使用了。
FIFO
FIFO即为命名管道,与无名管道不同的是,其可以在不相关的程序之间交换数据。FIFO其实是一种文件类型
FIFO有两种用途
· shell命令使用FIFO将数据从一条管道传送到另一条管道时,无须创建中间的临时文件。
· 客户进程-服务器进程应用程序中,FIFO用作汇聚点,在客户进程和服务器进程二者之间传递数据。
FIFO应用实例
实例1 用FIFO复制输出流:
FIFO可以用于复制一系列shell命令中的输出流。这就防止了将数据写向中间磁盘文件。这类似于使用管道(|)来避免中间磁盘文件。
实例2 使用FIFO进行客户进程-服务器进程通信:
如果有一个服务器进程,它与很多客户进程有关,每个客户进程都可以将其请求写到一个该服务器进程创建的“众所周知”的FIFO中(所有客户进程都知道该FIFO的路径名)。
消息队列
消息队列是消息的链接表,存储在内核中,有消息队列标识符标识。我们并不一定以先进先出的顺序取消息,可以按照消息的类型字段取消息。
信号量
1、信号量与管道,FIFO以及消息队列不同。它是一个计数器,用于为多个进程提供共享数据对象的访问。
2、 为了获得共享资源,进程需要执行下列操作:
1) 测试控制该资源的信号量;
2) 若此信号量的值为正,则进程可以使用该资源。在这种情况下,进程会将信号量值减1,表示它使用了一个资源单位;
3) 否则,若此信号量的值为0,则进程进入休眠状态,知道信号量的值大于0。进程被唤醒后,返回步骤1)。
当进程不在使用由一个信号量控制的共享资源时,该信号量值增1。如果有进程正在休眠等待此信号量,则唤醒它们。
共享存储
1、共享存储允许两个或多个进程共享一个给定的存储区。
因为数据不需要在客户进程和服务器进程之间复制,所以这是最快的IPC。在多个进程同步访问一个给定存储区时,若服务器进程正在将数据放入存储区,则在它做完之前,客户进程不应该去取这些数据。
信号量用于同步共享存储访问。
socket
关于socket通信以后会专门写一篇文章说明。
几种IPC方式优缺点比较
1、如果用户传递的信息较少,或者只是为了出发某些行为。信号是一种简洁有效的通信方式。但若是进程间要求传递的信息量较大或者存在数据交换的要求,就需要考虑别的通信方式了。
2、无名管道与有名管道的区别在于单向通信以及有关联的进程。
3、消息队列允许任意进程通过共享队列来进行进程间通信。并由系统调用函数来实现消息发送和接收之间的同步。从而使得用户在使用消息缓冲进行通信时不再需要考虑同步问题,使用相对方便。
但是消息队列中信息的复制需要耗费CPU时间,不适宜信息量大或频繁操作的场合。
4、消息队列与管道方式的区别在于,消息队列可以实现多对多,并需要在内存中实现,而管道可以在内存或磁盘上实现。
5、共享内存无须复制,信息量大是其最大的优势。但是需要考虑同步问题。