๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ“SUBJECT/๐Ÿ“š์‹œ์Šคํ…œํ”„๋กœ๊ทธ๋ž˜๋ฐ

[์‹œ์Šคํ…œํ”„๋กœ๊ทธ๋ž˜๋ฐ/Linux] 10. POSIX-semaphore

by Yun Je 2020. 5. 23.

1. Semaphore

Semaphore๋Š” 1960๋…„๋Œ€ Dijkstra์— ์˜ํ•ด ์„ค๊ณ„๋œ ์‹œ์Šคํ…œ ์ฝœ์ž…๋‹ˆ๋‹ค. 

mutex์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ(๋˜๋Š” ๋ฉ€ํ‹ฐํ”„๋กœ์„ธ์Šค) ํ™˜๊ฒฝ์—์„œ critical section์˜ ๊ณต์œ  ์ž์›์— ์ ‘๊ทผ์ œ์–ด๋ฅผ ์œ„ํ•ด ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. 

 

semaphore์˜ ๊ธฐ๋ณธ์ ์ธ ํŠน์ง•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. 

  • mutual exclusion(์ƒํ˜ธ ๋ฐฐ์ œ)์„ ์œ„ํ•œ block/wakeup ์•Œ๊ณ ๋ฆฌ์ฆ˜
  • ํ”„๋กœ์„ธ์Šค๊ฐ€ critical section์— ์ ‘๊ทผํ•˜์ง€ ๋ชปํ•œ๋‹ค๋ฉด, ํ”„๋กœ์„ธ์Šค๋Š” block ์ƒํƒœ๊ฐ€ ๋จ(block : Semaphore queue์— ํ•ด๋‹น ํ”„๋กœ์„ธ์Šค๋ฅผ ๋“ฑ๋กํ•˜๊ณ , ํ”„๋กœ์„ธ์Šค๋Š” CPU์—์„œ release๋จ)
  • ํ”„๋กœ์„ธ์Šค๊ฐ€ critical section์„ ์ด์šฉํ•œ ํ›„ ๋น ์ ธ๋‚˜์˜ฌ ๋•Œ, waiting queue์— ์žˆ๋Š” ํ”„๋กœ์„ธ์Šค๋ฅผ ๊นจ์›€
  • CPU time์˜ ์†Œ๋ชจ๊ฐ€ ์—†์Œ (wait queue์— block๊ณผ wait์— ๋Œ€ํ•œ ๋™์ž‘์„ ์ง€์›)
  • waiting queue๊ฐ€ ํ•„์ˆ˜์ ์ž„

 

2. Counting Semaphore

Counting Semaphore์€ critical section์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ํ”„๋กœ์„ธ์Šค์˜ ๊ฐœ์ˆ˜๋ฅผ n๊ฐœ๋กœ ์ œํ•œํ•ฉ๋‹ˆ๋‹ค. 

 

class Semaphore{
private:
	int value;	//ํ˜„์žฌ ์‚ฌ์šฉ๊ฐ€๋Šฅํ•œ Semaphore์˜ ์ˆ˜
	PCB* queue;	//Semaphore๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๊ณ  ํ•˜๋Š” processes
};

Semaphore::Semaphore(n){
	value = n;	//critical section์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ํ”„๋กœ์„ธ์Šค์˜ ๊ฐœ์ˆ˜ n๊ฐœ๋กœ ์ œํ•œ
}

Semaphore::wait(){
	//originally name: "P" operation
	value--;
	if(value>0){
		block the calling process;
		add it to the wait queue of this semaphore;
	}
}

Semaphore::signal(){
	//originally name: "V" operation
	value++;
	if(value<=0){
		remove the first process from the wait queue;
		add it to the scheduling queue;
	}
}

 

  • wait() : value>=0 ์ผ ๊ฒฝ์šฐ ๋ฐ”๋กœ semaphore๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Œ -> critical section ์ ‘๊ทผ ๊ฐ€๋Šฅ
  • signal() : ๋ฐ›์•˜๋˜ semaphore์„ ๋ฐ˜ํ™˜ํ•˜๊ณ  value๊ฐ’์„ ์ฆ๊ฐ€์‹œํ‚ด. value<=0์ผ ๊ฒฝ์šฐ, wait queue์— ๊ธฐ๋‹ค๋ฆฌ๋Š” ํ”„๋กœ์„ธ์Šค๊ฐ€ ์žˆ๋‹ค๋ฉด wait queue์— ๊ฐ€์žฅ ์ฒซ ํ”„๋กœ์„ธ์Šค๋ฅผ ๊นจ์šฐ๊ณ  scheduling ํ์— ๋„˜๊ฒจ์„œ semaphore ์–ป์–ด์„œ ์ž‘์—… ์‹คํ–‰

 

3. Binary Semaphore

Binary Semaphore์€ n=1์ธ Counting semaphore ์ž…๋‹ˆ๋‹ค. 

 

class Semaphore{
private:
	int value;
	PCB* queue;
};

Semaphore::Semaphore(1){
	value = 1;
}

Semaphore::wait(){
	//originally name: "P" operation
	if(value==1)
		value = 0;
	else {
		//block the calling process;
		//add it to the wait queue of this semaphore;
	}
}

Semaphore::signal(){
	//originally name: "V" operation-
	if(queue is not empty){
		//remove the first process from the wait queue;
		//add it to the scheduling queue;
	}
	else
		value = 1;
}

 

  • wait()
    • value == 1 : ์•„๋ฌด๋„ semaphore๋ฅผ ์–ป๊ณ  ์žˆ์ง€ ์•Š์Œ -> ์ž์‹ ์ด value-- ํ•˜๊ณ  semaphore ๊ฐ’์„ ์–ป์–ด ๋‹ค์Œ ์ž‘์—…์„ ์ˆ˜ํ–‰
    • value == 0 : ๋‹ค๋ฅธ ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ด๋ฏธ semaphore ์ ์œ  -> ํ•ด๋‹น ํ”„๋กœ์„ธ์Šค block
  • signal() : waiting queue๊ฐ€ ๋น„์–ด์žˆ์ง€ ์•Š๋‹ค๋ฉด ํ์˜ ๊ฐ€์žฅ ์ฒซ ๋ฒˆ์งธ ํ”„๋กœ์„ธ์Šค๋ฅผ ๋ฐ˜ํ™˜

 

4. Mutex Exclusion by Semaphore

Mutex Exclusion(์ƒํ˜ธ๋ฐฐ์ œ)๋Š” binary/counting semaphore๋ฅผ ์ด์šฉํ•ฉ๋‹ˆ๋‹ค. 

 

binary semaphore๋Š” mutex์™€ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. (lock์„ ์–ป์€ ์Šค๋ ˆ๋“œ ํ•˜๋‚˜๋งŒ ๊ณต์œ  ์ž์› ์ ‘๊ทผ ๊ฐ€๋Šฅ)

Semaphore S(1);   //global variable for mutual exclusion, initialized with 1

์Šค๋ ˆ๋“œ/ํ”„๋กœ์„ธ์Šค๊ฐ€ critical section์— ์ ‘๊ทผํ•˜๋ ค๊ณ  ํ•  ๋•Œ ๋จผ์ € S.wait()์„ ์ด์šฉํ•ด semaphroe์„ ์–ป์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ณต์œ ์ž์› ์‚ฌ์šฉ ์ดํ›„ S.signal()์„ ์ด์šฉํ•ด semaphore์„ ๋ฐ˜ํ™˜ํ•ด ์ฃผ๋ฉด wait queue์—์„œ ๊ธฐ๋‹ค๋ฆฌ๊ณ  ์žˆ๋˜ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ/ํ”„๋กœ์„ธ์Šค๊ฐ€ semaphore์„ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 

 

5. Resource Allocation by a Counting Semaphore

n๊ฐœ์˜ ํ”„๋กœ์„ธ์Šค(์Šค๋ ˆ๋“œ)๊ฐ€ ๋™์‹œ์— semaphore์„ ์–ป์–ด์„œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒฝ์šฐ, ์ฆ‰ ๋™์‹œ์— ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” resource๊ฐ€ ์—ฌ๋Ÿฌ๊ฐœ ์žˆ์„ ๊ฒฝ์šฐ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. 

Semaphore Printer(3);   //in case that the system has 3 identical printers

์˜ˆ๋ฅผ๋“ค์–ด 3๊ฐœ์˜ Printer๋ฅผ ๋™์‹œ์— ์‚ฌ์šฉํ•˜๋ ค๊ณ  ํ•œ๋‹ค๋ฉด Semaphore์˜ ๊ฐœ์ˆ˜๋ฅผ 3์œผ๋กœ ์ง€์ •ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. 

 

6. Synchronization with a Semaphore

ํ”„๋กœ์„ธ์Šค๋“ค ๊ฐ„์— ๋™๊ธฐํ™”๋ฅผ ์œ„ํ•œ ๋ชฉ์ ์œผ๋กœ Semaphore๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ ๋™๊ธฐํ™”๋ž€, ํ”„๋กœ์„ธ์Šค๋“ค ๊ฐ„์— ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ๋•Œ ๋™์ผํ•˜๊ฒŒ ์–ด๋–ค ํ•œ ๊ตฌ๊ฐ„๊นŒ์ง€ ๋ชจ๋“  ํ”„๋กœ์„ธ์Šค๊ฐ€ ๋„๋‹ฌํ•˜๋„๋ก ๊ธฐ๋‹ค๋ฆฌ๊ณ , ํ”„๋กœ์„ธ์Šค๊ฐ€ ๋ชจ๋‘ ๋„๋‹ฌํ•˜๋ฉด ๋™์‹œ์— ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜์—ฌ ํ”„๋กœ๊ทธ๋žจ์˜ ํ๋ฆ„์„ ๋งž์ถ”๊ฒŒ ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. 

Semaphore Sync(0);   //global ver. with the initial value of 0

์ด๋Š” semaphore์˜ ์ดˆ๊ธฐ๊ฐ’์„ 0์œผ๋กœ ์„ค์ •ํ•ด์คŒ์œผ์จ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.

semaphore์˜ ๊ฐ’์ด 0์ด๋ฉด ์ดˆ๊ธฐ์— ์•„๋ฌด๋„ semaphore์„ ์–ป์„ ์ˆ˜ ์—†์–ด signal์„ ๋งŒ๋‚˜๊ธฐ ์ „๊นŒ์ง€ wait queue์—์„œ ๊ธฐ๋‹ค๋ฆฌ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. 

์˜ˆ๋ฅผ ๋“ค์–ด P1์ด P0๋ณด๋‹ค wait-point์— ๋” ์ผ์ฐ ๋„์ฐฉํ•˜๊ฒŒ waitํ•  ๊ฒฝ์šฐ P0๋Š” wait์—์„œ block๋œ ์ƒํƒœ์ด๋ฏ€๋กœ P0์—์„œ signal์ด ์˜ฌ ๋•Œ ๊นŒ์ง€ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ด ์ฃผ๋‹ค๊ฐ€ signal์— ๋„์ฐฉํ•˜๋ฉด P1 ํ”„๋กœ์„ธ์Šค๋ฅผ ํ•ด์ œ์‹œ์ผœ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 

๋”ฐ๋ผ์„œ P1์˜ wait ๋‹ค์Œ์˜ ์ž‘์—…๋“ค์€ P0 signal ์ „์˜ ์ž‘์—…๋“ค๋ณด๋‹ค ๋จผ์ € ์ˆ˜ํ–‰๋  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

 

8. POSIX SEMAPHORES

semaphore์€ ๋ฐ˜๋“œ์‹œ pthread ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ์—ฐ๊ฒฐ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. 

 

Semaphore initialization

#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);

 

  •  parameters
    • sem : semaphore ๊ฐ์ฒด
    • pshared
      • 0 : ํ˜„์žฌ ํ”„๋กœ์„ธ์Šค ๋‚ด์—์„œ๋งŒ ์‚ฌ์šฉ๋จ
      • 0์ด ์•„๋‹˜ : ์—ฌ๋Ÿฌ ํ”„๋กœ์„ธ์Šค๊ฐ„ ๊ณต์œ ๋จ
    • value : semaphore์˜ ์ดˆ๊ธฐ ๊ฐ’

Semaphore wait operation

#include <semaphore.h>
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);

 

  •  sem_wait()
    • ์Šค๋ ˆ๋“œ๊ฐ€ semaphore์„ ์–ป์„ ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆผ (suspend)
    • semaphore์„ ์–ป์œผ๋ฉด ๋‚ด๋ถ€์ ์œผ๋กœ semaphore count๊ฐ€ ์ž๋™๊ฐ์†Œ
  • sem_trywait()
    • non-blocking
    • ๋งŒ์•ฝ ํ˜„์žฌ semaphore๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค๋ฉด(sem๊ฐ’์ด 0๋ณด๋‹ค ํฌ๋ฉด) semaphore์„ ์–ป๊ณ  ๋ฐ˜ํ™˜
    • semaphore์„ ์–ป์„ ์ˆ˜ ์—†๋‹ค๋ฉด ์ฆ‰์‹œ๋ฐ˜ํ™˜๋˜์ง€๋งŒ ๋‹ค์‹œ ์„ธ๋งˆํฌ์–ด๋ฅผ ์–ป์œผ๋ผ๋Š” error๋ฅผ ๋ฐ˜ํ™˜ (EAGAIN)

Semaphore post(signal) operations

#include <sekaphore.h>
int sem_post(sem_t* sem);
int sem_getvalue(sem_t *sem, int* sval);

 

  •  POSIX์—์„œ semaphore์˜ signal operation์˜ ์ด๋ฆ„์€ post
  • sem_post()
    • ์ž๋™์œผ๋กœ semaphore์˜ count๊ฐ’์„ ์ฆ๊ฐ€์‹œํ‚ด
    • ์ด ํ•จ์ˆ˜๋Š” ์ ˆ๋Œ€ block๋˜์ง€ ์•Š์Œ. ๋น„๋™๊ธฐ signal handler์— ์˜ํ•ด ์•ˆ์ „ํ•˜๊ฒŒ ์‚ฌ์šฉ๋จ
  • sem_getvalue()
    • ํ˜„์žฌ semaphore๊ฐ’์ด ์–ผ๋งŒ์ง€ ์•Œ์•„๋ƒ„

Semaphore destruction

#include <semaphore.h>
int sem_destroy(sem_t* sem);

 

  • sem์— ์˜ํ•ด semaphore๊ฐ€ destroy๋จ
    • ํ•ด๋‹น semaphore๊ฐ€ ์‚ฌ์šฉํ–ˆ๋˜ resource๋ฅผ ํ•ด์ œ
    • waiting์ค‘์ธ ์Šค๋ ˆ๋“œ๊ฐ€ ์—†์–ด์•ผ ํ•จ

 

9. Example: Mutual Exclusion using Semaphore

<semaphore.c>

#include <semaphore.h>
int cnt = 0;	//๊ณต์œ ์ž์›(shared variable)
static sem_t hsem;	//semaphore ๊ฐ์ฒด ๋ณ€์ˆ˜

int main(int argc, char *argv[]){
	pthread_t thread1;	//main์—์„œ๋Š” 2๊ฐœ์˜ ์Šค๋ ˆ๋“œ(thread1, thread2) ์„ ์–ธ. 
	pthread_t thread2;
	
	if(sem_init(&hsem, 0, 1) < 0){	//์‚ฌ์šฉํ•˜๋ ค๊ณ  ํ•˜๋Š” ์„ธ๋งˆํฌ์–ด์— ๋Œ€ํ•ด ์ดˆ๊ธฐํ™”, local ํ”„๋กœ์„ธ์Šค์—์„œ๋งŒ semaphre์‚ฌ์šฉ, binary semaphore
		fprintf(stderr, "Semaphore Initialization Error\n");
		return 1;
	}
	pthread_create(&thread1, NULL, Thread1, NULL);
	pthread_create(&thread2, NULL, Thread2, NULL);
	pthread_join(thread1, NULL);
	pthread_join(thread2, NULL);
	printf("%d\n", cnt);
	sem_destroy(&hsem);
	
	return 0;
}

void* Thread1(void *arg){
	int i, tmp;
    for(i=0; i<1000; i++){
    	sem_wait(&hsem);
        tmp = cnt;
        usleep(1000);
        cnt = tmp+1;
        sem_post(&hsem);
    }
    printf("Thread1 End\n");
    return NULL;
}

void* Thread2(void *arg){
	int i, tmp;
    for(i=0; i<1000; i++){
    	sem_wait(&hsem);
        tmp = cnt;
        usleep(1000);
        cnt = tmp+1;
        sem_post(&hsem);
    }
    printf("Thread2 End\n");
    return NULL;
}

 

๋Œ“๊ธ€