ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 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
Designed by Tistory.