340 likes | 667 Vues
Linux System Programming. Lecture #10 – 세마포어 (Semaphore). 세마포어 (Semaphore) (1). 세마포어 정의 : 실행단위 ( 프로세스 또는 쓰레드 ) 간의 동기화 도구 2 개의 원자적 연산 P 와 V 가 정의되어 있는 , 정수값을 가지는 객체 s : 세마포어 P(s) : if ( s > 0 ) then s-- else 현재 프로세스는 대기한다 ; V(s) :
E N D
Linux System Programming Lecture #10 –세마포어(Semaphore)
세마포어(Semaphore) (1) • 세마포어 정의 : • 실행단위(프로세스 또는 쓰레드) 간의 동기화 도구 • 2개의 원자적 연산 P와 V가 정의되어 있는,정수값을 가지는 객체 • s : 세마포어 • P(s) : if ( s > 0 ) then s-- else현재 프로세스는 대기한다; • V(s) : if ( 1개 이상의 프로세스가 대기중 ) then 1개 프로세스만 진행한다else s++; • 일상 생활에서의 ‘신호등’과 같은 동작을 수행 • 철도 교통을 통제하기 위한 깃발신호로부터 유래 Linux System Programming
세마포어(Semaphore) (2) • 세마포어 정의 : • 세마포어 연산 P & V 는 원자적 연산(atomic operation) • 하나의 프로세스가 P 또는 V 연산을 실행하는 도중에는 중지하지 않는다 • 어떤 프로세스가 P 또는 V 연산을 이용하여 세마포어에 접근하는 동안에는 다른 프로세스가 세마포어 값을 변경할 수 없다 • 세마포어의 활용 • 상호 배제(mutual exclusion) 문제 –두 개 이상의 프로세스가 하나의 공유 자원을 접근할 때에 한 순간에 하나의 프로세스만 공유 자원을 접근할 수 있도록 보장함 • 실행 동기화 –두 개 이상의 프로세스간에 실행 순서에 맞추어 실행됨을 보장함 Linux System Programming
세마포어(Semaphore) (3) • 세마포어 종류 : • 이진 세마포어(Binary Semaphore) • 0 또는 1의 정수 값만 가지는 세마포어 • P(s) 연산은 세마포어 s가 1일 때에 0으로 변경 • V(s) 연산은 세마포어 s가 0일 때에 1로 변경 • 하나의 자원에 대한 공유 및 동기화를 지원 • 계수형 세마포어(Counting Semaphore) • 범위에 제한이 없는 정수 값을 가지는 세마포어 • 일반적으로 언급하는 세마포어 • 다수의 공유 자원에 대해 여러 프로세스가 접근할 때에 상호 배제 및 동기화를 지원 Linux System Programming
세마포어(Semaphore) (4) • 세마포어 이용 : • 공유 자원에 대한 상호 배제(Mutual Exclusion) • 여러 개의 프로세스가 하나의 자원을 공유하는 경우, 동시에 여러 프로세스가 자원을 접근하면 예상하지 못하는 문제가 발생함 • 예: 동시에 여러 프로그램이 프린터에 출력을 시도하는 경우 • 해결책 –상호 배제 • 한 순간에 하나의 프로세스만 공유 자원을 접근함을 보장함 • 임계 영역(Critical Section) : 전체 프로그램 중에서 공유자원을 접근하는 프로그램 영역 • 한 순간에 임계 영역을 실행하는 프로세스는 하나만 존재하도록 보장함 • 세마포어를 이용하여 상호 배제를 구현함 • 공유자원의 갯수에 따라 이진 세마포어 또는 계수형 세마포어를 사용 • 공유 자원에 대한 잠금과 풀기(lock & unlock) 기능을 지원 Linux System Programming
프로세스 P1 프로세스 P2 세마포어 s (초기값: 1) P(s) V(s) P(s) V(s) Critical Section Critical Section 공유 자원 세마포어(Semaphore) (5) • 세마포어 이용 : • 공유 자원에 대한 상호 배제(Mutual Exclusion) Linux System Programming
세마포어(Semaphore) (6) • 세마포어 이용 : • 프로세스간의 실행 동기화(Synchronization) • 하나의 프로그램이 여러 개의 프로세스로 이루어지는 경우, 프로세스간의 종속성에 의해 실행 순서가 정해지며, 반드시 실행 순서에 의해 동작하여함 • 예: 하나의 프로세스가 다른 프로세스가 제공하는 데이터를 받아 동작하는 경우 • 해결책 –프로세스 실행 동기화 • 프로세서 P1가 T1 문장을 실행한 후에 프로세스 P2가 T2 문장을 실행하여야 하는 경우 • 프로세스 P2는 T2 문자을 실행하기 전에 프로세스 P1이 T1 문장을 실행하여는지를 검사 • 프로세스 P1이 T1 문장을 실행하였으면 바로 T2 문장을 실행 • 프로세스 P1이 T2 문장을 실행하지 않았으면 실행할 때까지 대기 • 세마포어를 이용하여 동기화를 지원함 • 대개의 경우 이진 세마포어를 사용 Linux System Programming
프로세스 P2 프로세스 P1 세마포어 s (초기값: 0) V(s) P(s) T1: ----- T2: ----- 세마포어(Semaphore) (7) • 세마포어 이용 : • 프로세스간의 실행 동기화(Synchronization) Linux System Programming
세마포어(Semaphore) (8) • 세마포어의 생성: semget 시스템 호출 Linux System Programming
세마포어(Semaphore) (9) • 세마포어의 생성: semget 시스템 호출 • 세마포어 구조체: Linux System Programming
세마포어(Semaphore) (10) • 세마포어의 생성: semget 시스템 호출 • Semget 시스템 호출의 동작 세마포어 구조체 초기화: Linux System Programming
세마포어(Semaphore) (11) • 세마포어의 제어: semctl 시스템 호출 • 세마포어 제어 연산 • 세마포어의 집합 안에 개별적인 세마포어나 모든 세마포어에 대해 세마포어 값을 읽어오거나 새로운 값을 설정 • 세마포어 집합의 상태 정보을 읽어오거나 변경 • 세마포어에서 대기중인 프로세스의 수를 결정 • 마지막으로 세마포어를 연산한 프로세스를 결정 • 세마포어를 제거 • semctl 시스템 호출을 통해 상기의 세마포어 제어 연산을 실행 Linux System Programming
세마포어(Semaphore) (12) • 세마포어의 제어: semctl 시스템 호출 Linux System Programming
세마포어(Semaphore) (13) • 세마포어의 제어: semctl 시스템 호출 • semctl 시스템 호출의 명령어 및 반환값 Linux System Programming
세마포어(Semaphore) (14) • 세마포어의 제어: semctl 시스템 호출 • 세마포어 생성 • 세마포어 집합에서 하나의 세마포어를 초기화 • 세마포어 집합에서 하나의 세마포어 값 읽기 #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #define ANY 0 int semid, rtn; struct semid_ds semds; ushort us[5], init_us[5] = {0, 6, 7, 1, 4}; … semid = semget(key, 5, IPC_CREAT|0666); semctl(semid, 2, SETVAL, 7); // GETCNT, GETZCNT, GETPID도 같은 형태로 사용 semctl(semid, 2, GETVAL, ANY); Linux System Programming
세마포어(Semaphore) (15) • 세마포어의 제어: semctl 시스템 호출 • 세마포어 집합 내의 모든 세마포어의 값을 초기화 • 세마포어 집합 내의 모든 세마포어의 값 읽기 • 세마포어의 소유자 변경 • 세마포어 집합 제거 semctl(semid, ANY, SETALL, init_us); semctl(semid, ANY, GETALL, us); // 접근 허가 모드 변경에도 같은 형태로 사용 semctl(semid, ANY, IPC_STAT, &semds); semds.sem_perm.uid = 51; semctl(semid, ANY, IPC_SET, &ds); semctl(semid, ANY, IPC_RMID, ANY); Linux System Programming
세마포어(Semaphore) (16) • 세마포어의 제어: semop 시스템 호출 • 세마포어의 값을 증가시키거나 감소시키는 연산을 수행 Linux System Programming
세마포어(Semaphore) (17) • 세마포어의 제어: semop 시스템 호출 Linux System Programming
세마포어(Semaphore) (18) • 세마포어의 제어: semop 시스템 호출 Linux System Programming
예제 프로그램 (1) • 예제 10-1 • 세마포어를 이용하여 공유 자원에 대한 접근을 제어하는 프로그램 • 공유 자원은 표준 출력 // file name : ex10-1.c // #include <sys/ipc.h> #include <sys/sem.h> #include <stdio.h> #define DUMMY 0 #define COUNT 4 int main(int argc, char *argv[]) { key_t ipckey; /* modified */ int semid, semget(),semctl(),semop(); int pid,getpid(); int creator,i; static struct sembuf lock = {0,-1,SEM_UNDO}; static struct sembuf unlock = {0, 1,SEM_UNDO}; setbuf(stdout,(char *) NULL); Linux System Programming
예제 프로그램 (2) ipckey = ftok(argv[0],'s'); if((semid = semget(ipckey,1,IPC_CREAT | IPC_EXCL | 0666)) != -1) { creator = 1; } else { if((semid = semget(ipckey,1,0)) == -1) { perror(argv[0]); exit(1); } creator = 0; } if(creator) { if(semctl(semid,0,SETVAL,1) == -1) { perror(argv[0]); exit(2); } } pid = getpid(); for(i=0;i<COUNT;i++){ if(semop(semid,&lock,1) == -1) { perror(argv[0]); exit(3); } printf("\t[%d]locking\t",pid); sleep(1); printf("[%d] unlocking\n",pid); if(semop(semid,&unlock,1) == -1) { perror(argv[0]); exit(4); } } Linux System Programming
예제 프로그램 (3) if(creator) { sleep(5); if(semctl(semid,DUMMY,IPC_RMID,DUMMY) == -1) { perror(argv[0]); exit(5); } } } Linux System Programming
예제 프로그램 (4) • 예제 10-2 • 다음 그림과 세 개의 세마포어를 갖는 세마포어 집합을 이용하여 프린터 자원을 관리하는 프로그램 • 첫번째 세마포어는 두 개의 프린터 모두를 관리하는 계수형 세마포어 • 두번째 및 세번째 세마포어는 각각 프린터1과 프린터2를 관리하는 세마포어 • 프린터는 단말기 장치를 PRINTER1으로, 정규 파일을 PRINTER2로 환경 변수를 정의하여 시뮬레이션한다 Linux System Programming
예제 프로그램 (5) #include <fcntl.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include <stdio.h> #include "ex10-2.h" #define DUMMY 0 #define NUMPR 2 #define ACQUIRE -1 #define RELEASE 1 int main(int argc,char *argv[]) { char *getenv(), *printer[NUMPR], buf[BUFSIZ]; key_t ipckey; /* modified */ int semid, semget(), semctl(), semop(); ushort initial[NUMPR +1]; int i, prntnum, creator, getpid(); int n, fdin, fdout; struct sembuf operation[2]; char errmsg[30]; if((printer[1] = getenv("PRINTER1")) == (char *) NULL || (printer[2] = getenv("PRINTER2")) == (char *) NULL) { printf("missing printer assignment\n"); exit(1); } Linux System Programming
예제 프로그램 (6) if(strncmp(argv[0],"line",4) ==0) prntnum = 1; else if(strncmp(argv[0],"lase",4) ==0) prntnum =2; else prntnum = getpid() % NUMPR +1; ipckey = ftok(argv[0], 's'); if((semid = semget(ipckey,NUMPR +1,IPC_CREAT| IPC_EXCL| 0666)) != -1) { creator = 1; } else { if((semid = semget(ipckey, NUMPR +1, 0666)) == -1) { sprintf(errmsg, "%s - semget", argv[0]); perror(errmsg); exit(2); } } if(creator) { /* initialize semaphore set */ initial[0] = NUMPR; for(i=1; i<= NUMPR; i++) initial[i] = 1; if(semctl(semid, DUMMY, SETALL, initial) == -1) { sprintf(errmsg,"%s -SETALL", argv[0]); perror(errmsg); exit(3); } } operation[1].sem_num = prntnum; operation[1].sem_op = ACQUIRE; operation[1].sem_flg = SEM_UNDO; operation[0].sem_num = 0; operation[0].sem_op = ACQUIRE; operation[0].sem_flg = SEM_UNDO; Linux System Programming
예제 프로그램 (7) if(semop(semid, operation, 2) == -1) { sprintf(errmsg,"%s - ACQUIRE", argv[0]); perror(errmsg); exit(4); } if((fdin = open(argv[1], O_RDONLY)) == -1) { sprintf(errmsg,"%s - %s", argv[0], argv[1]); perror(errmsg); exit(5); } if((fdout = open(printer[prntnum], O_CREAT| O_WRONLY)) == -1) { sprintf(errmsg,"%s - %s", argv[0], printer[prntnum]); perror(errmsg); exit(6); } while((n= read(fdin, buf, BUFSIZ)) > 0) write(fdout, buf, n); operation[1].sem_op = RELEASE; operation[0].sem_op = RELEASE; if(semop(semid, operation, 2) == -1) { sprintf(errmsg,"%s - RELEASE", argv[0]); perror(errmsg); exit(7); } } Linux System Programming
예제 프로그램 (8) • 예제 10-3 • 다음 그림과 같은 생산 라인을 시뮬레이션하는 프로그램 • 각 공정은 다중 프로세스를 생성하고 이들의 생산 순서와 소비 시간을 세마포어와 프로세스 수면(sleep)을 이용하여 시뮬레이션한다 Linux System Programming
예제 프로그램 (9) // // header file name : ex10-3.h // #define NWIDGETS 5 #define PARTA 0 #define PARTB 1 #define PARTC 2 #define SUB1 3 #define HOLDER 0 Linux System Programming
예제 프로그램 (10) // // source file name : ex10-3a.c // #include <signal.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include "ex10-3.h" int semprodn; void endsim(); int main(int argc, char *argv[]) { int widget; int pgrp; char asc_prod_key[20]; static struct sembuf partc_sub1[2] = { { PARTC, -1, SEM_UNDO}, { SUB1, -1, SEM_UNDO} }; if((semprodn=semget(IPC_PRIVATE,4,IPC_CREAT | 0640)) == -1) { printf("Can't get production line semaphore set\n"); exit(1); } Linux System Programming
예제 프로그램 (11) signal(SIGINT, endsim); sprintf(asc_prod_key,"%d", semprodn); if( fork() == 0) execl("ex10-3b","a", asc_prod_key, (char *)0); if( fork() == 0) execl("ex10-3b","b", asc_prod_key, (char *)0); if( fork() == 0) execl("ex10-3b","c", asc_prod_key, (char *)0); if( fork() == 0) execl("ex10-3c","ex10-3c", asc_prod_key, (char *)0); for(widget=1; widget < NWIDGETS; widget++) { semop(semprodn, partc_sub1, 2); printf("%s: ready to make widget #%d\n", argv[0], widget); } endsim(); } void endsim() { semctl(semprodn, 0,IPC_RMID, 0); signal(SIGTERM, SIG_IGN); kill(0, SIGTERM); exit(); } Linux System Programming
예제 프로그램 (12) // // source file name : ex10-3b.c // #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include "ex10-3.h" int main(int argc, char *argv[]) { int semprodn, index; int unit = 0; static struct sembuf parti = { HOLDER, 1, SEM_UNDO }; static int prodtimeabc[3] = {2,3,4}; semprodn = atoi( argv[1] ); index = argv[0][0]- 'a'; /* argv[0] == [abc] */ parti.sem_num = index; while(1) { semop(semprodn, &parti, 1); printf("%s: producing unit #%d\n", argv[0], ++unit); sleep(prodtimeabc[index]); } } Linux System Programming
예제 프로그램 (13) // // source file name : ex10-3c.c // #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include "ex10-3.h" int main(int argc, char *argv[]) { int semprodn; int unit = 0; static struct sembuf part_ab[2] = { {PARTA, -1, SEM_UNDO }, {PARTB, -1, SEM_UNDO } }; static struct sembuf sub1 = { SUB1, 1, SEM_UNDO }; semprodn = atoi( argv[1] ); while(1) { semop(semprodn, part_ab, 2); semop(semprodn, &sub1, 1); printf("%s: producing sub-assembly #%d\n", argv[0], ++unit); } } Linux System Programming