OS concept-同步

2021-11-05  本文已影响0人  1哥

OS concept - 同步工具(Synchronization tools)

1.背景

多个进程可以并发或并行执行,并发在于进程可能执行完部分程序的时候,被打断,处理器转而执行另一个进程的程序;并行在于代表两个进程的两个指令流在各自的处理器上同时执行。
多个并发或并发执行的程序访问到共享数据时,可能会导致数据的不一致;
故需要有种机制确保进程的有序执行,以维护数据的一致性。
举例说明-生产者-消费者程序模型
生产者程序

while (true) { 
    /* produce an item in next produced */ 
    while (count == BUFFER SIZE) ; 
    /* do nothing */ 
    buffer[in] = next produced; 
    in = (in + 1) % BUFFER SIZE;
    count++; 
}

消费者程序

while (true) { 
    while (count == 0) ; 
    /* do nothing */ 
    next consumed = buffer[out];
    out = (out + 1) % BUFFER SIZE; 
    count--; 
    /* consume the item in next consumed */ }

其中当生产者程序count++和消费者程序count--并发执行时,两者执行后结果可能是4,5或6.
count++ 等同于

register1 = count
register1 = register1 +1 
count = register1

count--等同于

register2 = count
register2 = register2 - 1 
count = register2

两者执行顺序可能是:
(1) 若生产者程序先执行,则结果是4,反过来则是6==》 不正确的状态

T0: producer execute register1 = count {register1 = 5} 
T1: producer execute register1 = register1 + 1{register1 = 6} 
T2: consumer execute register2 = count {register2 = 5} 
T3: consumer execute register2 = register2 − 1{register2 = 4} 
T4: producer execute count = register1 {count = 6} 
T5: consumer execute count = register2 {count = 4}

(2) 若两者错开执行,则为5 ==》正确的状态
这种状态就是竞态。这个问题主要靠进程同步工具来解决。

2.临界区(critical section)

避免竞态问题,即避免进程访问的共享数据的部分代码出现竞态,可将这部分程序抽象成一个临界区,并保证两个进程不可能同时临界区。
临界区框架

image.png
临界区问题解决方案要求
1)互斥-一个进程处于临界区时,不会有其他进程同时处于临界区;
2)前进-没有进程进入临界区时,下次进临界区的进程,不会无限期等待;
3)有限等待-不能使其他进程无限等待临界区;

3.同步的硬件支持

3.1 内存屏障指令

内存模型两种:
强有序:一个处理器上的内存修改立刻对所有其他处理可见;
弱有序:一个处理器上的内存修改可能不会立刻对所有其他处理可见;
内存屏障指令

例子:
线程1 执行

while (!flag)   
memory_barrier();  
print x

线程2 执行

x = 100;  
memory_barrier();  
flag = true

线程1加内存屏障指令保证在加载x 前,加载flag;
线程2加内存屏障指令保证,在赋值给flag 前先赋值给x;

3.2 硬件指令-特殊的硬件指令

Test-and-Set 指令

Compare-and-Swap 指令

Test-and-Set 指令

boolean test_and_set (boolean *target)
{
      boolean rv = *target;
      *target = true;
      return rv:
}
do {      while (test_and_set(&lock)) 
      ; /* do nothing */ 
      /* critical section */ 
      lock = false; 
      /* remainder section */ 
} while (true);

Compare-and-Swap 指令

int compare_and_swap(int *value, int expected, int new_value)
{                  
    int temp = *value; 
    if (*value == expected) 
         *value = new_value; 
    return temp; 
} 
while (true){
      while (compare_and_swap(&lock, 0, 1) != 0) 
          ; /* do nothing */ 
      /* critical section */ 
      lock = 0; 
      /* remainder section */ 
} 

有限等待的compare-and-swap

while (true) {   waiting[i] = true;   key = 1;   while (waiting[i] && key == 1) 
      key = compare_and_swap(&lock,0,1); 
   waiting[i] = false; 
   /* critical section */ 
   j = (i + 1) % n; 
   while ((j != i) && !waiting[j]) 
      j = (j + 1) % n; 
   if (j == i) 
      lock = 0; 
   else 
      waiting[j] = false; 
   /* remainder section */ 
}

3.3 原子变量

void increment(atomic_int *v){  int temp;   do {        temp = *v;  }   while (temp != (compare_and_swap(v,temp,temp+1));} 

4.同步工具

4.1 互斥量

while (true) { 
    acquire lock 
            
       critical section 

    release lock 
    
        remainder section 
} 

4.2 信号量(semaphore)

4.2.1 基本

wait(S) { 
    while (S <= 0)
       ; // busy wait
    S--;
}
signal(S) { 
    S++;
}

4.2.2 信号量用途

wait(mutex);
   CS
signal(mutex);
P1:
   S1;
   signal(synch);
P2:
   wait(synch);
   S2;

4.2.3 信号量的实现

前面定义的wait()和signal() 信号量操作都需要忙等的弱点,类似于互斥量的实现。
若临界区占用很少CPU时间,则只有很少的忙等。
若应用在临界区占用很多时间,则不是好的解决方案。
这就需要不忙等的信号量:

  1. value : int
  2. 指向链表的下一个entry
typedef struct { 
    int value; 
    struct process *list; 
} semaphore; 
wait(semaphore *S) { 
   S->value--; 
   if (S->value < 0) {
      add this process to S->list; 
      block(); 
   } 
}

signal实现

signal(semaphore *S) { 
   S->value++; 
   if (S->value <= 0) {
      remove a process P from S->list; 
      wakeup(P); 
   } 
} 
上一篇 下一篇

猜你喜欢

热点阅读