-
Semaphore (세마포)운영체제 2024. 4. 18. 23:02
락을 위해서도 조건변수를 위해서도 사용 가능
sem_init(semaphore, p_shared, initial_value)
semaphre : 사용할 세마포
p_shared : 프로세스 간 쉐어, 안하면 0으로 설정하면 됨
initial_value : 이걸 어떻게 사용할 건지에 따라 바이너리 세마포, 카운팅 세마포. 사용법이 다름
int sem_wait(sem_t *s) // 세마포의 값을 1 내리고 음수면 웨이트 int sem_post(sem_t *s) // 세마포의 값을 1 올리고 대기 중인 스레드 하나 꺠우기
두 함수를 사용해서 세마포 사용
sem_t m; sem_init(&m, 0, 1); sem_wait(&m); // critical section sem_post(&m);
락으로 사용하는 경우, 바이너리 세마포
세마포의 값을 1로 준다
값이 1 : 들어와도 된다. 들어오는 스레드는 값을 1 내리고 0으로 만들 것
값이 0 : 누가 수행 중이고, 기다리는 스레드는 없다. 들어오는 스레드는 값을 음수로 만들고 슬립
값이 -1, -2, -3.... : 기다리는 스레드가 절댓값만큼 있다.
sem_t s; void *s child(void *arg) { printf("child\n"); sem_post(&s); return NULL; } int main(int argc, char *argv[]) { sem_init(&s, 0, 0); printf("parent: begin\n"); pthread_t c; pthread_create(&c, NULL, child, NULL); sem_wait(&s); printf("parent: end\n"); return 0; }
조건변수로 사용하는 경우,
세마포의 값을 0으로 줘야 함
부모 스레드에서는 0인 세마포의 값을 -1로 만들고 슬립
-> 자식 스레드가 끝나기 전 세마포 값읗 1 증가시켜 0으로 만들고 종료
-> 이제 세마포 값이 음수가 아니니 부모 스레드 실행 가능
sem_t empty; sem_t full; sem_t mutex; void *producer(void *arg) { int i; for( i = 0; i < loops; i++ ) { sem_wait(&empty); sem_wait(&mutex); put(i); sem_post(&mutex); sem_post(&full); } } void *consumer(void *arg) { int i; for( i = 0; i < loops; i++ ) { sem_wait(&full); sem_wait(&mutex); put(i); sem_post(&mutex); sem_post(&empty); } }
mutex -> 바이너리 세마포
full, emtpy -> 카운팅 세마포
일반 조건변수와 다르게, 조건 변수가 락 바깥에 존재
full은 처음엔 0으로 초기화 (버퍼가 비어 있으니까)
empty는 최대 버퍼 크기로 초기화
Read / Writer Problem
판독자는 읽기만 하니까, 판독자 끼리는 상호배제(락)이 필요 없음
기록자 간에는 전통적인 방법으로 락하면 됨
typedef struct _rwlock_t { sem_t lock; // 기록자들 락 sem_t writelock; // 메인 락 int readers; } rwlock_t; void rwlock_acquire_readlock(rwlock_t *rw) { sem_wait(&rw->lock); rw->readers++; if( rw-> readers == 1 ) sem_wait(&rw->writelock); sem_post(&rw->lock); } void rwlock_release_readlock(rwlock_t *rw) { sem_wait(&rw->lock); rw->readers--; if( rw-> readers == 0 ) sem_post(&rw->writelock); sem_post(&rw->lock); } void rwlock_acquire_writelock(rwlock_t *rw) { sem_wait(&rw->writelock); } void rwlock_release_writelock(rwlock_t *rw) { sem_post(&rw->writelock); }
기록자: 라이트락을 걸고, 풀기만 하면 됨
판독자
기록자들은 락을 잡고 카운터를 증가 -> 1이면 혼자 있는 것
혼자 있으면 기록자가 쓰고 있는지 아닌지 모르기 때문에 라이트락도 잡고 들어가야 함
2 이상이다 -> 먼저 들어간 기록자가 있는 것 -> 라이트락 확인할 필요가 없어짐
언락 시에는 반대로 카운터를 감소시키는데, 0이면 마지막으로 나가는 판독자라 라이트락을 언락해줘야 함
The Dining Philosophers
5명의 철학자, 5개의 포크 / 먹거나 생각하거나 둘 중 하나의 행동
각 철학자는 자신의 왼쪽, 오른쪽 포크 두 개를 사용해야 식사가 가능
포크: 공유자원
void get_forks(int p) { sem_wait(&forks[left(p)]); sem_wait(&forks[right(p)]); } void put_forks(int p) { sem_post(&forks[left(p)]); sem_post(&forks[right(p)]); }
전부 자신의 왼쪽 것부터 집게 되는 상황 -> 데드락 유발
해결책
1. 순서 바꾸기
2. 철학자 수 제한하기
3. 트랜잭션 도입 (모니터)
4. 포크 더 쓰기
5. 포크 하나로 먹을 수 있게 만들기
* 트라이락: 못잡으면 undo
-> 5명이 놓고 잡고만 반복하는 라이브락 상태가 될 수 있음
우선순위도 무엇에 우선순위를 높게 줄 것인가 등, 어렵긴 해도 해결책은 될 것
'운영체제' 카테고리의 다른 글
I/O Device, Device Driver (0) 2024.05.06 데드락 (0) 2024.05.06 조건 변수, 생산자/소비자 문제 (0) 2024.04.18 Lock-based Concurrent Data Structure (0) 2024.04.18 Lock의 구현 (0) 2024.04.11