[회고] 신입 iOS 개발자가 되기까지 feat. 카카오 자세히보기

💻 CS/운영체제

[운영체제] 상호작용 프로세스와 동기화

inu 2020. 4. 28. 18:15
반응형

상호작용 프로세스 (Cooperating process)

  • 상호작용 프로세스 : 컴퓨터에는 여러 프로세스들이 집합을 이루고 있다. 그런 프로세스들이 서로 상호작용을 한다는 의미이다. 쓰레드끼리의 상호작용도 해당개념에 포함된다.
  • 상호작용이 원활히 되면 좋겠지만 결정성, 상호배제와 동기화, 교착상태, 기아 등의 이슈가 존재한다.
  • 해당 파트에서는 결정성, 상호배제와 동기화에 대해 중점적으로 학습한다.

프로세스 시스템

  • 프로세스 시스템 : 상호작용 프로세스의 모델링이다. 프로세스의 집합과 이들의 선행 제약으로 정의된다.
  • 선행제약 : 프로세스의 집합 내에서 프로세스 간의 제약 관계를 뜻한다. 예들 들어 순서에 대한 제약관계가 있다. 해당 순서관계는 부분 순서의 성질을 갖는다. 즉, 집합에 존재하는 모든 프로세스 간에 정의된 것이 아니고 그 중 일부에만 정의된다. 해당 순서관계를 가진 프로세스들 간에는 이행성이라는 성질이 생긴다. 예를 들어 p1이 p2에 선행하고 p2가 p3에 선행하면 이 두 관계를 통해 p1이 p3에 선행한다.
  • 만약 선행제약 관계가 없는 두 프로세스가 있을 경우, '두 프로세스는 독립적이다'라고 표현한다.
  • 단, 프로세스들은 서로 독립이거나 관계가 있다면 해당 선행제약관계는 비순환적이어야 한다.
  • '비순환적' 이란 프로세스 간 관계가 가위바위보와 같이 순환되는 관계가 되어선 안된다는 것이다. 즉, 관계는 단순한 일직선으로 이루어져야한다. 순환되는 부분이 단 한 부분이라도 있어선 안된다.
  • cf. 선행 그래프 : 프로세스 시스템 내의 프로세스간 선행 제약관계를 Directed 그래프로 표현한 것
  • 간섭 : 프로세스 간의 공유변수가 존재하여 두 프로세스가 서로 영향을 주고 받는 것이다. 예를 들어 프로세스 p1과 p2는 서로 독립적인 관계이면서 변수 a와 b를 공유한다면, 실행순서에 따라 결과가 달라질 위험이 있다. 이는 프로세스 시스템이 온전히 구성되더라도 발생할 수 있다.

결정성과 프로세스 비간섭 관계

  • 결정성 : 프로세스 시스템 내의 프로세스들 간의 코드 실행 순열이 매번 다를 수 있지만 같은 조건과 입력이 주어진다면, 항상 같은 결과를 산출해야 한다는 성질이다.
  • 즉, 프로세스들이 선행제약 관계를 가지고 있더라도 독립적인 관계가 존재하기 때문에 실행 순서는 다를 수 있다. 하지만 같은 조건과 입력이 주어지면 반드시 같은 결과를 산출해야한다는 것이 결정성이다.
  • 결정성을 유지하기 위해서는 프로세스간의 간섭이 존재해선 안된다. 이를 프로세스 비간섭 관계라고 한다.
  • 정확히는 프로세스 시스템에 두개의 프로세스가 있을 때, 한 프로세스가 다른 프로세스를 아예 선행하거나 / 독립적일 경우 한 프로세스의 출력 장소가 다른 프로세스의 입력 장소나 출력 장소가 아니면, 이들은 비간섭 관계에 있다고 정의한다.
  • 한 프로세스 시스템에서 모든 프로세스 쌍이 비간섭 관계를 만족하면 해당 시스템이 비간섭 관계를 만족한다고 한다. 이는 시스템이 결정적이도록 하는 필요충분적 조건이다. (비간섭을 보장하면 프로세스 시스템에 결정성이 생긴다)

결정성 지원 방법

  • 개발자가 시스템을 구성할 때 결정성을 만족하도록 하는 방법에는 두가지가 있다.
  • 첫 번째로는 선행관계를 명시하도록 지원하는 것이다. 예를 들면 parbegin과 parend를 사용하는 방법이 있다. parbegin과 parend를 사용하면 parbegin과 parend 사이의 각 문장들이 독립적으로 실행됨을 명시할 수 있다.
  • S0, parbegin, S1, S2, parend, S3의 순서로 명시하면, S1과 S2는 독립적이고 S3에 선행이며, SO는 그들보다 선행하는 관계임을 나타낼 수 있다.
  • fork와 join으로도 비슷한 형태를 만들 수 있다.
  • 두 번째로는 공유 변수 간섭문제를 해결하도록 지원하는 것이다. 해당 기능의 구현을 위해 임계구역, 상호배제, 세마포어와 같은 수단을 사용한다.
  • 이를 통해 '동기화된' 프로그램을 제공한다고 표현하는데, 여기서 '동기화된'이란 병렬로 돌던 프로그램을 순서있게 돌게하여 간섭을 배제한다는 의미이다.

FIFO, RR 쓰레드 사용 예제 주석 설명

#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <sched.h>
#include <linux/sched.h> // sched 관련 정보 사용가능하도록 라이브러리 호출
#include <sys/types.h>
#include <pthread.h>

#include <math.h>
#include <time.h>
#include <stdlib.h>

#define NUM_THREADS 20 // 일반적 쓰레드 20개
#define NUM_THREADS_FIFO  1 // FIFO 쓰레드 1개
#define NUM_THREADS_RR  1 // RR 쓰레드 1개
// 다른 쓰레드들이 도는 동안 FIFO와 RR의 수행시간이 어느정도 지켜지느냐?

int flag = 0; // 쓰레드가 동시에 시작될 수 있도록 방아쇠
char str_buf[16384]; // 자료 저장 버퍼
int buf_idx = 0;

char junk_buf[16384]; // 정크 버퍼
void set_my_sched_policy(int my_policy) // 내 쓰레드가 지금부터 이러한 정책으로 돌겠다.
{
    int policy;
    pthread_attr_t attr;

    /* get the default attributes */
    pthread_attr_init(&attr); // 쓰레드 특성 초기화

    /* set the scheduling policy - real-time */

    if (pthread_attr_setschedpolicy(&attr, my_policy) != 0) // 현재 policy 변경
        fprintf(stderr, "Unable to set policy.\n");

    /* get the current scheduling policy */
    if (pthread_attr_getschedpolicy(&attr, &policy) != 0)
        fprintf(stderr, "Unable to get policy.\n");
    else { // 현재 policy 변경되었는지 내용 확인 후 출력
        if (policy == SCHED_OTHER)
            printf("SCHED_OTHER\n");
        else if (policy == SCHED_RR)
            printf("SCHED_RR\n");
        else if (policy == SCHED_FIFO)
            printf("SCHED_FIFO\n");
    }
}
void *runner(void *);
void *runner_FIFO(void *);
void *runner_RR(void *);

int main(int argc, char *argv[])
{
    int i, policy;

    pthread_t tid[NUM_THREADS];
    pthread_t tid_FIFO[NUM_THREADS_FIFO];
    pthread_t tid_RR[NUM_THREADS_RR];

    //
    // EXECUTING NORMAL THREADS (SCHED_OTHER)
    //
    /* create SCHED_OTHER threads */
    for (i = 0; i < NUM_THREADS; i++)
        pthread_create(&tid[i],NULL,runner,NULL);

    //
    // EXECUTING FIFO THREADS
    //
    /* create real-time threads */
    for (i = 0; i < NUM_THREADS_FIFO; i++)
        pthread_create(&tid_FIFO[i],NULL,runner_FIFO,NULL);

    //
    // EXECUTING RR THREADS
    //
    /* create real-time threads */
    for (i = 0; i < NUM_THREADS_RR; i++)
        pthread_create(&tid_RR[i],NULL,runner_RR,NULL);

    //
    // RELEASE THREADS
    //
    flag = 1; // 쓰레드 홀딩하다가 실행

    //
    // DO 'JOIN' ON ALL THREADS
    //
    /* now join on each thread */
    /*
    for (i = 0; i < NUM_THREADS; i++){
        pthread_join(tid[i], NULL);
    } // FIFO와 RR이 끝나면 걍 끝나도록
    */
    for (i = 0; i < NUM_THREADS_FIFO; i++){
        pthread_join(tid_FIFO[i], NULL);
    }
    for (i = 0; i < NUM_THREADS_RR; i++){
        pthread_join(tid_RR[i], NULL);
    }

    str_buf[buf_idx] = '\0';
    printf("%s\n", str_buf);

    return 0;
}
/* Each thread will begin control in this function */
void *runner(void *param)
{
    int i, policy;
    pthread_attr_t attr;

    while(!flag) sleep(0); // sleep(0)은 스케줄링 한번만 시도해봐의 의미

    set_my_sched_policy(SCHED_OTHER); // 일반 스케줄링 정책 설정

    for(int i=0; i<=100; i++){
        for(int j=0; j<=500; j++){
            srand(time(0)); // random 넘버 시드 설정
            sprintf(junk_buf, "%f", pow(rand(), 10000));
            // 랜덤 넘버호출 후 그의 10000승 정크 버퍼에 출력
        }
        str_buf[buf_idx++] = '.';
    }
}

void *runner_FIFO(void *param)
{
    int i, policy;
    pthread_attr_t attr;

    while(!flag) sleep(0);

    set_my_sched_policy(SCHED_FIFO); // FIFO 스케줄링 정책 설정

    for(int i=0; i<=50; i++){
        for(int j=0; j<=100; j++){
            srand(time(0)); 
            sprintf(junk_buf, "%f", pow(rand(), 10000));             
        }
        usleep(55000); // 55ms sleep
        str_buf[buf_idx++] = '!';
        str_buf[buf_idx++] = '\n';
    }
}

void *runner_RR(void *param)
{
    int i, policy;
    pthread_attr_t attr;

    while(!flag) sleep(0);

    set_my_sched_policy(SCHED_RR); // RR 스케줄링 정책 설정

    for(int i=0; i<=50; i++){
        for(int j=0; j<=100; j++){
            srand(time(0)); sprintf(junk_buf, "%f", pow(rand(), 10000));
        }
        usleep(45000); // 45ms sleep
        str_buf[buf_idx++] = '@';
        str_buf[buf_idx++] = '\n';
    }
}

// 완벽한 규칙은 아니지만, 어느정도의 규칙성. 즉 연성 실시간성을 보여준다.
반응형