进程同步的主要任务:是对多个相关进程在执行次序上进行协调,以使并发执行的诸进程之间能有效的共享资源和相互合作,从而使程序的执行具有可再现性。 同步机制遵循的原则:
进程通信,是指进程之间的信息交换(信息量少则一个状态或数值,多则是成千上万个字节)。因此,对于信号量进行的进程间的互斥和同步,由于其所交换的信息量少而被归结为低级通信。 所谓高级进程通信指:用户可以利用操作系统所提供的一组通信命令传送大量数据的一种通信方式。操作系统隐藏了进程通信的实现细节。或者说,通信过程对用户是透明的。 高级通信机制可归结为三大类:
对于单核单线程 CPU 而言,在某一时刻只能执行一条 CPU 指令。上下文切换是一种将CPU资源从一个进程分配给另一个进程的机制。从用户角度看,计算机能够并运行多个进程,这恰恰是操作系统通过快速上下文切换造成的结果。在切换的过程中,操作系统需要先存储当前进程的状态(包括内存空间的指针,当前执行完的指令等等),再读入下一个进程的状态,然后执行此进程。
进程和线程都是一个时间段的描述,是CPU工作时间段的描述,不过是颗粒大小不同。
在现在操作系统中,往往是用组合方式实现多线程,即线程创建完全在用户空间中完成,并且一个应用程序中的多个用户级线程被映射到一些内核级线程上,相当于是一种折中方案。
CPU任务可以分为交互式任务和批处理任务,调度最终的目标是合理的使用CPU,使得交互式任务的响应时间尽可能短,用户不至于感到延迟,同时使得批处理任务的周转时间尽可能短,减少用户等待的时间。
一个简单的例子 假设系统中有3个反馈队列Q1,Q2,Q3,时间片分别为2,4,8。现在有3个作业J1,J2,J3分别在时间 0 ,1,3时刻到达。而它们所需要的CPU时间分别是3,2,1个时间片。
**定义:**如果一组进程中的每一个进程都在等待仅有该组进程中的其他进程才能引发的时间,那么该组进程就是死锁的。或者在两个或多个并发进程中,如果每个进程持有某种资源而又都等待别的进程释放它或它们现在保持着的资源,在未改变这种状态之前都向前推进,称这一组进程产生了死锁。通俗地讲,就是两个或多个进程被无限期地阻塞、相互等待的一种状态。
“池化技术”:提前保存大量的资源,以备不时之需以及重复使用。池化技术应用广泛,如内存池、线程池、连接池等。(内存池相关的内容,可以看Apache、Nginx等开源web服务器的内存池实现)。 由于在实际应用当作内存分配、创建进程、线程都会设计到一些系统调用,系统调用需要导致程序从用户态切换到内核态,是非常耗时的操作。因此,当程序中需要频繁的进行内存申请释放;进程、线程创建销毁等操作时,通常会使用内存池、进程池、线程池技术来提升程序的性能。
线程池类似于操作系统中的缓冲区的概念,他的流程如下:先启动若干数量的线程,并让这些线程都处于睡眠状态,当需要一个开辟一个线程去做具体工作时,就会唤醒线程池中的某一个睡眠线程,让它去做具体工作,当工作完成后,线程又处于睡眠状态,而不是将线程销毁。
通线程池。
内存池是指程序与先从操作系统申请一块足够大的内存,此后,当程序中需要申请内存的时候,不是直接向操作系统申请,而是直接从内存池获取;同理,当程序释放内存时,并不真正将内存返回给操作系统,而是返回内存池。当程序退出(或待定)时,内存池才将之前申请的内存真正释放。
定义:具有请求调入功能和置换功能,能从逻辑上对内存容量加以扩充的一种存储器系统。其逻辑容量由内存之和和外存之和决定。 与传统存储器比较虚拟存储器有以下三个主要特征:
操作系统将内存按照页面进行管理,在需要的时候才把进程相应的部分调入内存。当产生缺页中断时,需要选择一个页面写入。如果要换出的页面在内存中被修改过,变成了“脏”页面,那就需要先写回到磁盘。页面置换算法,就是要选出最合适的一个页面,使得置换的效率最高。页面置换算法有很多。
最理想的状态下,我们给页面做个标记,挑选一个最远才会被再次用到的页面调出。当然,这样的算法不可能实现,因为不确定一个页面在何时会被用到。
这种算法的思想和队列一样,总是淘汰最先进入内存的页面,即选择在内存中驻留时间最久的页面淘汰。实现:把一个进程已调入内存的页面按先后次序链接成一个队列,并且设置一个指针总是指向最老的页面。缺点:对于有些经常被访问的页面如含有全局变量、常用函数、例程等的页面,不能保证这些不被淘汰。
根据页面调入内存后的使用情况做出决策。LUR置换算法是选择最近最久未使用的页面进行淘汰。
中断就是在计算机执行程序的过程中,由于出现了某些特殊事情,使得CPU暂停对程序的执行,转而去执行处理这一事件的程序。等这些特殊事情处理完之后再回去执行之前的程序。中断一般分三类:
与中断紧密相连的一个概念:中断处理程序,当中断发生的时候,系统需要去对终端进行处理,对这些终端的处理是由操作系统内核中的特定函数进行的,这些处理中断的特定函数就是我们所说的中断处理程序。
另一个与中断紧密相连的概念:中断的优先级。中断的优先级说明的是当一个中断正在被处理的时候,处理器能接受的中断的级别。中断的优先级也表明了中断需要被处理的紧急程度。**每个中断都有一个对应的优先级,当处理器在处理某一中断的时候,只有比这个中断优先级高的中断可以被处理器接受并且被处理。**优先级比这个当前正在被处理的中断优先级要低的中断将会被忽略。
典型的中断优先级:
权限不一样。
当有多个线程时,经常需要去同步(注:同步不是同时刻)这些线程以访问同一个数据或资源。例如,假设有一个程序,其中一个线程用于把文件读到内存,而另一个线程用于统计文件中的字符数。当然,在把整个文件调入内存之前,统计他的计数是没有意义的。但是,由于每个操作都有自己的线程,操作系统会把两个线程当作是互不相干的任务分别执行,这样就可能在没有把整个文件装入内存时统计字数。为解决此问题,必须使两个线程同步工作。
线程间的同步方法大体可分为两类:用户模式和内核模式。顾名思义,内核模式就是指利用系统内核对象的单一性来进行同步,使用时需要切换内核态与用户态,而用户模式就是不需要切换到内核态,只在用户态完成操作。
用户模式下的方法有:原子操作(例如一个单一的全局变量)、临界区。
内核模式下的方法有:事件、信号量、互斥量。
在内存管理中,内部碎片是已经被分配出去的的内存空间大于请求所需的内存空间。
外部碎片是指还没有分配出去,但是由于大小太小而无法分配给申请空间的新进程的内存空间空闲块。
固定分区存在内部碎片,可变式分区分配会存在外部碎片;页式虚拟存储系统存在内部碎片;段式虚拟内存系统,存在外部碎片,为了有效利用内存,使内存产生更少的碎片,要对内存分页,内存以页为单位来使用,最后一页往往装不满,于是形成了内部碎片。
为了共享要分段,在段的换入换出时行成外部碎片,比如5k的段换出后,有一个4k的段进来放到原来5k的地方,于是形成1k的外部碎片。
当有多个线程的时候,经常需要去同步这些线程以访问同一个数据或资源。例如,假设有一个程序,其中一个线程用于把文件读到内存,而另一个线程用于统计文件中的字符数。当然,在把整个文件调入内存之前,统计它的计数是没有意义的。但是,由于每个操作都有自己的线程,操作系统会把两个线程当作是互不相干的任务分别执行,这样就可能在没有吧整个文件装入内存时统计字数。为解决此问题,必须使两个线程同步工作。
如果多线程的程序运行结果是可预期的,而且与单线程的程序运行结果一样,那么说明是“线程安全”的。
同步:
异步:
IO多路复用是指内核一旦发现进程指定的一个或者多个 IO 条件准备读取,它就通知该进程。IO多路复用适用于如下场合:
如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。或者说:一个类或者程序所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题。
线程安全问题都是由全局变量及静态变量引起的。
若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。
一个进程中的所有线程共享该进程的地址空间,但他们有各自独立的(私有的)栈(stack),Windows 线程的缺省堆栈大小为 1M。堆(heap)的分配与栈有所不同,一般是一个进程有一个C运行时堆,这个堆为本进程中所有线程共享,Windows 进程还有所谓进程默认堆,用户也可以创建自己的堆。
堆:是大家共有的空间,分全局堆和局部堆。全局堆就是所有没有分配的空间,局部堆就是用户分配的空间。堆在操作系统对进程初始化的时候分配,运行过程中也可以向系统要额外的堆,但是记得用完了要还给操作系统,要不然就是内存泄漏。
栈:是线程独有的,保存其运行状态和局部自动变量的。栈在线程开始的时候初始化,每个线程的栈互相独立,因此,栈是 thread safe 的。操作系统在切换线程的时候会自动切换栈,就是切换 SS/ESP 寄存器。栈空间不需要在高级语言里面显式的分配和释放。

