#세마포어

🔗 Semaphores

  • set_init()의 pahared는 하나의 프로세스내의 쓰레들 간의 동작이면 0, 0이 아닌경우는 프로세스간의 상황을 의미한다.

    int sem_init(sem_t *s, int pshared, unsigned int value)
    
  • set_wait()은 s를 1 감소시고, s가 음수가 되면 큐에 재운다.

    int sem_wait(sem_t *s)
    
  • set_post()은 s를 1 증가시키고, 큐에서 깨워준다.

    int sem_post(sem_t *s)
    

    Binary Semaphores

    sem_t m;
    sem_init(&m, 0, 1);
    
    sem_wait(&m);   //1 -> 0
    //critical section  //다른 쓰레드가 0 -> -1를 하고, 큐에 들어간다.
    sem_post(&m);   //-1 -> 0, 큐에 있는걸 깨운다.  
    

컨디션 변수로서의 세마포어

sem_t s;

void * child(void * arg){
    printf("child\n");
    sem_post(&s);
    return NULL;
}
int main(int argc, char *argv[]){
    pthread_t c;
    sem_init(&s, 0, X); //what should X be?
    printf("parent: begin\n");
    pthread_create(&c, NULL, child, NULL);
    sem_wait(&s);
    printf("parent: end\n");
    return 0;
}

위의 코드에서 X에는 어떤수가 와야할까?

생산자/소비자 문제

이전장의 생산자 소비자 함수를 세마포어 함수로 구현해보자.

sem_t empty, fill;
sem_t mutex;

void *producer(void *arg){
    int i;
    for(int i=0;i<loops;i++){
        pthread_mutex_lock(&mutex);
        while(count == MAX)
            pthread_cond_wait(&empty, &mutex);
        put(i);
        pthread_mutex_unlock(&mutex);
    }
}

void *consumer(void *arg){
    int i;
    for(int i=0;i<loops;i++){
        pthread_mutex_lock(&mutex);
        wihle(count == 0)
            pthread_cond_wait(&fill, &mutex);
        int tmp = get();
        pthread_cond_signal(&empty);
        pthread_mutex_unlock(&mutex);
        printf("%d\n",tmp);
    }
}

int main(int argc, char *argv[]){
    sem_init(&empry, 0, MAX);   
    sem_init(&full, 0, 0);
    sem_init(&mutex, 0, 1);
}

Reader/Writer 락

읽기 락이 쓰기락도 획득하여 모든 읽기가 끝날 때까지 쓰기 쓰레드는 대기해야한다. 이 방법은 몇가지 단점이 있는데 쓰기 쓰레드가 공정성에서 불리하다.

typedef struct _rwlock_t{
    sem_t lock;
    sem_t writelock; //하나의 쓰기 또는 다수의 읽기락을 위한 락
    int readers;
}rwlock_t;
void rwlock_init(rwlock_t *rw){
    rw -> readers = 0;
    sem_init(&rw->readers, 0, 1);
    sem_init(&rw->writelock, 0, 1);
}
void rwlock_acquire_readlock(rwlock_t *rw){
    sem_wait(&rw->lock);
    rw->readers++;
    if(rw->readers == 1)
        sem_wait(&rw->writelock);   //읽기용 락이 writelock획득
    sem_post(&rw->lock);
}
void rwlock_release_readlock(rwlock_t *rw){
    sem_wait(&rw->lock);
    rw->readers--;
    if(rw->readers == 0)
        sem_wait(&rw->writelock);   //마지막으로 읽기용 락이 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);
}

세마포어 구현

실제 세마포어는 다음과 같이 구현되어있다. 원래 세마포어에서는 세마포어의 음수 값이 대기중인 쓰레드의 수를 나타낸다. 그런데 아래 코드처럼 리눅스에서는 세마포어가 음수가 될수 었다. 구현이 간편해서 그렇다고 한다. 참고로 리눅스는 condition variable이 아닌 futex로 구현되있다.

typedef struct __Sem_t{
    int value;
    pthread_cond_t cond;
    pthread_mutex_t lock;
}Sem_t;
void Sem_init(Sem_t *s, int value){
    s->value = value;
    Cond_init(&s->cond);
    Mutex_init(&s->lock);
}
void Sem_wait(Sem_t *s){
    Mutex_lock(&s->lock);
    while(s->value <= 0)
        Cond_wait(&s->cond, &s->lock);
    s->value--;
    Mutex_unlock(&s->lock);
}
void Sem_post(Sem_t *s){
   Mutex_lock(&s->lock);
    s->value++;
    Cond_signal(&s->cond);
    Mutex_unlock(&s->lock); 
}