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: TrueProcess 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后Psemaphore 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 (...) {...}
}