학교수업/운영체제

운영체제 및 보안 - Lecture 4. Thread

정보보호학과 새내기 2025. 4. 14. 19:34

세종대학교 - 운영체제 및 보안 수업

Multithreaded Programing

  • 개요
  • 멀티 코어 프로그래밍
  • 멀티스레딩 모델
  • 스레드 라이브러리
  • 스레드 암시
  • 스레딩 문제

Objectives

  • 스레드의 개념 설명
    • cpu 사용의 근본적인 단위 (멀티스레드 컴퓨터의 형태에서)
  • Pthread, Windows, Java thread 라이브러리 API 알기
  • 암시적 스레딩을 제공하기 위한 몇 가지 전략 알기
  • 멀티 스레드 프로그래밍과 관련된 문제 고찰
  • Windows와 Linux에서 스레드를 위한 운영체제 지원 다루기

개요 (4.1)

  • 스레드

    • CPU 이용의 기본 단위

    • 같은 프로세스에 속한 스레드 간에는 운영체제 자원 공유 가능

      • (코드, 데이터, 파일 공유) (레지스터, 스택 ,pc는 스레드 별로 저장)

동기 (4.1.1)

  • 대부분 최근 응용 프로그램은 다중 스레드를 이용 중
  • 하나의 응용에서 작업은 여러 스레드에서 나눠져서 작동
    • ex) update display, fetch data, spell checking, answer a network reqeust
  • 프로세스 생성 대비 스레드 생성은 비교적 가볍다. (자원 할당 및 시간 차원에서)
    • 심지어 간단한 코드로 효율성 증가
    • 커널은 일반적으로 멀티 스레드로 동작

장점 (4.1.2)

  • 응답성 (responsiveness)
    • 프로세스가 블락되어도 실행 지속 가능
      • 특히 사용자 인터페이스에서 중요
  • 자원 공유
    • 스레드는 프로세스의 자원 공유
      • 공유 메모리나, 메시지 전달보다 쉬움 (IPC)
  • 경제성
    • 프로세스 생성보다 쉬움
      • 스레드 스위칭이 문맥 교환(프로세스 교환)보다 쉬움
  • 규모 적응성
    • 멀티 프로세서 구조에서 장점을 가짐
      • 각 스레드가 병렬 수행 가능

멀티 코어 프로그래밍 (4.2)

  • 다중 코어

    • 단일 컴퓨팅 칩에 여러 컴퓨팅 코어 배치된 시스템
  • 멀티 코어 또는 멀티 프로세서 시스템은 프로그래머에게 아래 과제들로 압박을 줌

    • 테스크 인식
      • 응용을 분석하여 독립된 병행 가능 테스크로 나눌 수 있는 영역 찾는 작업
    • 균형
      • 전체 작업에 균등한 기여도를 가지도록 테스크 나누기
    • 데이터 분리
      • 접근하고 조작하는 데이터 또한 개별 코어에서 사용할 수 있는 상태 만들기
    • 데이터 종속성
      • 테스크가 접근하는 데이터가 둘 이상이 관련있는지 확인
    • 시험 및 디버깅
  • 병행성 (concurrency)

    • 모든 작업이 진행되게 하여 둘 이상의 작업을 지원

    • 단일 코어에서 가능

  • 병렬성 (parallelism)

    • 둘 이상의 작업을 동시에 수행 가능

  • 병렬성 종류
    • 데이터 병렬 실행
      • 동일한 데이터의 부분집합을 다중 코어에 분배한 뒤 각 코어에서 동일한 연산을 하는지 확인
    • 테스크 병렬 실행
      • 테스크(스레드)를 다수의 코어에 분배

Amdahl’s Law

  • 순차 실행 구성요소와 병렬 실행 구성요소로 이루어진 응용에 추가 코어를 넣었을 때 성능 확인하는 공식
    • N : 시스템 코어 개수
    • S : 반드시 순차적으로 실행되어야만 하는 구성요소
  • ex) 75% 병렬 실행 구성 요소 (당연히 25% 순차 실행 요소)를 가진 응용이 코어 2개인 시스템에서 실행시키면 1.6배 속도 향상, 코어 4개면 2.28배 속도 향상
  • N이 무한에 가까워지면 1/S에 수렴 (즉 1/S가 최고 성능 증가량)

다중 스레드 모델 (4.3)

  • 사용자 스레드

    • 사용자 단계에 의해 관리하는 스레드 라이브러리
      (사용자 공간에서 라이브러리 제공, 시스템 콜 호출이 아님)
    • 라이브러리 종류
      • POSIX Pthreads, Win32 threads, Java threads
  • 커널 스레드

    • 커널에 의해 지원
    • 대부분의 범용 OS가 가지고 있음
      • Windows, Solaris, Linux, Tru64 Unix, Mac OS X
  • 관계

    • 다대일, 일대일, 다대다 관계 존재

다대일 모델 (4.3.1)

  • 다수의 사용자 단계의 스레드가 하나의 커널 스레드와 연결
  • 하나의 스레드가 봉쇄형 시스템 콜 요청 시, 다른 전체 스레드 봉쇄
  • 아직 이용하는 시스템 존재
    • Solaris Green Threads, GNU Portable Threads

일대일 모델 (4.3.2)

  • 각 사용자 스레드를 하나의 커널 스레드로 사상 (커널 스레드가 많을 때)
  • 다대일과 다르게 봉쇄형 시스템 콜 호출해도 다른 스레드 영향 없음 → 더 많은 병렬성 제공
  • 커널 스레드를 많이 만들어야하고, 많은 수의 스레드가 시스템 성능에 부담
  • 예시
    • Windows NT/XP/2000, Linux, Solaris 9 포함 이후 버전

다대다 모델 (4.3.3)

  • 여러개의 사용자 수준 스레드를 여러 커널 스레드에 사상
  • 충분한 커널 스레드 제공 (코어 클수록)
    • 하지만 커널은 하나의 커널 스레드만 스케쥴하므로 진정한 병렬 실행 획득 아님
  • 예시
    • Solaris prior to version 9, Windows NT/2000 with the ThreadFiber package

Two-level 모델

  • 다대다 모델의 변형으로 일대일 모델도 포함
  • 일단 구현이 어렵고, 코어 수 증가로 일대일 모델 주로 사용

스레드 라이브러리 (4.4)

  • 프로그래머에게 제공된느 스레드 API
  • 방법
    • 사용자 공간만 사용되는 라이브러리
      • 사용자 공간의 지역 함수 호출 효과
    • 커널 레벨에서 지원되는 라이브러리
      • 커널 시스템 콜을 부르는 결과

Pthreads (4.4.1)

  • 사용자 단계 또는 커널 단계에서 제공
  • 스레드 생성 및 동기화 위한 POSIX standard API 존재
    • 구현이 아니라 명세이다.
  • 운영체제 설계자들이 구현할 수 있고 많은 시스템이 이미 구현하고 있다.
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

int sum; /* 스레드 간 공유되는 전역 변수 선언 */
void *runner(void *param); /* 함수 호출하는 스레드 */

int main(int argc, char *argv[]){
    pthread_t tid;
    pthread_attr_t attr;

    pthread_attr_init(&attr); /* 스레드 기본 속성 불러오기 */
    pthread_create(&tid, &attr, runner, argv[1]); /* 자식 스레드 생성, runner 함수 호출 */
    pthread_join(tid,NULL); /* 자식 스레드 종료 기다림 */

    printf("sum = %d\n", sum);
}

void *runner(void *param){ /* 자식 스레드로 실행될 runner 함수 */
    int i, upper = atoi(param);
    sum = 0;

    for (i = 1; i <= upper; i++) sum += i;

    phtread_exit(0); /* 스레드 종료 */
}

Java Threads (4.4.3)

  • Java threads는 JVM에서 관리됨

  • 스레드를 사용하는 것을 명시하는 방법은 OS 별로 제공됨

  • Java threads는 아래와 같이 생성됨

      public interface Runnable{
          public abstract void run();
      }
    • 스레드 클래스 확장
    • 작동 가능한 인터페이스 명시
import java.util.concurrent.*;

class Summation implements Callable<Integer>{
    private int upper;
    public Summation(int upper){
        this.upper = upper;
    }

    public Integer call(){
        int sum = 0;
        for (int i = 1; i <= upper; i++)
            sum += i;

        return new Integer(sum);
    }
}
public class Driver{
    public static void main(String[] args){
        int upper = Integer.parseInt(args[0]);

        ExecutorService pool = Execuotrs.newSingleThreadExecutor();
        Future<Integer> result = pool.submit(new Summation(upper));
        try {
            System.out.println("sum =" + result.get());
        } catch (InterruptedException | ExecutionException ie) {}
    }
}

암묵적 스레딩 (4.5)

  • 스레드의 수가 증가함에 따라, 암묵적 스레드를 가지고 프로그램을 연결하는 것이 어려워졌다.

  • 스레드의 생성과 관리는 프로그래머가 아니라 컴파일러와 런타임 라이브러리가 한다.

    → 암묵적 스레딩

  • 개발자가 병렬로 실행할 수 있는 스레드가 아닌 작업 식별

스레드 풀 (4.5.1)

  • 프로세스를 시작할 때 일정한 수의 스레드를 미리 풀로 만들기
  • 장점
    • 기존 스레드로 사용하는 것이 새 스레드 생성보다 빠름
    • pool 개수를 미리 정해서 응용에서 스레드의 수를 미리 제한할 수 있음
    • 테스크를 생성하는 방법을 테스크로부터 분리하면 테스크를 실행을 다르게 할 수 있다.
      • 테스크를 일정 시간 후에 실행되도록 스케줄하거나 주기적으로 실행 가능
    • (pool은 VM에서 많이 사용)

OpenMP (4.5.3)

  • c, c++, FORTRAN으로 작성된 API와 컴파일러 디렉티브의 집합
  • 공유 메모리 환경에서 병렬 프로그래밍을 할 수 있도록 도움을 줌
  • 병렬 실행 될 수 있는 블록을 찾음
    • 병렬 영역 (parallel regions)
#include <omp.h>
#include <stdio.h>

int main(int argc, char *argv[]){
    #pragma omp parallel{
        printf("I am a parallel region.");
    }
    return 0;
}
#pragma omp parallel for
    for(i=0;i<N;i++){
        c[i] = a[i] + b[i];
    }

Grand Central Dispatch (4.5.4)

  • macOS, iOS 운영체제를 위한 애플 개발 기술
  • C, C++ 언어 사용, API, 런타임 라이브러리
  • 병렬로 실행될 코드 섹션을 식별
  • 스레딩의 세부 사항을 관리
  • 블럭 형태는 ^{ printf("I am a block");} 과 같고 이 블럭은 디스패치 큐에 넣어짐
  • 디스패치 큐 종류
    • 직렬 큐(메인 큐)
      • 순차적
        • FIFO 순서로 제거, 큐는 프로세스별로 있음
    • 병렬 큐 (전역 디스패치 큐)
      • 동시적
        • 우선순위를 3단계로 나눔(low, default, high)

스레드 관련 문제 (4.6)

  • fork(), exec() 시스템 콜
  • 신호처리
    • 동기식, 비동기식
  • 스레드 취소
    • 비동기식, 지연취소
  • 스레드-로컬 저장장치
  • 스케쥴러 액티베이션

fork(), exec() 시스템 콜 (4.6.1)

  • fork() 호출 시 스레드 복제 문제
    • exec() 할 거면 스레드 하나만 가져도 됨
    • 아예 복제할거면 모든 스레드들 복제해야함

신호 처리 (4.6.2)

  • 신호

    • unix에서 프로세스에 어떤 이벤트가 일어났음을 알려주기 위해 사용
  • 신호 핸들러는 시그널 절체에 따라 사용

    • 특정 이벤트에 의해 발생
    • 프로세스에 전달
    • 신호 처리기는 두 가지 종류 있음
      • 디폴트 신호 처리기
      • 사용자 정의 처리기
  • 디폴트 신호 처리기

    • 모든 신호마다 커널이 실행 → 단일 스레드 방식
  • 사용자 정의 처리기

    • 디폴트 대체
  • 다중 스레드 프로그램에서의 신호 처리 방식

    • 신호가 적용될 스레드에게 전달
    • 모든 스레드에 전달
    • 몇몇 스레드에 선택적 전달
    • 특정 스레드가 모든 신호 전달받도록 지정

스레드 취소 (4.6.3)

  • 스레드가 끝나기 전에 강제 종료 시키는 작업

  • Target thread : 취소되어야할 스레드

  • 스레드 취소 방법

    • 비동기식 취소 (asynchronous cancellation)
      • 한 스레드가 즉시 목적 스레드를 강제 종료시킴
    • 지연 취소 (deferred cancellation)
      • 목적 스레드가 자신이 종료되어야하는지 지속적으로 점검
pthread_t tid;

pthread_create(&tid, 0, worker, NULL);

pthread_cancel(tid);
  • 대상 스레드를 취소하라는 요청을 호출해도, 실제 취소는 요청을 처리하기 위해 대상 스레드가 설정되는 방식에 달려있다.

  • 취소가 비활성화되어 있으면 스레드를 취소할 수 없다.

  • 기본 타입은 지연 취소

    • 취소는 스레드가 취소점에 도달할때 발생
    • 정리 핸들러가 호출됨

스레드-로컬 저장장치 (4.6.4)

  • Thread-local storage (TLS)는 각 스레드가 자신만 액세스할 수 있는 데이터를 가짐
  • 스레드 생성 절차에서 제어가 없을 때 유용
  • 지역 변수와는 다름
    • 지역변수는 하나의 함수가 호출될 때만 보이고
    • TLS은 전체 함수 호출에 걸쳐 보임
  • 정적 데이터와 유사
    • 스레드 별로 고유

Scheduler Activations

  • M:M 관계와 Two-leve 모델에서는 으용에서 할당된 커널 스레드의 적절한 번호를 유지하기 위한 통신이 요구됨
  • LWP (경량 프로세스) : 사용자와 커널 스레드 사이에 둔 자료구조
  • upcall
    • 커널이 응용에게 특정 이벤트를 알려주는 프로시저
    • upcall 처리기에서 실행