본문 바로가기
CS 지식/운영체제 (OS)

[운영체제] OS의 스레드 종류(User, Kernel)와 관련 아키텍처 소개

by 코딩하는 동현 2025. 4. 13.

운영체제 스레드 (Threads)

Multithreaded Server 아키텍처

서버는 accept()라는 블로킹 함수를 실행해서 대기하다가, 요청을 받으면 소켓을 할당을 해서 read()라는 블로킹 함수로 읽을 수 있다. 서버는 클라이언트 연결요청을 기다린다. 클라이언트가 보낸 요청을 읽어서 수행한다.

  1. request는 이미 연결이 성공 후 보내는 것이다.
  2. 서버는 read()함수를 이용해서 서버가 처리를 해준다. → task → 별도의 스레드로 테스크를 할당한다.
  3. accept(), read()는 동시에 처리해도(순서가 지켜지지 않아도) 되므로 메인 스레드는 항상 accept()하면서 concurrent하게 진행이 된다.

장점

  • Responsiveness: 프로세스의 일부가 block 되더라도 다른 task는 진행된다. 특히 User Interface 부분에서 중요하다.
  • Resource sharing: 여러 스레드들이 그 프로세스의 리소스를 공유해서 스레드 간의 협력을 더욱 간단하게 할 수 있다.
  • Economy: 하나의 프로그램을 멀티 프로세스로 구성한것이 아닌, 한 프로세스로 구성할 수 있다. OS 입장에서는 한 프로세스만 실행하는것이 더 경제적이고 thread switching이 context switching보다 overhead가 더 작다.
  • Scalability: 하나의 프로세스가 멀티 프로세서 아키텍처의 CPU의 이점을 활용할 수 있다.

Multicore Programming

멀티코어는 CPU 칩이 1개이지만, 안에 다중 코어가 있어서 멀티 태스크를 실행할 수 있다. 멀티 프로세서는 CPU 칩 자체가 여러 개이다.

concurrent보다 더 좋고 어려운 parallel 프로그래밍을 할 수 있다.

  • Parallelism: 동시에 한 개 이상의 태스크를 실행하는 것 (HW에서 지원 필요)
  • Concurrency: 여러 개의 프로세스와 스레드를 실행하는 것

병렬성의 어려움

  • Dividing activities: 병렬로 처리할 태스크 산정
  • Balance: 균형 있게 스케줄링
  • Data splitting: 데이터 분할하고 처리
  • Data dependency: 데이터 간 의존성
  • Testing and debugging: 테스트, 디버깅 더욱 어려움

병렬성의 분류

  • Data parallelism: 데이터를 분할해서 동일한 task를 적용
  • Task parallelism: 여러 개의 unique한 task를 스레드로 병렬 실행

CPU 코어 = HW threads


Single and Multithreaded Process

  • 장점: code, data, files를 공유할 수 있다.
  • 단점: 프로그래머가 조심해야 한다.
    • OS가 해결 못해주므로, OS의 lock 기능을 활용해서 직접 해줘야 한다.

Amdahl's Law

앱이 순차적으로 실행되는 부분, 병렬적으로 실행되는 부분이 나눠져 있을 때, 코어 수가 증가함에 따라 성능이 얼마나 증가할지를 수식으로 표현함.

  • S: 순차적 비율 (%)
  • N: 코어 갯수

코어가 증가하면 성능 증가. 병렬 부분이 많을수록 효과가 크다. 점점 1/S에 수렴함.

요즘 멀티코어 시스템에는 안 맞을 수도 있다(HW 발전으로 인해). 그래서 수식이 변경되어 보통 사용한다.


User Threads and Kernel Threads

스레드는 유저 스레드커널 스레드로 나뉜다.

  • User threads: 라이브러리에서 관리. kernel은 이 스레드의 존재를 모른다.
    • POSIX Pthreads
    • Windows threads
    • Java threads
  • Kernel threads: 커널에 의해 지원

Multithreading Models

유저 스레드 - 커널 스레드 관계

  • Many-to-One
  • One-to-One
  • Many-to-Many

Many-to-One

여러 개의 유저 레벨 스레드가 하나의 커널 스레드에서 실행됨. 퍼포먼스 낮고, 하나라도 block되면 전부 block.

 


One-to-One

직접 만든 유저 스레드가 즉 커널 스레드가 됨. 성능은 좋지만, OS에게 부담이 된다.

 

HW의 발전으로 현재 여러 OS에서 사용되는 모델

  • windows
  • Linux
  • Mac OS
  • Solaris 9 이후

Many-to-Many

유저 스레드가 많고, 이들을 커널 스레드보다 더 많이 구성. 동적 매핑 가능.

 

  • Solaris prior
  • windows : ThreadFiber

Two-level Model

M:M + 1:1이 섞여 있음.


Thread Libraries

  • User-space 관리 라이브러리
  • OS 지원 kernel-level 라이브러리

 

Pthreads (C언어)

유닉스 계열에서 공통 사용. user-level과 kernel-level 모두 지원.

// 스레드 생성
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                   void *(*start_routine)(void*), void *arg);
// 스레드 종료
void pthread_exit(void *retval);
// 스레드 종료 대기 (join)
int pthread_join(pthread_t thread, void **retval);
// 스레드 속성 초기화
int pthread_attr_init(pthread_attr_t *attr);
// 스레드 속성 해제
int pthread_attr_destroy(pthread_attr_t *attr);
// 스케줄링 범위 얻기 (PCS or SCS)
int pthread_attr_getscope(const pthread_attr_t *attr, int *scope);
// 스케줄링 범위 설정
int pthread_attr_setscope(pthread_attr_t *attr, int scope);

 

Java Threads

JVM 상에서 관리되며, OS-level thread와 매핑 가능.

// Runnable 인터페이스: 실행 가능한 작업을 정의
public interface Runnable {
    public abstract void run();
}

// 스레드 생성
Thread thread = new Thread(new RunnableClass());

// 스레드 시작
thread.start();

// 스레드 종료 대기 (join)
thread.join();

// 스레드 상태 확인
thread.isAlive();

// 현재 실행 중인 스레드 반환
Thread.currentThread();

// 스레드 이름 설정/조회
thread.setName("MyThread");
String name = thread.getName();

Implicit Threading

프로그래머 대신 라이브러리가 스레드 관리 책임을 가지도록 함.

  • Thread Pools
  • OpenMP
  • Grand Central Dispatch
  • TBB
  • java.util.concurrent 패키지

Threading 정책의 Issues

Semantics of fork() and exec()

  • 다중 스레드에서 fork()하면 구현마다 다르다.
  • 단, exec()는 프로세스 자체를 대체하므로 무관하다.

Signal Handling

  • default handler 또는 user-defined handler 존재.
  • 멀티 스레드에서는 어떤 스레드가 시그널 처리할지 고려 필요.

Thread Cancellation

  • Asynchronous cancellation: 즉시 취소
  • Deferred cancellation: 연산 완료 후 취소

Thread-local Storage (TLS)

  • 스레드 간 고유 데이터 유지
  • 스레드 풀 사용: 일반 지역변수와는 달리 여러 함수에서 공유 가능 (단, 스레드 한정)
  • TLS는 스레드의 여러 함수 간에서 접근 가능하다.
    • 일반 함수의 지역변수는 단일 함수에서만 접근 가능하다.
    • static 데이터와 비슷하나, 해당 스레드에만 유효(unique)한 면에서 다르다.

Scheduler Activations

M:M 매핑관계나 Two-level model을 쓰는 아키텍처에서는 어느 쪽에 매핑, 어느 시점에 매핑하는지 이슈가 있기 때문에 communication이 필요하다.
중간 단계 자료구조인 lightweight process(LWP)를 사용한다.

user 스레드는 직접 CPU를 볼수 없으나, LWP라는 가상의 CPU 를 통해서 이용한다.

 

얼마나 많은 LWP를 생성할지도 이슈이다.


Scheduler Activations(스케쥴 활성화)를 할때 커널에서 부터 유저레벨에게 전달, 호출하기 위해서 upcalls(콜백)을 통해서 upcall handler로 간다

M:M 모델은 중간에 매핑관계가 바뀔수도 있다.
upcall을 통해서 알아서 유저영역에게 스케쥴링을 시키거나  또는 매핑을 바꾸라고 지시한다. -> LWP 생성 또는 재배정

반응형

댓글