ICode9

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

操作系统期末复习——第六章 进程同步

2022-06-12 10:02:42  阅读:143  来源: 互联网

标签:复习 进程同步 signal 信号量 互斥 临界 第六章 进程 wait


第六章 线程同步

1.背景

防止竞争条件问题——多个进程共享数据,需要保持数据的一致性

防止竞争条件的关键:确保操作共享数据的代码段执行同步(互斥运行)

2.临界区问题

多个进程同时操作共享数据时,每个进程操作共享数据的代码段,这个代码段成为临界区

解决竞争条件的关键:

  1. 确保单个进程在临界区内执行
  2. 确保其他进程可以进入临界区问题

解决临界区问题需要满足如下条件:

  1. 互斥
  2. 前进:没有进程在临界区,确保其他进程能进入临界区
  3. 有限等待:一个进程请求进入临界区,等待时间必须有限

3.Peterson's算法

前提条件:加载和存储指令是原子指令,不可被中断

设置两个变量:

int turn//表示哪个进程可以进入临界区
bool flag[n]//表示哪个进程想要进入临界区,n为进程数量
    
do{
    flag[i] = true;
    turn = i;
    while(flag[j] && yurn == j);
    //临界区代码
    flag[i] = false;
    //退出区代码
}while(true);
  • 没有turn的话,两个程序并行执行可能导致一直卡在while循环

4.硬件同步

单、多处理器系统都可以通过禁用中断的方式解决临界区问题,单是代价高!

硬件同步主要采用原子指令(不可中断)——TestAndSet() / swap()

TestAndSet()指令:

bool TestAndSet(bool* lock){
//lock代表临界区是否上锁,true则上锁、false则不上锁,默认false
    bool old = *lock;
    lock = ture;
    return old;
}

//进程判断代码
do{
    while(TestAndSet(&lock));
    //临界区代码
    lock = false;
    //结束区代码
}

swap()指令

//lock同上
void swap(bool* a,bool* b){
    bool tmp = *a;
    *a = *b;
    *b = tmp;
}

//进程判断代码
while(true){
    old = true;
    while(old == true)swap(&old,&lock);
    //临界区代码
    lock = false;
    //结束区代码
}

Peterson's,TestAndSet,swap都会出现忙等待问题!

5.信号量

无忙等待的同步工具

用S来表示信号量,S为整数变量(一般表示临界资源的数量)

只能通过标准原子操作来访问信号量

  1. wait(S): 也称P(S)

    wait(S){
        while(S<=0);
        S--
    }
    
  2. signal(S): 也称V(S)

    signal(S){
            S++;
    }
    

封锁代码的实现

do{
    wait(S);
    //临界区代码
    signal(S);
    //结束区代码
}while(true);

信号量的实现关键是保证wait()和signal()操作的原子执行,没有两个进程能同时对一个信号量执行以上操作

无忙等待的信号量实现

为了让忙等待的进程挂起,在可以进入临界区时重新启动。

将信号量定义如下:

typedef struct{
    int value;//整数值,资源数量
    struct process *list;
}semaphore

wait和signal定义如下:

wait(semaphore *S){
    S->value--;
    if(S->value < 0){
        //将该进程加入等待队列S->list中
        block();//挂起该进程
    }
}

signal(semaphore *S){
    S->value++;
    if(S->value <= 0){
        //从S->list中取出一个进程P
        wake(P);//唤醒取出的进程P
    }
}

死锁与饥饿

6.经典同步问题

6.1有限缓冲问题

假定缓冲池有n个缓冲项,每个缓冲项能存一个数据项

  1. 缓冲池满不能写
  2. 缓冲池空不能读
  3. 读和写互斥

解决方法:

  1. 定义信号量empty——有几个空缓冲项,初始化为n
  2. 定义信号量full——有几个满缓冲项,初始化为0
  3. 定义信号量mutex——保证读写互斥,初始化为1

生产者(写)代码实现:

while(true){
    //生产一个缓冲项
    wait(empty);
    wait(mutex);
    //写入缓冲池
    signal(mutex);
    signal(full);
}

消费者(读)代码实现:

while(true){
    wait(full);//full>0,只要缓冲项有数据就读
    wait(mutex);
    //读出一个缓冲项
    signal(mutex);
    signal(empty);
    //使用读出的数据
}

6.2读者-写者问题

问题规定:

  1. 读写互斥,写写互斥
  2. 多人读可以同时访问数据,但是要知道读的人数

解决方法:

  1. 定义信号量wrt——读写互斥
  2. 定义countread——记录读的人数
  3. 定义信号量mutex——写countread互斥

写者进程代码实现:

while(true){
    wait(wrt);
    //写
    signal(wrt);
}

读者进程代码实现:

while(true){
    wait(mutex);
    countread++;
    if(countread == 1){//第一位读者加wrt锁
        wait(wrt);
    }
    signal(mutex);
    //读
    wait(mutex);
    countread--;
    if(countread == 0){//最后一位读者释放wrt锁 
        signal(wrt);
    }
    signal(mutex);
}

6.3哲学家用餐问题

  • 哲学家只会思考,吃饭
  • 1碗米饭,5根筷子,每个哲学家之间各有一根筷子
  • 同时又两根筷子才能吃饭

问题:可能产生死锁(每个哲学家同时请求他们左手的筷子)

几个解决方案:

  • 规定至多有4个哲学家开始吃饭
  • 单数位上的哲学家必须先申请左边的筷子,双数位上的哲学家必须先申请右边的筷子
  • 每个哲学家申请筷子时互斥,哲学家必须一次申请完两根筷子

7.管程

为了解决不当使用信号量而产生的死锁问题

管程是一种程序结构(很像java中的类)

  1. 采用面向对象的方法,简化进程间同步控制
  2. 任何时刻最多只有一个线程执行管程代码
  3. 可以临时放弃管程的互斥访问,等待事件出现时恢复

为什么引入管程:

  1. 把各进程临界区集中管理
  2. 防止违法同步操作
  3. 用高级语言程序书写便于程序的正确性识别

管程的组成:

  1. 一个锁,控制管程访问的互斥
  2. 0或多个条件变量,管理共享数据的互斥访问
  3. 一个条件变量对应一个等待队列,每个条件变量有以一个wait(),signal()操作

标签:复习,进程同步,signal,信号量,互斥,临界,第六章,进程,wait
来源: https://www.cnblogs.com/zhaoqinmumiand/p/16367422.html

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

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

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

ICode9版权所有