W(s) while (s<=0) { //do nothing } s=s-1; P(s) s=s+1;위 알고리즘으로부터 세마포어를 기다리는 것은 세마포어 카운터를 1만큼 감소시키는 것 이외 아무것도 아님을 쉽게 알 수 있다. 세마포어를 사용하는 것은 이와 정확히 반대로 세마포어 카운터를 1만큼 증가시킨다.
struct semaphore { atomic_t count; int sleepers; wait_queue_head_t wait; } struct rw_semaphore { _s32 activity spinlock_t wait_lock; struct list_head wait_list; }이 구조는 최근 커널에서 수정이 진행 중이며 아래에서 보는 것처럼 추가된 멤버 변수를 포함하고 있다.
struct rw_semaphore { signed long count; #define RWSEM_UNLOCKED_VALUE 0x00000000 #define RWSEM_ACTIVE_BIAS 0x00000001 #define RWSEM_ACTIVE_MASK 0x0000ffff #define RWSEM_WAITING_BIAS (-0x00010000) #define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS #define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS) spinlock_t wait_lock; struct list_head wait_list; #if RWSEM_DEBUG int debug; #endif }커널 레벨에서 세마포어 기능을 구현한 기본함수는 /asm/semaphore.h와 /asm/rwsem.h에서 찾을 수 있다.
int semget(key__t key, int nsems, int semflag);key :
{ int semid; semid=semget(IPC_PRIVATE, 1,IPC_CREAT); if (semid<0) { perror("Semaphore creation failed Reason:"); } }[주의] 실제로 IPC_PRIVAE는 ((__key_t)0)으로 정의된다. 이 부분은 ipc.h에서 찾을 수 있다.
int semc시 (int semid, int semnum, int 층, ...);semid :
union semun { int val; struct semid_ds *buf; ushort *array; } arg;아래의 코드는 세마포어 semid로 구분되는 집합의 첫 번째 세마포어를 1로 설정한다.
{ semun init; init.val = 1; int i = semctl(semid, 1, SETVAL, init); }세마포어가 생성되고 초기화 되자마자 사용자는 이 세마포어 집합에 대해 동작을 수행할 수 있는 준비가 되어 있다. 다시 한번 강조하지만 세마포어 locking이나 unlocking같은 동작을 수행하는데 사용되는 인터페이스는 직접적이지 않다. 세마포어에 대해 원하는 동작을 수행 할 함수를 호출하기 전에 일정량의 명령어를 수행해야 한다.
int semop(int semid, struct sembuf *sops, size_t nsops);semid :
struct sembuf{ ushort sem_num; /*집합내에서 세마포어를 구분한다 */ short sem_op; /* +, -, 0의 값을 가질 수 있다 */ short sem_flag; /*IPC_NOWAIT, SEM_UNDO값을 가질 수 있다 */ };nsops : 사용자가 전달하는 sembuf의 수를 결정한다. nsops인자는 동작이 한 번에 한 묶음의 세마포어에 대해 수행될 필요가 있는 경우에 제공된다.
{ int ret = semctl(semid,0,IPC_RMID); //집합에서 0번째 세마포어를 제거 }IPC_RMID플래그는 semid로 구분되는 세마포어에 대해 수행될 필요가 있는 동작 유형을 나타낸다. 아래의 동작 예가 이 개념을 사용하고 있다.
File: sysvsem_demo.c #include[주의] System V 세마포어는 유저에 의해 제어되므로 세마포어에 대한 허가권이 개발자에게 문제일 수 있다. 이 정보를 유지하는 구조체는 struct semid_ds이다.#include #include #include //세마포어 초기화를 위해 유저가 정의한 semun을 생성한다. void *Thread1(void *arg) { int semid; semid = (int)arg; //세마포어에 동작을 수행하기위해 sembuf 정의를 위해 가장 먼저 필요 struct sembuf op1,op2; //0번째 세마포어에 대한 동작 op1.sem_num = 0; //0번째 세마포어를 나타냄 op1.sem_op = -1; //lock을 위해 세마포어 카운트 감소시킴 op1.sem_flg = 0; //세마포어에 대해 lock을 얻을때 까지 기다림 //1번째 세마포어에 대한 동작 op2.sem_num = 1; //0번째 세마포어를 나타냄 op2.sem_op = -1; //lock을 위해 세마포어 카운트 감소시킴 op2.sem_flg = 0; //세마포어에 대해 lock을 얻을때 까지 기다림 //0번째 세마포어 locking if (semop(semid,&op1,1) == -1) { perror("Thread1:semop failure Reason:"); exit(-1); } else fprintf(stderr,"Thread1:Successfully locked 0th semaphoren"); //1번째 세마포어 locking if (semop(semid,&op2,1) == -1) { perror("Thread1:semop failure Reason:"); exit(-1); } else fprintf(stderr,"Thread1:Successfully locked 1th semaphoren"); //0번째 세마포어 릴리즈 op1.sem_num = 0; //0번째 세마포어를 나타냄 op1.sem_op = 1; //lock을 위해 세마포어 카운트 감소시킴 op1.sem_flg = 0; //세마포어에 대해 lock을 얻을때 까지 기다림 if (semop(semid,&op1,1) == -1) { perror("Thread1:semop failure Reason:"); exit(-1); } else fprintf(stderr,"Thread1:Successfully unlocked 0th semaphoren"); //1번째 세마포어 릴리즈 op2.sem_num = 1; //0번째 세마포어를 나타냄 op2.sem_op = 1; //lock을 위해 세마포어 카운트 감소시킴 op2.sem_flg = 0; //세마포어에 대해 lock을 얻을때 까지 기다림 if (semop(semid,&op2,1) == -1) { perror("Thread1:semop failure Reason:"); exit(-1); } else fprintf(stderr,"Thread1:Successfully unlocked 1th semaphoren"); } void *Thread2(void *arg) { int semid; semid = (int)arg; //in order to perform the operations on semaphore // first need to define the sembuf object struct sembuf op1,op2; //operation for 0th semaphore op1.sem_num = 0; //0번째 세마포어를 나타냄 op1.sem_op = -1; //lock을 위해 세마포어 카운트 감소시킴 op1.sem_flg = 0; //세마포어에 대해 lock을 얻을때 까지 기다림 //operation for 1th semaphore op2.sem_num = 1; //0번째 세마포어를 나타냄 op2.sem_op = -1; //lock을 위해 세마포어 카운트 감소시킴 op2.sem_flg = 0; //세마포어에 대해 lock을 얻을때 까지 기다림 //0번째 세마포어 locking if (semop(semid,&op1,1) == -1) { perror("Reason:"); exit(-1); } else fprintf(stderr,"Thread2:Successfully locked 0th semaphoren"); //1번째 세마포어 locking if (semop(semid,&op2,1) == -1) { perror("Reason:"); exit(-1); } else fprintf(stderr,"Thread2:Successfully locked 1th semaphoren"); //0번째 세마포어 릴리즈 op1.sem_num = 0; //0번째 세마포어를 나타냄 op1.sem_op = 1; //lock을 위해 세마포어 카운트 감소시킴 op1.sem_flg = 0; //세마포어에 대해 lock을 얻을때 까지 기다림 if (semop(semid,&op1,1) == -1) { perror("Reason:"); exit(-1); } else fprintf(stderr,"Thread2:Successfully unlocked 0th semaphoren"); //1번째 세마포어 릴리즈 op2.sem_num = 1; //0번째 세마포어를 나타냄 op2.sem_op = 1; //lock을 위해 세마포어 카운트 감소시킴 op2.sem_flg = 0; //세마포어에 대해 lock을 얻을때 까지 기다림 if (semop(semid,&op2,1) == -1) { perror("Reason:"); exit(-1); } else fprintf(stderr,"Thread2:Successfully unlocked 1th semaphoren"); } int main() { pthread_t tid1,tid2; int semid; //세마포어 초기화를 위해 유저가 정의한 semun 생성 typedef union semun { int val; struct semid_ds *buf; ushort * array; }semun_t; semun_t arg; semun_t arg1; //하나의 집합에 2개의 세마포어를 가진 세마포어 객체 생성 //0번째, 1번째 세마포어 semid = semget(IPC_PRIVATE,2,0666|IPC_CREAT); if(semid<0) { perror("semget failed Reason:"); exit(-1); } //1로 설정하여 0번째 세마포어 초기화 arg.val = 1; if ( semctl(semid,0,SETVAL,arg)<0 ) { perror("semctl failure Reason:"); exit(-1); } //1로 설정하여 1번째 세마포어 초기화 arg1.val = 1; if( semctl(semid,1,SETVAL,arg1)<0 ) { perror("semctl failure Reason: "); exit(-1); } //이 세마포어에 대해 동작하는 2개의 쓰레드 생성 if(pthread_create(&tid1, NULL,Thread1, semid)) { printf("n ERROR creating thread 1"); exit(1); } if(pthread_create(&tid2, NULL,Thread2, semid) ) { printf("n ERROR creating thread 2"); exit(1); } //쓰레드 동작이 끝나기를 기다림 pthread_join(tid1, NULL); pthread_join(tid2, NULL); //once done clear the semaphore set if (semctl(semid, 1, IPC_RMID ) == -1 ) { perror("semctl failure while clearing Reason:"); exit(-1); } //메인 쓰레드 종료 pthread_exit(NULL); return 0; }
struct semid_ds { struct ipc_perm sem_per; /* operation"s permission structure */ struct sem * sem_base; /* pointer to first sem in a set */ ushort sem_nsems; /* number of sem in a set */ time_t sem_otime; /*time of last semop */ time_t sem_ctime; /* time of last change */ }시스템의 모든 세마포어에 대해 커널은 sys/ipc.h에 정의된 정보에 관한 구조체를 유지한다. 위에서 설명한 세마포의 좀 더 상세한 설정을 위해 IPC_STAT플래그가 사용되고 구조체 semid_ds에 대한 포인터가 전달된다. 허가권을 얻고 허가권을 설정하는 멤버 변수는 sem_per이다. 간단한 예제를 살펴보자.
#include[주의] 새로운 허가권 집합이 처음 허가권의 부분 집합인 경우에만 허가권 설정이 허용된다.#include #include #include int main() { int semid; struct semid_ds status; semid = semget(( key_t )0x20,10,IPC_CREAT|0666); if(semid == -1) { perror("sem creation failed:Reason"); exit(0); } //허가권을 가져옴 semctl(semid,0,IPC_STAT,&status); printf("owners uid is %un",status.sem_perm.uid); printf("group uid is %un",status.sem_perm.gid); printf("Access mode is %cn",status.sem_perm.mode); //허가권을 설정함 status.sem_perm.uid = 102; status.sem_perm.gid = 102; status.sem_perm.mode = 0444; semctl(semid,0,IPC_SET,&status); return 1; }
이전 글 : 하이버네이트를 사용한 애자일 데이터베이스 리팩토링(2)
다음 글 : 리눅스에서의 세마포어(2)
최신 콘텐츠