1.进程同步
进程同步机制的主要任务是对多个相关进程在执行次续上进行协调,使并发执行的诸进程之间能按照一定的规则(或时序)共享系统资源,并能很好地相互合作,从而使程序的执行具有可再现性。
同步机制应遵循的规则:空闲让进、忙则等待、有限等待、让权等待。
进程同步的方式:
①硬件同步机制:
(1)中断屏蔽方法
在进入锁测试之前关闭中断,直到完成锁测试并上锁之后才能打开中断。
优点:简单、高效
缺点:滥用关中断权力可能导致严重后果;关中断时间过长,会影响系统效率,限制了处理器交叉执行程序的能力;不适用于多CPU系统。
(2)Test-And-Set / TS指令 也叫 Test-And-Set-Lock / TSL指令
//TS指令的一般性描述
bool TS(bool *lock)
{
bool old;
old = *lock;
*lock = true;
return old;
}
//利用TS指令实现互斥的循环进程结构
while (TS(&lock)); //上锁并检查
临界区代码段
lock = false;//解锁
剩余区代码段
优点:有效地实现进程互斥,实现简单,无需像软件实现方法那样严格检查是否会有逻辑漏洞;适用于多处理机环境
缺点:临界资源忙碌时,其它访问进程必须不断的进行测试,处于一种“忙等”状态,不符合“让权等待”的原则,造成处理机时间的浪费,同时也很难将它们用于解决复杂的进程的同步问题。
(3)Swap指令 / XCHG指令
//处理过程描述如下
void Swap(bool *a, bool *b)
{
bool temp;
temp = *a;
*a = *b;
*b = temp;
}
//利用Swap指令实现进程互斥的循环进程可描述如下
bool old = true;
while (old == true)
Swap(&lock, &old);
临界区代码端...
lock = false;
剩余区代码端...
优缺点同TS指令一致。
②信号量机制:
用户进程可以通过使用操作系统提供的一对原语来对信号量进行操作,从而很方便的实现了进程互斥、进程同步。
信号量是一个变量(可以是一个整数,也可以是更复杂的记录型变量),可以用一个信号量来表示系统中某种资源的数量。
(1)整形信号量
//原子操作(原语) wait原语 和 signal原语
wait(S)//P操作 相当于进入区
{
while(S<=0);//如果资源数量不够 一直循环等待
S--;//如果资源数量够,占用一个资源
}
signal(S)//V操作 相当于退出区
{
S++;//使用完资源后在退出区释放资源
}
//进程Pn
...
wait(S);
使用资源...
signal(S);
...
在整形信号量机制中的wait操作,只要是信号量S≤0,就会不断的测试。因此,该机制并未遵循“让权等待”的准则,而是使进程出现“忙等”的状态。
(2)记录型信号量
除了需要一个用于代表资源数目的整形变量value外,还应增加一个进程链表指针,用于链接上述所有的等待进程。
//记录型信号量的定义
typedef struct
{
int value;//剩余资源数
struct process_control_block *L;//等待队列
}semaphore;
//某进程需要使用资源时,通过wait原语申请资源
void wait(semaphore S)
{
S.value--;//需要占用一个资源 剩余资源数减一
if(S.value<0)//如果减一之前不够一个
{
block(S.L);//锁住 从运行态变为阻塞态
}
}
//某进程需要使用完资源后,通过signal原语释放资源
void signal(semaphore S)
{
S.value++;//释放一个资源
if(S.value<=0)//如果还有进程在等待资源
{
weakup(S.L);//唤醒等待队列中的一个进程,从阻塞态变为运行态
}
}
(3)AND型信号量
一个进程往往需要获得两个或更多的共享资源后方能执行任务,假定A和B都要访问数据D和E,假设D E资源个只有一个,刚开始给进程A分配D资源,给进程B分配E资源,两者都无法从僵持状态中解脱出来,称A和B已经进入死锁状态。
AND同步机制的基本思想是:将进程在整个运行过程中需要的所有资源,一次性全部分配给进程,待进程使用完后再一起释放。只要尚有一个资源未能分配给进程,其他所有可能为之分配的资源也不分配给它。即对若干个临界资源的分配采取原子操作方式:要么把它所请求的资源全部分配到进程,要么一个也不分配,以此来避免“死锁”。
在wait操作中加入一个“AND”条件,故称为AND同步,或称为同时wait操作。
Swait(S1,S2,...,Sn)
{
while(true)
{
if(Si>=1&&...&&Sn>=1)
{
for(i=1;i<=n;i++)
{
Si--;
}
break;
}
else
{
将进程放到第一个Si<1的等待队列中
}
}
}
Ssignal(S1,S2,...,Sn)
{
while(true)
{
for(i=1;i<=n;i++)
{
Si++;
将与Si相关的等待队列中等待的所有进程移动到准备队列中
}
}
}
(4)信号量集
在记录型信号量机制中,wait(S)和signal(S)操作仅能对信号量+1或-1,意味着每次只能对某临界资源换进行一个单位的申请或释放。当一次需要N个单位时,便要进行N次wait(S)操作,这是低效的甚至会增加思索的概率。此外,在有些情况下,为确保系统的安全性,当所申请的资源数量低于某一下限值时,在每次分配之前,都必须测试资源的数量,判断是否大于可分配的下限值,决定是否予以分配。
基于以上两点,对AND信号加以扩充,对进程所申请的所有资源以及每类资源不同的资源需求量,在一次P、V原语操作中完成申请或释放。进程对信号量Si的测试值不再是1,而是该资源的分配下限值ti,即要求Si≥ti,否则不予分配。一旦允许分配,进程对该资源的需求值为di,即表示资源占用量,进行Si=Si-di操作,由此形成一般化的“信号量级”机制。
//对应的Swait和Ssignal格式
Swait(S1,t1,d1,...,Sn,tn,dn);
Ssignal(S1,d1,...,Sn,dn);
③管程机制
(没展开写就是我还没学 )
2.进程通信
系统中各进程存在着一定制约关系,这种制约关系来源于并发进程的合作以及对资源共享,所以要进行必要通信。
进程通信的类型:
①共享存储器系统
在共享系统中,相互通信的进程共享某些数据结构或共享存储区,进程之间能够通过这些空间进行通信。
(1)基于共享数据结构的通信方式:
要求诸进程公用某些数据结构,借以实现诸进程间的信息交换,如在生产者-消费者问题中的有界缓冲区。操作系统仅提供共享存储器,由程序员负责对公用数据结构的设置及进程间同步的处理。
缺点:仅适用传递相对少量的数据,通信效率低下,属于低级通信。
(2)基于共享存储区的通信方式:
为了传输大量数据,在内存中划出了一块共享存储区域,诸进程可通过对该共享区的读或写交换信息,实现通信,数据的形式和位置甚至访问控制都是由进程负责,而不是OS。
这种通信方式为高级通信。
进程在通信前,先向系统申请获得共享存储区中的一个分区,并将其附加到自己的地址空间中,对其中的数据进行正常读、写,读写完成或不再需要时,将其归还给共享存储区。
②管道通信系统
管道:用于连接一个读进程和写进程以实现它们之间通信的一个共享文件,又名pipe文件。
③消息传递系统
以格式化的消息为单位,将通信的数据封装在消息中,并利用操作系统提供的一组通信命令,在进程间进行消息传递,完成进程间的数据交换。