Chapter5 Synchronization--操作系统翟岩龙老师

Motivation of Synchronization

  • When threads concurrently read/write memory, program behavior is undefined.
  • Thread schedule is non-determinstic.
  • Also compiler/hardware instruction reordering and mulit-word operations are not atomic

Threads

1.Independent Threads
No state shared with other threads —> determinstic and reproducible.
2.Cooperating Threads
State shared with multiple threads —> nondeterminstic and non-reproducible.
cooperating threads
Most multi-thread process have both per-thread state(stack and registers which are stored in the TCB) and shared state(heap).

Race Conditions and Reording

Race conditions : What
Reording : Why

Implementation of Synchronization

Synchronization : using atomic operations to ensure cooperation between threads.
Mutual exclusion and critical section are two ways of describing the same thing.

Solutions by using Synchronization Variables
1.Lock
2.Condition Varibale
3.Semaphore

Lock

Lock before going to the critical section and before accessing the shared data.
Lock :: Acquire()
wait until lock is free
Lock :: Release()
release lock, waking up anyone waiting for it.

Rules for using locks:

  • lock is free initially
  • acquire before accessing the shared data and release after accessing the shared data
  • never access shared data without lock
  • example : bounded buffer

Lock implementation with Interrupts

Recall and analysis --> general idea is implementing locks by disabling/enabling interrupts.
Key idea : maintain a lock variable and impose mutual exlusion only during operations on that variable.

// the code between the dis/enable interrupt is called the critical section
int value = FREE;
Acquire() {
 disable interrupts
 if (value == BUSY) {
  put thread on wait queue;
  Go to sleep();
  // and The scheduler selects other threads
  // Enable interrupts?
 } else {
  value = BUSY;
 }
 enable interrupts;
}

Release() {
 disable interrupts;
 if (anyone on wait queue) {
  take thread off wait queue
  Place on ready queue;
 } else {
  value = FREE;
 }
 enable interrupts;
}

In the acquire, and value == Busy,

 if (value == BUSY) {
  put thread on wait queue;
  Go to sleep();
  // and The scheduler selects other threads
  // Enable interrupts?
 }

Actually we have 3 positions to enable the interrupts, and we have to choose the third one which is after go to sleep. The scheduler will do the following work because the thread doesn’t work after going to sleep.

How to enable interrupts after going to sleep?
Context Switch
In scheduler, since interrupts are disabled when you call sleep:Responsibility of the next thread to re-enable ints;
When the sleeping thread wakes up, returns to acquire and re-enables interrupts;

Lock implementation with Atomic instruction sequences

We have talked a lot about the lock, but interrupts are only available with the single CPU situation. If we have multiprocessor, we need other forms of lock.

Atomic Read-Modify-Write Instructions

  • Read-modify-write instructions
    Atomically read a value from memory, operate on it, and then write it back to memory
    Intervening instructions prevented in hardware(hardware implemented)
  • Examples
    Test and Set
    Exchange (Intel: xchgb, w/ lock prefix to make atomic)
    Compare and Swap
  • Any of these can be used for implementing locks and condition variables!
    test&set
test&set (&address) {           /* most architectures */
    result = M[address];        // return result from “address” and
    M[address] = 1;             // set value at “address” to 1 
    return result;
}

Implementing locks with test&set
Another flawed, but simple solution:

 int value = 0; // Free
  Acquire() {
  while (test&set(value)); // while busy
 }
  Release() {
  value = 0;
 }
  • Simple explanation:
    If lock is free, test&set reads 0 and sets value=1, so lock is now busy. It returns 0 so while exits.
    If lock is busy, test&set reads 1 and sets value=1 (no change)
    It returns 1, so while loop continues.
    When we set value = 0, someone else can get lock.
  • Busy-Waiting: thread consumes cycles while waiting
    For multiprocessors: every test&set() is a write, which makes value ping-pong around in cache (using lots of network BW).
    Multiprocessor Spin Locks: test&test&set
    a better solution for multiprocessors:
 int mylock = 0; // Free
  Acquire() {
   do {
    while(mylock);   // Wait until might be free
  } 
  while(test&set(&mylock)); // exit if get lock
  }
  
  Release() {
  mylock = 0;
 }
  • Simple explanation:
    Wait until lock might be free (only reading – stays in cache and means the operation is atomic)
    Then, try to grab lock with test&set
    Repeat if fail to actually get lock
  • Issues with this solution:
    Busy-Waiting: thread still consumes cycles while waiting
    However, busy waiting but it does not impact other processors!
    通过以上讨论,我们需要Higher-level Primitives than Locks

Semaphores

Definition

First defined by Dijkstra in late 60s.
Main synchronization primitive used in original UNIX.
A Semaphore has a non-negative integer value and supports the following two operations:

  • P()/wait(): an atomic operation that waits for semaphore to become positive, then decrements it by 1. Think of this as the wait() operation. P不能将信号量降为负数
  • V()/signal(): an atomic operation that increments the semaphore by 1, waking up a waiting P, if any.

Explanation : Semaphores are a kind of generalized lock. However, the value of lock is treated as bool value as 0 and 1 while the semaphore value is non-negative integer value.

Binary Semaphore
The metaphore is initialled to be 1.(类比lock的话1就是unlocked的状态,因为一般锁初始化状态是unlocked,但有些时候也要初始化为0,比如实现某种特殊调度)
(同时1的话实现mutual exclusion)

Two uses of Semaphores

  1. mutual exclusion(lock)
    The lock function can be realized with a binary semaphore: semaphore subsumes lock.
    Semaphore has an initial value of 1
    P() is called before a critical section
    V() is called after the critical section
semaphore litter_box = 1;
P(litter_box);
// critical section
V(litter_box);
  1. synchronization
    Enforcing some order between threads(实现调度约束,semaphore is usually initialled to 0).

Producer&Consumer with Bounded Buffer

This is a simple but important problem.
Correctness constraints

  • Only one thread can manipulate buffer queue at a time (mutual exclusion)
  • When the buffer is full, producers must wait for consumers to consume a product (scheduling constraint)
  • When the buffer is empty, consumers must wait for producers to put a product (scheduling constraint)

Naturally , we can use a separate metaphore for each of the 3 constraints

semaphore mutex = 1; //for mutual exclusion
semaphore nFreeBuffers = N; // for producer
semaphore nLoadedBuffers = 0; // for consumer

Develop the solution

Producer() {
	P(nFreeBuffers);
	P(mutex);
	// put 1 item in the buffer
	V(mutex);
	V(nLoadedBuffers);
}

Consumer() {
	P(nLoadedBuffers);
	P(mutex);
	// take 1 item from the buffer
	V(mutex);
	V(nFreeBuffers);
}

三点说明:
1.调换两个P位置会引起deadlock
2.调换两个V的位置没有关系
3.mutex是个二进制信号量(mutual exclusion),只有P(mutex)和V(mutex)之间的代码才是critical section,这部分要越小越好。

Synchronization Problems using metaphore

  • 读者写者问题
  • 哲学界进餐问题

Semaphore in UNIX

Managing concurrent access to shared memory. System resources.
Semaphore system calls

  • creation : semget(…)
  • incr/decr/set : semop(…)
  • deletion: semctl(semid, 0, IPC_RMID, 0);

Implementation of Semaphore

How to implement semaphore?

  • Almost exactly like lock.
  • Using spinlock or queue
    What hardware support is needed?
  • Interrupt disable
  • Test-and-set

Implement with interrupt with disable

class semaphore {
	int value;
}

semaphore::semaphore(int i) {
   value = i;
}
semaphore::p() {
	// disable interrupts
	while (value == 0) {
		// enable interrupts
		// disable interrupts
	}
	value --;
	// enable interrupts
}
semaphore::v() {
	// disable interrupts
	value ++;
	// enable interrupts
}

Implement with interrupt with test&set

class semaphore {
	int value;
}

semaphore::semaphore(int i) {
   value = i;
}
semaphore::p() {
	while (test_and_set(guard));
	while (value == 0) {
		// queue the thread
		// guard = 0 and sleep
	}
	value --;
	guard = 0;
}
semaphore::v() {
	while (test_and_set(guard));
   if (anyone waiting) {
	  // wake up one thread
     // put in on ready queue
    } else {
       value ++;
    }
	 guard = 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值