• 操作系统之互斥记录


    忙等待: 

    while (1); // 占用CPU资源

    饥饿:

    高个子(进程1)和矮子(进程2)抢篮板(CPU执行权), 矮子抢不到篮板, 矮子饥饿.

    一. 临界区的四个特点

    1. 互斥
    2. 前进
    3. 有限等待
    4. 无忙等待(可选)
    临界区很短时, 允许忙等待
    临界区很长时, 考虑基于上下文切换实现无忙等待

    二. 实现互斥的四种方式

    1. 禁用硬件中断
    OS如何实现多进程?
    通过时钟中断, 导致进程切换, 实现并发.

    具体方法:
    进入临界区 禁用中断
    退出临界区 开启中断

    局限性:
    - 硬件事件无法及时响应
    - 临界区长, 影响系统效率
    - 多CPU下, 无法实现互斥

    2.  软件实现
    2.1Peterson算法

    适用于双进程

    Peterson算法的多进程变式:
    Eisenberg and Meauire's 算法

    两个重要的参数:
    flag 需要?
    turn 谁来?

    伪代码(双进程)

    1 process (i) {
    2     do {
    3         flag[i] = true;
    4         turn = j; // 进程i将机会让给进程j
    5         while (turn == j && flag[j]); // 进程j把握机会且本身也想进入临界区
    6         // 临界区
    7         flag[i] = false;
    8         } while (1);
    9 }

    可以使用反证法证明Peterson算法满足临界区互斥, 前进和有限等待的属性.

    2.2 Dekker算法

    适用于双进程

    2.3 面包店算法
    适用于多进程

    3. 原子指令

    3.1 锁

    两个方法:
    acquire: 等待解锁, 获取锁
    release: 解锁, 唤醒

    锁方法具体实现:

    3.1.1 基于test-and-set机器指令

    test-and-set内部实现

    1 boolean TestAndSet(boolean* target)
    2 {
    3     boolean rv = *target;
    4     *target = true;
    5     return rv;
    6 }

    忙等待版:

     1 class Lock {
     2     int value = 0;
     3 }
     4 
     5 Lock::Acquire() {
     6     while (test-and-set(value));
     7 }
     8 
     9 Lock::Release() {
    10     value = 0;
    11 }

    两种情况:
    锁被释放, value=0, value=1且返回0, 跳过while, 进入临界区
    锁被占用, value=1, value=1且返回1, 自旋while, 忙等待状态

    若临界区很长时, 应该考虑基于上下文切换实现无忙等待

    无忙等待版:

     1 class Lock {
     2     int value = 0;
     3     WaitQueue q;
     4 }
     5 
     6 Lock::Acquire() {
     7     while (test-and-set(value)) {
     8         add this TCB to wait queue q;
     9         schedule(); // 线程睡眠, 阻塞, 让出CPU
    10     }
    11 }
    12 
    13 Lock::Release() {
    14     value = 0;
    15     remove one thread t from q;
    16     wakeup(t); // 唤醒
    17 }

    3.1.2 基于exchange机器指令

    exchange内部实现

    1 void Exchange(boolean* a, boolean* b)
    2 {
    3     boolean temp = *a;
    4     *a = *b;
    5     *b = temp;
    6 }

    伪代码:

     1 int lock = 0;
     2 
     3 thread (i) {
     4     int key;
     5     do {
     6             key = 1;
     7             while (key == 1) exchange(lock, key);
     8             // 临界区
     9             lock = 0;
    10         } while (1);
    11 }

    3.2 信号量

    可实现同步和互斥

    3.2.1 信号量的实现

     1 // 信号量
     2 class Semaphore {
     3     int sem;
     4     // 等待队列
     5     WaitQueue q;
     6 }
     7 
     8 // P操作
     9 Semaphore::P() {
    10     sem--;
    11     if (sem < 0) {
    12         add this thread t to q;
    13         block(t);
    14     }
    15 }
    16 
    17 // V操作
    18 Semaphore::V() {
    19     sem++;
    20     if (sem <= 0) {
    21         remove a thread t from q;
    22         wakeup(t);
    23     }
    24 }

    4. 管程(可实现同步或互斥)

    schedule和wakeup操作

    1 schedule(): 当前thread原地睡眠(醒来继续向下执行), 获取一个就绪thread
    2 wakeup(): 将sleep进程 -> 就绪态进程

    4.1. 实现管程

    管程由Lock和条件变量组成

    Lock保证管程中只有唯一一个线程

    4.1.1 Lock实现

    见上文

    4.1.2 条件变量实现

     1 // 条件变量
     2 Class Condition {
     3     // 等待队列默认为空
     4     int numWaiting = 0;
     5     WaitQueue q;
     6 }
     7 
     8 // 条件变量等待操作
     9 Condition::Wait(lock) {
    10     numWaiting++;
    11     add this thread t to q;
    12     // 释放当前锁
    13     release(lock);
    14     schedule();
    15     // 重新获得锁
    16     require(lock);
    17 }
    18 
    19 // 条件变量唤醒操作
    20 Condition::Signal() {
    21     // 等待队列不为空时
    22     if (numWaiting > 0) {
    23         remove a thread from q;
    24         wakeup(t);
    25         numWaiting--;
    26     }
    27 }

    4.2. 使用管程

    生产者消费者问题

     1 // 缓冲区
     2 class BoundedBuffer {
     3     ...
     4     Lock lock;
     5     Condition notFull, notEmpty;
     6     // 缓冲区产品个数, 默认为空
     7     int count = 0;
     8 }
     9 
    10 // 生产者
    11 BoundedBuffer::Deposit(c) {
    12     lock->Acquire();
    13     // 缓冲区为满
    14     while (count == n)
    15         notFull.Wait(&lock);
    16     add c to the buffer;
    17     // 缓冲区产品个数增加1, 执行此操作之后, 缓冲区必定不为空
    18     count++;
    19     notEmpty.Signal();
    20     lock->Release();
    21 }
    22 
    23 // 消费者
    24 BoundedBuffer::Remove(c) {
    25     lock->Acquire();
    26     // 缓冲区为空
    27     while (count == 0)
    28         notEmpty.Wait(&lock);
    29     remove c from buffer;
    30     // 缓冲区产品个数减少1, 执行此操作之后, 缓冲区必定不为满
    31     count--;
    32     notFull.Signal();
    33     lock->Release();
    34 }

    三. 进程通讯

    1. 通讯方式

    间接通讯: 管道  消息队列(先进先出msg)  共享内存(shm)

    直接通讯: 信号 

    2. 阻塞和非阻塞

    阻塞 同步
    非阻塞 异步

  • 相关阅读:
    C#文件操作常用相关类(Directory类、File类、Path类)
    winform使用相对路径读取文件的方法
    设置GridView不换行强制GridView不换行GridView强制不换行
    VS2010 Visual Studio2010 保护视力 背景色设置颜色设置
    20190306
    20190325
    常用DOS命令
    项目创建
    VS2015自定义工具栏,往工具栏上添加按钮
    ping不通公网ip时路由器设置
  • 原文地址:https://www.cnblogs.com/shaohsiung/p/9983856.html
Copyright © 2020-2023  润新知