date: 2024-04-11
title: 06-Process Synchronization
status: DONE
author:
- AllenYGY
tags:
- Lec6
- OS
- NOTE
created: 2024-04-11T16:09
updated: 2024-06-01T12:42
publish: True
Process Synchronization
Processes can execute concurrently 进程并发地执行
进程具有异步性。
异步性:各并发执行的进程以及各自独立的、不可预知的速度向前推进。
To solve this problem, we need to make processes access the same data mutually exclusive 令进程互斥地访问相同数据来解决数据不一致的问题
Race condition:
Critical section problem is to design a protocol that the processes can use to cooperate.
一个时间段内只允许一个进程使用的资源称为临界资源。
临界资源只能互斥的访问。
对临界资源的互斥访问,可以在逻辑上分成四部分:
do {
entry section
critical section
exit section
reminder section
}
turn’s value is initialized to be either i or j
do{
while(turn==j); // Entry section
critical section; // turn is i
turn = j; // Exit section
reminder section;
} while(true);
do{
while(turn==i); // Entry section
critical section; // turn is i
turn = i; // Exit section
reminder section;
} while(true);
Can't Satisfy Progress 无法实现,空闲让进
bool flag[2];
flag[0]=false;
flag[1]=false;
// For P0 Process
while(flag[1]);
flag[0] = true;
critical section;
flag[0] = false;
reminder section;
// For P0 Process
while(flag[0]);
flag[1] = true;
critical section;
flag[1] = false;
reminder section;
先上锁后检查以解决 双标志先检查 的问题
在进入临界区前先标志自己需要进入临界区,然后检查其他进程是否进入临界区
存在问题:当标记时发生进程切换,多个进程都需要进入临界区---导致死锁
违反空闲让进,有限等待
bool flag[2];
flag[0]=false;
flag[1]=false;
// For P0 Process
flag[0] = true;
while(flag[1]);
critical section;
flag[0] = false;
reminder section;
// For P0 Process
flag[1] = true;
while(flag[0]);
critical section;
flag[1] = false;
reminder section;
It is a classic software-based solution to the critical-section problem
Solution for two processes by using two variables:
int turn; // indicates whose turn it is to enter the critical section. 表示谦让 最后谦让的无法执行
boolean flag[2] // indicate if a process is ready to enter the critical section. 表达意愿
do{
flag[i] = true; //ready
turn = j; //allow pj to enter
while(flag[j] && turn == j);
critical section
falg[i] = false; //exit
reminder section
} while(true);
do{
flag[i] = true; //ready
turn = j; //allow pj to enter
while(flag[j] && turn == j);
critical section
falg[i] = false; //exit
reminder section
} while(true);
Software-based solutions are not guaranteed to work on modern computer architecture
Many systems provide hardware support for implementing the critical section code.
Modern machines provide special atomic hardware instructions
Atomic = non-interruptible,the atomic hardware instruction will do the following work
Uniprocessors - could disable interrupts 单处理器 禁止中断 实现
Currently running code would execute without preemption
Advantage:
Disadvantage:
Use the idea of locking
do {
acquire lock; // Entry section
critical section;
release lock; // Exit Section
reminder section;
}while(true);
Definition: TS 指令由硬件实现的,只能一气呵成。
bool test_and_set(bool *target); // Do nothing just wait;
/*Critical section*/
bool rv = *target;
*target = true;
return rv;
do {
while(test_and_set(&lock));
/*Critical section*/
lock = false;
/*Reminder Section*/
} while(true);
rv
is FALSE, means lock is FALSE (available), target’s new value is TRUE rv
is TRUE, means lock is TRUE (locked)Advantage:
Disadvantage:
Exchange Instruction
/XCHG Instruction
Definition: TS 指令由硬件实现的,只能一气呵成。
*value
(the lock) to the value of the passed parameter new_value but only if *value
== expected. That is, the swap takes place only under this condition.Returns as result the original value of the lock. Similar to test_and_set but with an integer lock and an extra condition.
int compare_and_swap (int *value, int expected, int new_value){
int temp = *value;
if(*value==expected)
*value=new_value;
return temp;
}
Lock: 上锁
bool
while(test_and_set(&lock));
lock = false;
Previous hardware-based solutions are complicated and generally inaccessible to application programmers
OS designers build high level
software tools to solve critical section problem
acquire (){
while(!available);
available = false;
}
release (){
available = true;
}
This solution still requires busy waiting This lock therefore is called a spin lock
需忙等,进程时间片用完才下处理机,违反让权等待
一般用于多处理器,一个核忙等,其他核照常工作,并快速释放临界区
不太适用于单处理机系统、忙等的过程中不可能解锁
Semaphore is a synchronization tool more sophisticated than mutex locks
信号量其实就是一个变量(可以是一个整数,也可以是更复杂的记录型变量),可以用一个信号量来表示系统中某种资源的数量
原语是一种特殊的程序段,其执行只能一气呵成,不可被中断。原语是由关中断/开中断指令实现的。 软件解决方案的主要问题是由 “进入区的各种操作无法一气呵成”,因此如果能把进入区、退出区的操作都用“原语”实现,使这些操作能“一气呵成”就能避免问题。
proberen
和verhogen
)。POSIX Named Semaphore Normally used among different processes
POSIX Named Semaphore Normally used among different threads within a process
Semaphore S: an integer variable S can only be accessed via two atomic operations
int S=1;
//acquire lock
void wait(int S){
while(S<=0){
S-=1;
}
}
//release lock
void signal(int S){
S+=1;
}
typedef struct{
int value;
struct process* L;
} semaphore;
// acquire lock
void wait(semaphore S){
S.value--;
if(S.value < 0){
block(S.L); //阻塞该进程
}
}
//release lock
void signal(semaphore S){
S.value++;
if(s.value<=0){
wakeup(S.L); //唤醒进程为就绪态
}
}
信号量机制实现进程互斥、同步、前驱
S
, 如果资源不够就阻塞等待
S
, 如果有进程在等待该资源,则唤醒一个进程
信号量机制实现进程互斥
semaphore mutex=1;
P1(){
P(mutex);
...;
V(mutex);
}
P2(){
P(mutex);
...;
V(mutex);
}
信号量机制实现进程同步
S
,初始为0前操作
之后执行 V(S)
后操作
之前执行 P(S)
前V后P
semaphore S=0;
P1(){
...;
V(S);
...;
}
P2(){
P(S);
...;
...;
}
保证了只能先执行P1()
再执行 P2()
信号量机制实现进程前驱
生产者消费者问题
semaphore mutex = 1; // 互斥信号量,实现对缓冲区的互斥访问
semaphore empty = n; // 同步信号量,表示空闲缓冲区的数量
semaphore full = 0; // 同步信号量,表示产品的数量,也即非空缓冲区的数量
producer(){
while(1){
...;
P(empty);//消耗一个空闲缓冲区
P(mutex);
...;
V(mutex);
V(full); //增加一个产品
}
}
consumer(){
while(1){
P(full); //消耗一个产品
P(mutex);
...;
V(mutex);
V(empty); //增加一个空闲区
}
}
吸烟者问题
semaphore offer1 = 0;
semaphore offer2 = 0;
semaphore offer3 = 0;
semaphore finish = 0;
int i = 0;
provider(){
while(1){
if(i==0){
...;// put Combination-1
V(offer1);
} else if(i==2){
...;// put Combination-2
V(offer2);
} else if(i==3){
...;// put Combination-3
V(offer3);
}
i=(i+1)%3;
P(finish);
}
}
smoker1(){
while(1){
P(offer1);
...;// Get Combination-1
V(finish);
}
}
smoker2(){
while(1){
P(offer2);
...;// Get Combination-2
V(finish);
}
}
smoker3(){
while(1){
P(offer3);
...;// Get Combination-3
V(finish);
}
}
读者写者问题
有读者和写者两组并发进程,共享一个文件,当两个或两个以上的读进程同时访问共享数据时不会产生副作用,但若某个写进程和其他进程(读进程或写进程)同时访问共享数据时则可能导致数据不一致的错误。
互斥关系:读进程-写进程,写进程-写进程
semaphore rw=1; // 用于实现对共享文件的互斥访问
int count=0; // 记录当前有几个读进程在访问文件
semaphore mutex; // 用于保证对count变量的互斥访问
writer(){
while(1){
P(rw);
...;
V(rw);
}
}
while(1){
P(mutex); // 各读进程互斥访问count
if(count==0) // 第一个读进程关门
P(rw);
count++;
V(mutex);
P(mutex);
count--;
if(count==0) // 最后一个读进程开门
V(rw);
V(mutex);
}
这样的实现存在问题:
如果一直有读进程进入那么写进程将会 starvation
需要实现读写公平的算法
semaphore rw = 1; // 用于实现对共享文件的互斥访问
int count = 0; // 记录当前有几个读进程在访问文件
semaphore mutex; // 用于保证对count变量的互斥访问
semaphore w = 1; //实现读写公平 可以理解成排队
writer(){
while(1){
P(w); //谁先抢到 谁先排队
P(rw);
...;
V(rw);
V(w);
}
}
reader(){
while(1){
P(w);
P(mutex); // 各读进程互斥访问count
if(count==0) // 第一个读进程关门
P(rw);
count++;
V(mutex);
V(w);
P(mutex);
count--;
if(count==0) // 最后一个读进程开门
V(rw);
V(mutex);
}
}
P(w)
, 直到读者1 V(w)
P(w)
, 直到写者1 V(w)
P(w)
, 直到写者1 V(w)
总的来说,新增的 w
信号量实现了一个排队的功能,读者和写者都可以排队
semaphore chopstick[5]={1,1,1,1,1};
Pi (){ //i 号哲学家的进程
while(1){
P(chopstick[i]); //Take Left chopstick
P(chopstick[(i+1)%5]); //Take Right chopstick
...;
V(chopstick[i]); //Put Left chopstick
V(chopstick[(i+1)%5]); //Put Right chopstick
...;
}
}
如果每个哲学家同时拿起左边的筷子, 将导致死锁
新定义一个等于4的信号量
更准确的说法应该是:各哲学家拿筷子这件事必须互斥的执行。
semaphore chopstick[5]={1,1,1,1,1};
semaphore mutex = 1; //互斥地取筷子
Pi (){ //i 号哲学家的进程
while(1){
P(mutex);
P(chopstick[i]); //Take Left chopstick
P(chopstick[(i+1)%5]); //Take Right chopstick
V(mutex);
...;
V(chopstick[i]); //Put Left chopstick
V(chopstick[(i+1)%5]); //Put Right chopstick
...;
}
}
A process may never be removed from the semaphore queue in which it is suspended
Process holds a lock needed by higher-priority process
This situation shows how a lower-priority task (in this case
这种情况显示了一个低优先级任务(在这个例子中是
管程
monitor monitor-name { // shared variable declarations
procedure P1 (...) {...}
procedure Pn (...) {...}
Initialization code (...) {...}
}