[운영체제] CH04. Thread

PikminProtectionAssociation·2024년 11월 10일

행성 탈출기

목록 보기
13/21
post-thumbnail

Overview

  • 하나의 프로세스는 메모리 상의 이미지인 코드, 데이터, 스택 등으로 구성
  • 실행 중인 프로세스는 프로그램 카운터를 포함한 레지스터를 가짐
  • single-threaded process : 전통적인 프로세스는 프로세스 안에 thread가 하나인 프로세스

  • multithreaded process
    • 코드, 데이터, 파일은 공유
    • 레지스터, 스택은 각 thread마다 가짐
      → 각 실행을 대표하는 내용, 즉 실행시마다 달라지는 레지스터와 스택을 합쳐서 thread라고 부름
    • 실행의 기본 단위는 process가 아닌 thread
      → scheduling의 대상도 thread
  • thread를 'light-weight process'라고도 함
  • 현대 OS들은 kernel 자체가 다수의 thread로 이루어져 있음

Multithreaded Server Architecture

  • 네트워크 서버 프로그램의 동작
  • 클라이언트로부터 서비스 요청이 들어오면 사용자 요청을 대기하고 있던 thread가 요청을 받아서 이를 처리할 수 있는 서비스 thread를 생성하고, 요청에 대한 처리를 넘김
  • 서버는 또 다른 클라이언트 요청을 반복해서 대기하는 구조
  • 하나의 웹 서버 프로세스가 웹으로부터의 요청을 받는 하나의 thread와 다수의 클라이언트를 담당하는 thread 등으로 이루어짐

장점

  • Responsiveness
    • interactive한 응용의 경우 프로그램의 일부가 block 되거나 긴 작업을 수행하더라도 프로그램의 다른 부분의 수행이 계속되는 것을 허용할 수 있음
    • 사용자 응답성 증가
  • Resource Sharing
    • 프로세스는 공유 메모리나 message 전달로 통신을 해야 했지만, thread는 자신이 속한 프로세스의 다른 thread들과 메모리 등 각종 자원을 공유 가능
  • Economy
    • 프로세스를 생성하기 위해 메모리와 자원을 할당하는 것은 많은 비용이 드는 작업
    • thread는 자신이 속한 프로세스의 자원을 이미 공유하고 있으므로, thread만 생성 후 이들 간의 문맥을 교환하는 것이 훨씬 경제적임
  • Scalability
    • single-threaded 프로세스의 경우 다중처리기 상에서 수행되더라도 한 처리기에서만 실행됨
    • multi-threaded 환경에서는 각 thread가 다른 CPU에서 병렬로 수행 가능



Multicore Programming

  • Multiprocessor : 여러 개의 CPU를 가지는 컴퓨터 시스템
  • Multicore : 단일 CPU 칩 안에 명령을 실행할 수 있는 컴퓨팅 코어가 여러 개인 시스템
    • OS 같은 소프트웨어 입장에서는 멀티코어 상에서의 각 코어가 독립적인 CPU로 보임

  • Parallelism
    • 여러 개의 task를 병렬로 실행시킬 수 있음
    • multi-threaded 프로그램은 병렬 실행을 할 수 있는 방법을 제시



  • Concurrency
    • 병행 수행은 여러 개의 task가 동시에 진행되도록 하는 것
    • CPU가 하나인 경우에도 가능
    • multi-programming은 병행 수행을 추구하는 기법

  • Data parallelism
    • 전체 데이터 집합을 다수의 부분 집합으로 나누고 이를 다수의 컴퓨팅 코어에 분배해서 각 코어가 동일한 연산을 실행하게 하는 경우
    • single-core에서의 계산보다 실행시간이 절반으로 줄어듦
  • Task parallelism
    • thread가 각각 동일한 작업을 하는 것이 아닌 각기 다른 작업을 하는 경우
  • Multicore 또는 Multiprocessor 시스템에서의 과제
    • Dividing activities : 응용을 분석해서 주어진 응용이 서로 독립적인 병렬로 실행 가능한 task들로 나눌 수 있는지, 어느 부분에서 나뉘는지 찾는 작업이 필요
    • Balance : 병렬 실행이 가능하도록 여러 개의 task로 분리했을 때 각 부분이 전체 작업에 대해 균등한 기여도를 가지도록 나누는 것이 중요
    • Data splitting : task가 접근하는 데이터 또한 코어에서 사용할 수 있도록 나눠져야 함
    • Data dependency : task가 접근하는 데이터가 둘 이상의 task가 접근했을 때 종속성이 있는지 검토해야 함
    • Testing and debugging : 프로그램이 다중 코어 상에서 병렬 실행될 때 실행 경로가 여러가지 존재할 수 있으므로 모든 경로가 올바른 실행인지 검증할 필요가 있음

Amdahl's Law

  • 순차 실행을 해야 되는 구성요소와 병렬 실행이 가능한 구성요소를 동시에 가지는 응용이 있을 때 이 응용을 멀티코어에서 실행함으로써 얻을 수 있는 성능 향상을 나타내는 공식

  • S : 한 프로그램에서 순차 실행해야 되는 부분의 비율
  • N : 코어의 개수
  • N이 무한대로 커지면 성능 향상은 1/S에 수렴하게 됨
    성능 향상의 최대치가 1/S
  • 응용에 포함된 순차 실행은 코어를 추가해서 얻을 수 있는 성능 향상에 불균형적인 영향을 미치게 됨
  • 암달의 법칙은 코어의 개수를 늘리면 성능이 향상되지만 성능 향상에는 물리적 한계가 있다는 것을 시사함



Multithreading models

  • User thread
    • 사용자 수준에서 동작
    • 사용자 수준 thread는 thread library를 통해 사용 가능
    • thread library를 사용하면 하위의 OS kernel에서 thread 기능을 제공하는지 여부에 상관없이 thread를 만들어 사용 가능
  • Thread libraries
    • POSIX Pthreads
    • Window thread
    • Java threads
  • Kernel thread
    • kernel 수준에서 제공하는 thread
    • kernel 자체도 여러 개의 thread로 이루어져 있음
  • User thread와 Kernel thread는 둘 사이 연관 관계가 있어야 함
    • Many-to-One
    • One-to-One
    • Many-to-Many

Many-to-One

  • 여러 개의 사용자 수준 thread가 하나의 kernel thread에 연관되어 있음
  • 여러 개의 user thread 중 하나가 blocking 시스템콜을 하게 되면 그에 따라 유일한 kernel thread가 block 상태가 되어 나머지 thread들도 원치 않게 block 상태가 됨
  • 한 번에 하나의 thread만이 kernel에 접근 가능하므로, 여러 thread가 다중코어 시스템에서 병렬로 실행
    • 무늬만 다중 thread 형태임
    • 실제로는 한 번에 한 thread만 실행 가능
  • 예시
    • Solaris Green Threads
    • GNU Portable Threads

One-to-One

  • 각 user thread가 kernel thread 하나와 일대일 대응 되어있는 모델
  • user thread 하나를 만들면 그에 대응하는 kernel thread가 하나 생성됨
  • Many-to-One 모델보다 더 많은 병렬성 제공
  • 너무 많은 수의 kernel thread가 생성되어 시스템에 부담을 줄 수 있으므로 사용자 수준에서 프로세스당 만들 수 있는 thread의 개수가 제한되는 경우가 많음
  • 예시
    • Windows
    • Linux
    • Solaris 9 and later

Many-to-Many

  • 다수의 사용자 thread를 다수의 kernel thread에 연관시키는 모델
  • 여러 개의 사용자 thread를 그보다 작거나 같은 수의 kernel thread로 multiplex하는 방식
    • multiplex : 어느 사용자 thread가 어느 kernel thread와 연관될지는 그때그때 달라짐
  • kernel이 미리 적절한 수의 kernel thread를 생성하고 필요한 user thread가 생길 때마다 이를 적당한 kernel thread에 대응시킴
  • Many-to-One과 One-to-One 모델의 단점을 보완
    • 응용 개발자 입장에서는 필요한 만큼 많은 사용자 수준의 thread 생성 가능
    • 이에 상응하는 kernel thread가 멀티프로세서 상에서 병렬 수행 가능
  • 예시
    • Solaris prior to 9
    • Windows with the ThreadFiber package

Two-level

  • 한 시스템 내에서 Many-to-Many 모델과 One-to-One 모델을 혼용하는 방식
  • 기본적으로는 Many-to-Many 모델을 사용하되, 일부 thread의 경우 One-to-One 방식으로 user와 kernel thread가 연관될 수 있음
  • 예시 (과거 일부 유닉스 계열 OS가 사용하던 방식)
    • IRIX
    • HP-UX
    • Tru64 UNIX
    • Solaris 8 and earlier



Thread Libraries

  • 프로그래머에세 thread를 생성하고 관리할 수 있는 API를 제공
  • thread library 구현 방법
    • 순수하게 user level에서 구현
    • OS가 지원하는 kernel 수준 library
  • POSIX Pthreads / Windows Threads / Java Thread

Pthreads

  • 대표적으로 널리 사용되며, user 수준 또는 kernel 수준에서 실행 가능

  • IEEE 1003.1c 표준을 준수하는 library

  • 대부분의 유닉스 계열 OS는 Pthread를 지원

  • Pthread library를 사용해서 숫자 n을 입력 받고 1~n까지 정수의 합을 구해서 출력하는 프로그램

    #include <pthread.h>
     #include <stdio.h>
    
     int sum;	/* shared by the thread(s) */
     void *runner(void *param);	/* threads call this function */
     
     int main(int argc, char *argv[])
     {
     	pthread_t tid;	/* thread identifier */
      	pthread_attr_t attr;	/* set of thread attributes */
          
        if (argc != 2) {
        	fprintf(stderr, "usage: a.out <integer value>\n");
          	return -1;
        }
        
        if (atoi(argv[1]) < 0) {
        	fprintf(stderr, "%d must be >= 0\n", atoi(argv[1]));
          	return -1;
        }
        
        /* get the default attributes */
        pthread_attr_init(&attr);
        
        /* create the thread */
        pthread_create(&tid, &attr, runner, argv[1]);
        
        /* wait for the thread to exit */
        pthread_join(tid, NULL);
        
        printf("sum = %d\n", sum);
    }
    
    /* The thread will begin control in this function */
    void *runner(void *param)
    {
    	int i, upper = atoi(param);
      	sum = 0;
          
        for (i = 1; i <= upper; i++)
        	sum += i;
        
        pthread_exit(0);
    }
    • sum : main thread와 runner thread 간의 결과값을 주고받을 수 있는 전역변수
    • pthread_attr_init(&attr)
      • Pthread는 새로운 thread를 만들 때 thread의 속성을 담을 변수가 필요
      • 변수는 사용하기 전에 pthread_attr_init 함수를 이용해 속성 변수 attr을 초기화
    • pthread_create(&tid, &attr, runner, argv[1])
      • tid : thread의 id. Pthread는 새로운 thread가 생성될 때마다 새로운 id를 부여
      • attr : thread의 속성을 담는 변수
      • runner : 함수의 이름(포인터). Pthread에서는 새로 생성되는 thread가 실행할 코드를 함수 단위로 지정
      • argv[1] : 명령줄에 표시된 숫자 n을 가리킴
      • 4번째 parameter는 3번째 parameter에 지정된 함수가 실행될 때 이 함수에 대한 parameter로 주어지게 됨
    • pthread_join(tid, NULL)
      • 특정한 thread의 종료를 기다리는 동작
      • 어떤 thread를 기다릴지는 첫 번째 parameter로 명시

Windows Multithreaded C Program

#include <windows.h>
#include <stdio.h>
DWORD Sum;	/* shared by the thread(s) */

/* the thread runs in this separate function */
DWORD WINAPI Summation(LPVOID Param)
{
	DWORD Upper = *(DWORD*)Param;
    for (DWORD i = 0; i <= Upper; i++)
    	Sum += i;
    return 0;
}

int main(int argc, char *argv[])
{
	DWORD ThreadId;
    HANDLE ThreadHandle;
    int Param;
    
    if (argc != 2) {
    	fprintf(stderr, "An integer parameter is required\n");
        return -1;
    }
    
    Param = atoi(argv[1]);
    
    if (Param < 0) {
    	fprintf(stderr, "An integer >= 0 is required\n");
        return -1;
    }
    
    /* create the thread */
    ThreadHandle = CreateThread(
    	NULL,		/* default security attributes */
        0,			/* default stack size */
        Summation,	/* thread function */
        &Param,		/* paramter to thread function */
        0,			/* default creation flags */
        &ThreadId);	/* returns the thread identifier */
        
    if (ThreadHandle != NULL) {
    	/* wait for the thread to finish */
        WaitForSingleObject(ThreadHandle, INFINITE);
        
        /* close the thread handle */
        CloseHandle(ThreadHandle);
        
        printf("sum = %d\n", Sum);
    }
}
  • Sum : Thread 간에 공유되는 전역변수
  • Summation : 새로 생성된 thread의 코드에 해당하는 함수
    • 별도의 종료 함수가 따로 있지 않고 thread 함수가 return 하는 것으로 thread가 종료됨
  • Param : Summation 함수에 주어질 parameter



Implicit Threading

  • multi-programming이 많이 사용되면서 하나의 프로그램이 수백 개의 thread로 구성되기도 함
    → 프로그램이 올바르게 동작한다는 것을 검증하는 것이 어려워짐
  • implicit threading : thread의 생성과 관리를 프로그래머가 아닌 컴파일러나 run-time library 등이 대신 해줌
  • 대표적인 기법
    • Thread Pools
    • OpenMP
    • Grand Central Dispatch

Thread Pools

  • 프로세스를 시작할 때 미리 일정한 수의 thread들을 pool 형태로 만들어 놓는 것
  • pool에 속하는 thread들은 평소에 아무것도 하지 않고 대기 상태로 존재하다가 요청이 발생하면 할당됨
  • 장점
    • 미리 thread를 만들어둠으로써 필요시에 생성하는 것보다 전체 동작이 빨라짐
    • thread가 무한정 늘어나는 것을 방지
    • task의 실행을 생성과 분리함으로써 실행에 대한 다양한 전략 적용 가능

OpenMP

  • C나 C++, FORTRAN 같은 언어들을 위한 일련의 컴파일러 directive와 API의 집합
  • parallel region : OpenMP에서 병렬로 실행 가능한 부분
    • 개발자는 자신의 코드 중 parallel region에 관련 컴파일러 directive를 삽입함으로써 OpenMP run-time library에 해당 영역을 병렬로 실행하도록 요청할 수 있음

Grand Central Dispatch

  • 애플 사에서 Mac OS를 위해 개발한 기술로, C language, API, run-time library를 확장한 기술
  • C언어를 확장해서 block을 정의해 사용
    • "^{ }"을 하나의 block으로 정의
    • 예시 : ^{ printf("I'm a block"); }
  • block을 dispatch queue에 넣어서 나중에 실행이 될 수 있도록 스케줄할 수 있음
  • queue에서 block을 하나씩 꺼내어 해당 block을 thread pool에서 꺼낸 가용 thread에 할당해서 실행
  • dispatch queue 타입
    • serial
      • first-in-first-out 방식으로 block을 꺼냄
      • 꺼내진 block은 또 다른 block을 꺼내기 전 실행이 되도록 보장함
      • 프로세스마다 serial queue를 하나씩 갖고 있으며 이를 main queue라고 함
      • 프로그래머가 추가로 생성 가능
    • concurrent
      • first-in-first-out 순으로 block을 꺼내는데, 한 번에 여러 개를 꺼낼 수 있음
      • 여러 개를 꺼내는 경우 이들은 동시 실행 가능
      • 시스템 전체적으로 3개의 concurrent queue가 있음 (우선순위에 따라 나뉨)



참고 자료 : Operating System Concepts Essentials
*이미지 자료는 교재 자료를 직접 다시 만든 것으로 무단 불펌 금지입니다




제일 좋아하는 파트가 점점 다가와서 설렌다

끗!!

0개의 댓글