ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

操作系统概念 第6章 进程同步

2021-04-25 19:33:28  阅读:252  来源: 互联网

标签:readcnt 操作系统 进程同步 互斥 signal 概念 线程 进程 wait


概述

多进程并发访问操作同一数据,且执行结果与访问顺序有关,这种现象称为竞争条件。为避免竞争条件,需要进行进程同步。

临界区问题中,没有两个进程可以同时在临界区内执行,代码可以分为进入区、临界区、退出区、剩余区。三个基本的要求是:互斥访问,空闲让进,有限等待。假设每个进程的执行速度非零,但相对速度没有任何假设。

非抢占内核中,处于内核态的进程会一直运行到退出内核态、阻塞或者主动放弃 CPU,这种模式下不会有竞争条件,但抢占内核更适合实时程序,响应更快。

解决临界区问题

临界区问题可以用较为繁复的软件方法解决,而一些简单的尝试可能不能同时满足三个要求。证伪一个要求的方式是构造恰当的执行顺序,很多时候一人一步或一人一直走等极端情况会很有效。

软件方法

单标志法中,我们维护 turn 表示接下来该由谁来访问,进入区只需要 turn=j; while(turn!=i);,即先谦让,等被让回自己时再走。这样不满足空闲让进。

双标志先检查法中,我们维护 flag[] 表示某个进程是否有访问的意愿。所谓先检查,就是先检查是否有他人有意愿,再提出自己意愿。这样不满足互斥访问。同理,双标志后检查法不满足空闲让进。

Peterson 算法是双标志后检查和单标志法的融合,“我有想法但下次归你,如果你有想法并且下次归你我就等。”三个要求同时得到满足。

Bakery 算法维护 choosing[] 表示某个进程是否在选号,选到的号存进 number[] 中,前项为摇号机摇到的号(为当前 number[] 中最大值 +1),后项为自身进程号。进程先标记 choosing[i] 然后计算一个 number,取消 choosing[i] 后扫描一遍,如果有人在选就等他选好,如果有人的编号比我小就等着。扫描完后即可进入邻接区,退出时别忘了清楚编号。注意 choosing[] 是必须的,不妨设想进程 1 已经计算出号 (1,1) 还未保存,切到进程 2 计算出号 (1,2),但以为自己最小而进入临界区,此时切回进程 1 又进入临界区,未被互斥访问。

硬件方法

下面我们考虑如何构建一个互斥锁。用单标志 flag 显然是不行的,我们考虑一些硬件支持。

关中断是有效的,但它权限太高,不适用于多处理器,可能会丢失中断,也不够高效。

借助 Test-and-Set 指令,我们用 while(TAS(flag,1)==1); 就可以实现加锁,解锁只需 flag=0;,这样不满足有限等待,但一个标记数组和循环扫描也许会有所帮助,即解锁时按顺序考虑唤醒一个等待者。

借助 Compare-and-Swap 指令,我们用 while(CAS(flag,0,1)==1); 就可以实现加锁,类似地,它同样不满足有限等待。

条件变量

条件变量是一种显式队列,线程可以通过条件变量来等待一个条件变为真。执行条件不满足时,线程可以把自己加入队列。另一个线程改变了状态时可以通过发信号来唤醒等待线程。

wait(condition, mutex) 调用时 mutex 必须已是上锁状态,wait 会释放 mutex 并使调用线程休眠。线程被唤醒时必须重新获取 mutex 再返回调用者。signal(condition) 用于唤醒某个条件变量上的睡眠线程。

用条件变量可以实现线程的 joinexit,设条件变量 c 配套有互斥锁 m 和标记 done,则 join() 实现为 lock(m); while(!done) wait(c,m); unlock(m);exit() 实现为 lock(m); done=1; signal(c); unlock(m);。如果去掉互斥锁 m 或者标记 done 都可能会导致先 signalwait,因此通常条件变量会和互斥锁与标记配套使用。

信号量

信号量是一个整数变量,通过 wait()signal() 原语来操作。最简单的信号量是个自旋锁,为了克服忙等待我们稍加修改。若 wait() 时发现信号量值不为正则必须阻塞自己,而 signal() 后若信号量值不为正则从对应的等待队列头部唤醒一个阻塞的进程。具体地,wait() 相当于 value--; if(value<0) push(this), block(this);signal() 相当于 value++; if(value<=0) wakeup(pop());。另外,对 SMP 系统,还需要其它加锁机制来保证 waitsignal 的原子性。

多个进程无限等待只能由它们自己产生的事件,则它们称为死锁。进程在信号量内无限期等待称为饥饿。

经典同步问题

生产者-消费者问题

定义三个信号量,empty 表示空缓冲区数,full 表示满缓冲区数,mutex 用于保护缓冲区。

对于生产者,只需要 wait(empty); wait(mutex);,然后干活并释放即可。

注意 wait 的顺序不能颠倒,否则会出现死锁。

读者-写者问题

第一类读者-写者问题要求读者优先。实现时需要 readcnt 表示当前读者数量,readcnt_m 保护 readcntrw_m 表示当前是否有人在读或者有人在写。实现如下:

Write()
{
    wait(rw_m);
    write();
    signal(rw_m);
}
Read()
{
    wait(readcnt_m);
    readcnt++;
    if(readcnt==1) wait(rw_m);
    signal(readcnt_m);
    
    read();
    
    wait(readcnt_m);
    readcnt--;
    if(readcnt==0) signal(rw_m);
    signal(readcnt_m);
}

第二类读者-写者问题要求写者优先,即一旦有写者要写,下次就不得让读者进入。实现时需要 readcount 表示当前读者数量,writecount 表示当前写者数量,read_m 表示当前是否可读,write_m 表示当前是否可写。此外还需要保护数据一致性的 readcount_mwritecount_m

哲学家进餐问题

在哲学家进餐问题中,可以通过限制进餐者总数,或者让部分人反手,来解决死锁。

标签:readcnt,操作系统,进程同步,互斥,signal,概念,线程,进程,wait
来源: https://www.cnblogs.com/mollnn/p/14701641.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有