본문 바로가기
Linux

[Linux] semaphore

by Bokoo14 2022. 12. 12.

# 12주차 1206 강의 요약

 

IPC: 프로세스들 사이에 서로 데이터를 주고 받는 행위 또는 그에 대한 방법이나 경로

IPC(Inter-Process Communication) 두 가지 방법

1. message queue 

2. shared memory 


### shminit.c

// shared memory를 하나 만들어서 초기화시키는 프로그램
#include <stdio.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/types.h>

int main(){
	key_t key;
	int shmid;
	int *shmaddr;

	key=100; // 두 프로세스 간의 고유한 key를 공유해야 한다
	shmid=shmget(key,sizeof(int), IPC_CREAT | 0666); // 100이라는 key로 만들어진 shared memory에 쓸거야. 없으면 만든다
	shmaddr = shmat(shmid, 0, 0); // attach
	//printf("shmaddr = %p \n", shmaddr);
    
	*shmaddr = 0; // 공유 메모리에 숫자 0을 저장
    
	shmdt(shmaddr); // 다 쓰면 detach
}

### bash shell

gcc shminit.c -o shminit

./shminit

ipcs -m : shared memory의 정보를 보여줌

 

### shmsum.c

// shminit.c을 실행 후 shmsum.c를 실행하면 공유 메모리이므로 위에서 저장된 값에 접근 가능
#include <stdio.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/types.h>

int main(){
	key_t key;
	int shmid;
	int *shmaddr;
	int i, j;

	key=100;
	shmid=shmget(key,sizeof(int), IPC_CREAT | 0666); //공유 메모리에 값을 씀
	shmaddr = shmat(shmid, 0, 0);
    
	for (i=0; i<10; i++){
		for(j=0; j<10000000; j++){
			usleep(200000); // 0.2초
			(*shmaddr)++; // shminit.c에서 저장된 공유메모리값에 접근하여 ++연산
		}
	}
    
	printf("*shmaddr = %d \n", *shmaddr); // 100000000출력
	shmdt(shmaddr);
}

### bash shell

gcc shmsum.c -o shmsum

./shmsum

ipcs -m

 

공유 메모리는 프로그램이 끝나도 지위지지 않음

shmsum을 실행할 때마다 shmaddr에 저장된 값이 +100000000 더해진 값이 출력

 

sleep: 단위 1sec

usleep: 단위 1micro sec (1/1000000sec)

 

shminit과 shmsum을 동시에 수행하면 원하는 결과값이 나오지 않는다 .. 
왜? 공유 메모리를 두 개의 프로세스가 동시에 접근 ..

cpu가 하나있어도 오류가 발생한다 .. 
문제 발생: cpu가 실행할 때 어셈블리 언어로 바꿔서 사용하게 됨 
x=x+1이 cpu에서는 3가지 명령어로 쪼개서 실행됨
load $1, = x
add $2, $1, 1
store $2, = x

서로 다른 메모리를 동시에 읽고 쓰면 중간에 write가 끼게 되면 race condition이 발생할 수 도 있음(os과목 참고)

하드웨어는 하나인데 어떻게 하면 여러 개의 프로그램을 실행할 수 있을까? -> 공유된 자원을 한 번에 하나만 read, write하자.. -> 동기화 도구(semaphore, mutex lock)

## semaphore

동기화 도구: semaphore, mutex

semaphore란?

- 두 개 이상의 프로세스가 동시에 공유 메모리와 같은 공유 자원을 접근할 때 동기화를 걸어줌

- 공유된 자원의 데이터 혹은 임계영역(Critical Section) 등에 여러 Process 혹은 Thread가 접근하는 것을 막아줌(동기화 대상이 하나 이상)

 

semaphore는 int형 변수

wait(), signal(), init() 사용

 

- init() 

딱 한번 초기화할 수 있음

 

- wait() // 감소

 while(s<=0){block;} s—;

 

- post() (= signal()) // 증가

s++;

 

=> atomic하게 동작하는 것을 보장해준다 (한번에 하나만 실행하도록 보장해줌)


## POSIX Semaphore

 

### semainit.c

#include <stdio.h>
#include <semaphore.h>
#include <sys/types.h>
#include <fcntl.h>

int main(){
	sem_t *sem; //세마포어 데이터타입
	int svalue;
	int ival;

	printf("Enter an integer: ");
	scanf("%d: ", &ival); // 세마포어 초기값 받기
	sem = sem_open("test", O_CREAT, 0666, ival); //새로운 세마포어 만들기 -> 세마포어 이름은 마음대로("test"), 세마포어 원하는 값(ival)으로 초기화

	sem_getvalue(sem, &svalue); // 세마포어의 현재 값을 알 수 있음
	printf("svalue = %d \n", svalue);
}

### bash shell

gcc semainit.c -o semainit -lpthread : 라이브러리를 포함해서 컴파일해야 함

./semainit

 

~/dev/shm 에 "sem.test"라는 세마포어가 파일형태로 생성됨

 

sem = sem_open("test", O_CREAT, 0666, ival); 

test라는 세마포어가 이미 존재하면 새로 만들지 않고, 원래 있던 값을 쓰게 된다

test라는 세마포어가 없다면 초기값을 설정해서 만들 수 있다

 

rm /dev/shm/sem.test

이미 있는 세마포어를 지우고 다시 만들 수 있음

다시 만들때는 원하는 초기값으로 세마포어를 만들 수 있음


### s_wait.c

#include <stdio.h>
#include <semaphore.h>
#include <sys/types.h>
#include <fcntl.h>

int main(){
	sem_t *sem;
	int svalue;
	int ival;

	sem = sem_open("testsem", O_CREAT, 0666, 1); //새로운 세마포어 만들기 -> 마지막 원하는 값으로 초기화
	sem_wait(sem);

	printf("wait is done \n");
}

### s_post.c

#include <stdio.h>
#include <semaphore.h>
#include <sys/types.h>
#include <fcntl.h>

int main(){
	sem_t *sem;
	int svalue;
	int ival;

	sem = sem_open("testsem", O_CREAT, 0666, 1); //새로운 세마포어 만들기 -> 마지막 원하는 값으로 초기화
	sem_post(sem);

	printf("post is done \n");
}

### semwork.c

#include <stdio.h>
#include <semaphore.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <time.h>
#include<unistd.h>

int main(){
	sem_t *sem;
	int work_t, i;

	sem = sem_open("testsem", O_CREAT, 0666, 1); //새로운 세마포어 만들기 -> 1으로 초기화
	srand(time(NULL)); // 난수 생성
	for (i=0; i<10; i++){
		printf("Trying to get a semaphore \n");
		sem_wait(sem); //while(s<=0){block;} s--;
		
		work_t = rand()%5 +1; // 1~5까지 난수 생성
		printf("Working for %d sec \n", work_t);
		sleep(work_t);
		printf("          Done\n\n");

		sem_post(sem); // s++;
		sleep(1);
	}
}

앞 뒤로 sem_wait(sem), sem_post(sem)으로 감싸주면, 여러 프로세스가 동시에 수행될 때 한 번에 하나만 수행되도록 보장해준다.

 

### bash shell 

gcc semwork.c -o semwork -lpthread

./semwork

rm /dev/shm/sem.testsem

 

- 세마포어의 초기값을 0으로 설정하면? sem_wait => while(s<=0){block;} s—; 에 걸려서 모든 프로세스가 무한 block에 걸리게 된다

- 세마포어의 초기값을 2로 설정하면? 3개의 프로세스를 동시에 수행하면 2개의 프로세스가 동시에 수행될 수 있음

- 세마포어의 초기값을 100으로 설정하면? 동시에 100개의 프로세스를 동시에 수행할 수 있음


ipcs -a : 모든 IPC 자원을 조회

ipcs -q : 메시지 큐 자원을 조회

ipcs -m : 공유메모리 자원을 조회

ipcs -s : 세마포어 자원을 조회 

ipcrm -q  [ID] : 해당 메시지 큐 자원을 삭제

ipcrm -m [ID]:  해당 공유메모리 자원을 삭제

ipcrm -s  [ID]:  해당 세마포어 자원을 삭제

 

2개의 세마포어, 1개의 공유변수


## 실습

(1) Shared Memory 이용한 메시지 전달하기 (동기화를 위해서 Semaphore 2개 사용 필요)

  - program A

   + 키보드로 입력받은 문자열을 공유메모리에 write한다. 

   + 프로그램 B가 공유 메모리에 새로운 문자열을 쓰기를 기다린다. 

   + 그리고, 그 문자열을 출력한다. 

   + 이 과정을 "end"를 입력할 때까지 반복한다. 

  - program B

   + program A가 새로운 문자열을 write하기를 기다린다. 

   + 그 문자열을 출력하고, 새로운 문자열을 만든다. ("예: strcat을 사용해서 msg + "이름")

   + 새로 만든 문자열을 공유메모리에 write한다. 

   + 이 과정을 program A가 "end"를 받을 때까지 반복한다. 

 

(2) 제출물 

  - 두 개의 프로그램 소스 코드

  - 실행화면 캡쳐한 이미지 파일

 

### A.c

#include <stdio.h>
#include <semaphore.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <stdlib.h>


int main() {
	char buf[100];
	char buf2[100];

	// A
	sem_t *sem;
	sem = sem_open("AAA", O_CREAT, 0666, 0); 

	int svalue;
	sem_getvalue (sem, &svalue);
	printf("program A svalue=%d \n", svalue); // 값 확인
	
	// B
	sem_t *sem2;
	sem2 = sem_open("BBB", O_CREAT, 0666, 0); 
	
	sem_getvalue (sem2, &svalue);
	printf("program B svalue=%d \n", svalue); // 값 확인

	// 공유메모리 
	key_t key=100;
	int shmid;
	char *shmaddr;
	
	shmid = shmget(key, sizeof(char)*100, IPC_CREAT | 0666);
	shmaddr = shmat(shmid, NULL, 0);

	while(1){
		// program A
		printf("This is Program A \n");
		printf ("Enter a string to send in program A: ");
		gets (buf);
		
        //공유메모리에 쓰기
		strcpy(shmaddr, buf);
		sem_post(sem);

		if (!strcmp(buf, "end")){exit(0);}

       // program B 
		sem_wait(sem2);
		sleep(2);
		printf("This is Program B\n");
		strcpy(buf, shmaddr);	
		printf("Program B string is [%s] \n", buf);
	}
	
	shmdt(shmaddr);
	return 0;
}

### B.c

#include <stdio.h>
#include <semaphore.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <stdlib.h>


int main() {
	char buf[100];
    char buf2[100];

	// A
	sem_t *sem;
	sem = sem_open("AAA", O_CREAT, 0666, 0); 

	int svalue;
	sem_getvalue (sem, &svalue);
	printf("program A svalue=%d \n", svalue); // 값 확인
	
	// B
	sem_t *sem2;
	sem2 = sem_open("BBB", O_CREAT, 0666, 0); 
	
	sem_getvalue (sem2, &svalue);
	printf("program B svalue=%d \n", svalue); // 값 확인

	// 공유메모리 
	key_t key=100;
	int shmid;
	char *shmaddr;
	
	shmid = shmget(key, sizeof(char)*100, IPC_CREAT | 0666);
	shmaddr = shmat(shmid, NULL, 0);
    
    while(1){
        // program A
        sem_wait(sem);
        printf("This is Program A \n");
        
        //공유메모리에서 읽기
        strcpy(buf, shmaddr);
        printf("get from shared memory: [%s] \n", buf);
        
        if (!strcmp(buf, "end")){exit(0);}
        
        sleep(1);
        strcat(buf, "bokyung");
        strcpy(shmaddr, buf);
        printf("Program B string is [%s] \n", buf);
        sem_post(sem2);
	}
    shmdt(shmaddr);
    return 0;
}

이번 시간 : semaphore

다음 시간: mutex lock