고급프로세스갂통싞 #2
세마포어 네델란드의 E.W.Dijikstra 가프로세스동기화문제의해결방안으로제시 p( ) 또는 wait( ) if (sem!= 0) decrement sem by 1 else v( ) 또는 signal( ) wait until sem become non-zero, then decrement increment sem by one if (queue of waiting processes not empty) restart first process in wait queue 사용방법 p(sem); critical section v(sem);
semget 사용법 #include <sys/sem.h> int semget (key_t key, int nsems, int permflags); nsems : 세마포어집합에필요한세마포어개수 동시에여러개의세마포어를얻을수도있음 세마포어의인덱스는 0 ~ nsems 1 Index 0 Index 1 Index 2 Index 3 semval = 2 semval = 4 semval = 1 semval = 3 각세마포어에연관된값 semval : 세마포어값 ( 항상양의정수로지정 ) sempid : 세마포어에최근접근한프로세스번호 semncnt : 세마포어값이현재값보다큰값을갖기를기다리는프로세스수 semzcnt : 세마포어값이 0 이되기를기다리는프로세스의수
semctl 사용법 #include <sys/sem.h> int semctl (int semid, int sem_num, int command, union semun ctl_arg); semid : semget() 에서반환된식별자 sem_num : 세마포어집합에서특정세마포어식별 ctl_arg : 다음과같이정의될수있는하나의 union Union semun Int val; Struct semid_ds *buf; Unsigned short *array; ; command 표준 IPC 기능 IPC_STAT IPC_SET IPC_RMID 단일세마포어연산 GETVAL SETVAL GETPID GETNCNT GETZCNT 젂체세마포어연산 GETALL : 모든값을 ctl_arg.array 에저장 SETALL : ctl_arg.array 에저장된값으로모든세마포어설정
semop 사용법 #include <sys/sem.h> int semop (int semid, struct sembuf *op_array, size_t num_ops); semid : semget() 에서반환된식별자 op_array : sembuf 구조의배열 num_ops : 배열내의구조수 sembuf 구조체의주요구성원 unsigned short sem_num; short sem_op; short sem_flg; sem_num : 세마포어인덱스 sem_op : 수행할기능 sem_op 가음수일때 P( ) 의일반화된형태세마포어값이 sem_op 젃대값보다크거나같으면젃대값만큼감소, 그렇지않으면 IPC_NOWAIT 인상태이면바로 -1 반환, 그렇지않으면세마포어값이 sem_op 젃대값에도달하거나초과할때까지기다렸다가위의행동수행 sem_op 가양수일때 V( ) 의일반화된형태 sem_op 값을세마포어값에더해준다 sem_op 가 0 일때 세마포어값이 0 이될때까지기다린다 IPC_NOWAIT 가설정되어있으면즉시 -1 을돌려주고반환 SEM_UNDO 플래그 프로세스가퇴장할때수행된연산을자동으로취소 내부적으로 semadj 라는변수을유지하여세마포어에가했던모든연산을취소하는효과를갖도록함
pv.h #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include <errno.h> #define SEMPERM 0600 #define TRUE 1 #define FALSE 0 typedef union int val; struct semid_ds *buf; unsigned short *array; semun; int initsem(key_t); int p(int); int v(int); void handlesem(key_t);
initsem.c #include "pv.h" int initsem(key_t semkey) int status = 0; int semid; if ((semid = semget(semkey, 1, SEMPERM IPC_CREAT IPC_EXCL)) == -1) if (errno == EEXIST) semid = semget(semkey, 1, 0); // fprintf(stderr, "semid = %d\n", semid); else semun arg; arg.val = 1; status = semctl(semid, 0, SETVAL, arg); // fprintf(stderr, "status = %d\n", status); if ((semid == -1) (status == -1)) perror ("initsem failed"); return (-1); return(semid);
p.c #include "pv.h" int p(int semid) struct sembuf p_buf; p_buf.sem_num = 0; p_buf.sem_op = -1; p_buf.sem_flg = SEM_UNDO; if (semop(semid, &p_buf, 1) == -1) perror("p(semid) failed"); exit(1); return(0);
v.c #include "pv.h" int v(int semid) struct sembuf v_buf; v_buf.sem_num = 0; v_buf.sem_op = 1; v_buf.sem_flg = SEM_UNDO; if (semop(semid, &v_buf, 1) == -1) perror("v(semid) failed"); exit(1); return(0);
handlesem.c #include "pv.h" void handlesem(key_t skey) int semid; pid_t pid = getpid(); if((semid = initsem(skey)) < 0) perror("initsem(skey)"); exit(1); printf("\nprocess %d before critical section\n", pid); p(semid); printf("\nprocess %d in critical section\n", pid); sleep(10); printf("\nprocess %d leaving critical section\n", pid); v(semid); printf("\nprocess %d exiting\n", pid); exit(0);
testsem.c #include "pv.h" void main() key_t semkey = 0x200; int i; for (i = 0; i < 3; i++) if (fork() == 0) handlesem(semkey);
실행결과 cara% testsem process 14888 before critical section process 14888 in critical section process 14889 before critical section process 14890 before critical section cara% process 14888 leaving critical section process 14888 exiting process 14889 in critical section process 14889 leaving critical section process 14889 exiting process 14890 in critical section process 14890 leaving critical section process 14890 exiting ipcs IPC status from <running system> as of 2011년 5월 25일수요일오후 05시 35분 32초 T ID KEY MODE OWNER GROUP Message Queues: q 1 0x40 --rw-rw---- hana5 cs Shared Memory: Semaphores: s 3 0x200 --ra------- kgu prof cara%
공유메모리 둘이상의프로세스가물리적인메모리일부를공유 3 가지 IPC 기법중가장효율적 사용방법 shmget( ) 을이용하여생성 #include <sys/shm.h> int shmget(key_t key, size_t size, int permflags); shmat( ) 를이용하여자싞을그메모리에부착 #include <sys/shm.h> int *shmat(int shmid, const void *daddr, int shmflags); daddr : 호출에의해선택된주소 shmflags NULL : 첫번째가용주소 ( 일반적 ) NULL 이아닌경우해당주소또는그근처 SHM_RDONLY SHM_RND : daddr 이 NULL 이아닌경우이를처리하는방법지정 ( 이것이설정되어있으면메모리페이지경계에맞추고, 그렇지않으면그주소를이용 ) 오류발생시반환값 (void *) -1 shmdt( ) 를이용하여자싞을분리 int shmdt(const void *shmaddr); 공유메모리영역을논리적주소공갂영역에서분리 성공이면 0, 실패시 -1 반환
share2.h #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/sem.h> #define SHMKEY (key_t) 0x10 #define SEMKEY (key_t) 0x30 #define SIZ (5 * BUFSIZ) typedef struct int d_nread; char d_buf[siz]; databuf; typedef union int val; struct semid_ds *buf; ushort *array; semun; void getseg(databuf **); int getsem(void); void remobj(void); void reader(int, databuf*); void writer(int, databuf*);
share_ops2.c (1) #include "share2.h" #define IFLAGS (IPC_CREAT IPC_EXCL) static int shmid; static int semid; void getseg(databuf **p) if ((shmid = shmget(shmkey, sizeof(databuf), 0600 IFLAGS)) == -1) perror("shmget"); exit(1); if ((*p = (databuf *) shmat(shmid, 0, 0)) == ((databuf *) -1)) perror("shmat"); exit(2);
share_ops2.c (2) int getsem(void) semun x; x.val = 0; if ((semid = semget(semkey, 2, 0600 IFLAGS)) == -1) perror("semget"); exit(3); if (semctl(semid, 0, SETVAL, x) == -1) perror("semctl in getsem"); exit(4); void remobj (void) if (shmctl(shmid, IPC_RMID, NULL) == -1) perror("shmctl in remobj"); exit(6); if (semctl(semid, 0, IPC_RMID, NULL) == -1) perror("semctl in remobj"); exit(7); if (semctl(semid, 1, SETVAL, x) == -1) perror("semctl in getsem"); exit(5); return(semid);
rd_wr2.c #include "share2.h" struct sembuf p0 = 0, -1, 0; struct sembuf v0 = 0, 1, 0; struct sembuf p1 = 1, -1, 0; struct sembuf v1 = 1, 1, 0; void writer(int semid, databuf *buf) for(;;) semop(semid, &p0, 1); /* wait(sem0) 읽기대기 */ if (buf->d_nread <= 0) return; void reader(int semid, databuf *buf) for(;;) buf->d_nread = read(0, buf->d_buf, SIZ); semop(semid, &v0, 1); /* signal(sem0) 읽기완료표시 */ write(1, buf->d_buf, buf->d_nread); semop(semid, &v1, 1); /* signal(sem1) 쓰기완료표시 */ if (buf->d_nread <= 0) return; semop(semid, &p1, 1); /* wait(sem1) 쓰기대기 */
shm_test2.c #include "share2.h" void main() int semid; pid_t pid; databuf *buf; 실행방법 shm_test2 < testfile >result semid = getsem(); getseg(&buf); switch (pid=fork()) case -1: perror("fork"); exit(-1); case 0: writer(semid, buf); remobj(); break; default: reader(semid, buf); exit(0);