Threads_운영체제(7)

조권휘·2023년 1월 3일
0

운영체제

목록 보기
7/14

Why Threads?

  • Process는 많은 정보를 포함하기 때문에 hevey-weight하다.
    • Address space : code, data pages
    • Hardware execution state(Context) : PCB 내부의 PC, SP, registers..
    • OS resources, accounting info. : open files..
  • 새로운 process를 create(fork)하는 것은 시간이 많이 걸리고 costly하다.
  • 여러 프로세스는 각자 독립적인 영역을 가지기 때문에 Inter-Process Communication하는 것이 Costly하다.

Cooperating Processes

  • web server같은 경우는 자기 자신의 clone을 만들어서 동시에 작업을 하고 multiprocessor 환경에서 병행적인 프로그램으로 활용한다.
  • data를 share하기 위한 같은 address space를 필요로 한다 – OS support shared memory
  • 이러한 여러 process들의 scheduling은 OS가 한다.
  • 이러한 scheduling을 자원, 시간 측면에서 비효율적이다.

Idea

  • fork해서 사용하는 process들은 대부분 code, data, heap, privilege, resources는 동일하다.
  • 각 process가 수행상태(hardware execution state)만 다르다.
  • 공통적으로 사용하는 것은 생성하지 말고 수행상태 정보만 관리하면 되는 것이다.
  • 수행상태를 분리 → Thread / lightweight process(LWP)
    • PC, SP, registers, stack, context..

Threads

  • program에서 실행되는 instruction들의 sequence를 의미한다.
  • PC, register, stack 등의 hardware context로 구성되어 있다.
  • thread끼리는 instruction과 data(global variable)를 공유한다.
  • global variable이 변화가 생긴다면 다른 thread들도 알 수 있다.
  • thread들은 대부분의 OS state를 공유한다.

Processes vs Threads

  • process는 덩치가 크고 비용, 시간이 많이 들지만 thread는 덩치가 작고 비용, 시간이 적게 든다.
  • process는 여러개의 threads를 가질 수 있다.
  • thread끼리 data를 공유하는 것은 cheap하다.
  • process가 아닌 thread가 scheduling의 단위가 된다. (kernel-level threading인 경우)
  • process는 static하지만 thread는 dynamic하다.

Multi-threading Benefit

  • concurrency하게 이용하기 쉽다.
  • Improve program structure → Modular
  • Throughput
    • computation과 I/O를 overlap할 수 있다.
  • Responsiveness : 응답성이 좋아진다.
  • 자원을 쉽게 공유할 수 있다.
  • 경제적이다.
  • multiprocessor(core) architectures 성능 향상

Process Address Space – Multi thread

  • 각 T1, T2, T3는 code를 공유하며 각자의 HES를 가진다.

Threads Interface

Pthread

  • POSIX에서 제안한 API
  • Implementation이 아닌 specification이다. → Linux, windows 사용 방식이 다를 수 있다.
  • #include <pthread.h>를 이용하여 라이브러리를 사용한다.
  • gcc library libpthread.a : -lpthread
  • pthread_join : thread의 wait과 같은 것
  • *attr : 보통 NULL

pthread_join

  • 생성한 thread가 돌아오기를 기다리는 것

Mutexes & Condition variables

  • wait을 하기 위해서는 mutex를 항상 지정해줘야 한다.
  • mattr는 사용하지 않으면 NULL이 가능하지만 mutex는 object를 줘야하고 NULL은 안된다.

Threading Issues

fork() and exec()

  • thread가 fork call을 한 경우
    • Pthread : fork를 호출한 thread 1개만 복제를 한다.
    • Unix International standard
      • fork() : 모든 thread를 복제
      • fork1() : fork를 호출한 thread만 복제
  • exec() : 기존 thread를 모두 제거하고 다시 새로운 process가 되어 1개의 single thread로 된다.

Thread cancellation

  • thread가 완료되기 전에 종료를 하는 것

1) Asynchronous cancellation

  • thread를 즉시 종료시킨다.
  • 좋은 방법은 아님

2) Deferred cancellation (default)

  • thread를 즉시 종료하지 않고 safe한 곳까지 대기했다가 종료시킨다.
  • cancellation point : safety zone
  • pthread_testcancel() : cancellation point에서 함수 호출을 하면 된다.
  • cancel이 일어나면 cleanup_handler가 작동하여 처리를 한다.(자원 회수 등..)

3) cancellation func

  • int pthread_setcancelstate(int state, int *oldstate);
  • int pthread_canceltype(int type, int *oldtype);
  • oldstate, oldtype을 지정하지 않는다면 NULL을 이용한다.

Signal handling

  • 만일 signal이 전달되었을 때 누구한테 전달을 할 것인가?
    1) 1개의 대표 thread를 정하고 모든 signal을 해당 thread에 전달 : solaris 2
    2) 모든 thread에 signal을 전달
    3) signal mask block을 통해 per-thread 방식

Thread-Local Storage

  • TLS : thread에서 각 함수에서 만들어진 data를 개별로 저장하는 local 공간
  • thread pool : NULL thread를 만들어서 모아둔 공간
  • NULL thread : OS가 모든 자원을 다 제공했지만 함수가 주어지지 않은 빈 thread
  • function의 local variable과는 다른 개념으로, local variable은 function이 끝나면 사라진다. 하지만 Thread-local storage에 저장된 data는 thread가 끝날 때까지 사라지지 않는다.

Using libraries

  • errno : 각 thread는 각자의 errornum을 가지고 있다.
  • Multi thread-safe(MT-safe, reentrant function) : global variable을 사용하지 않고 local variable만 이용해서 안전하게 사용을 하는 방식
  • 만일 global variable을 사용해야한다면 critical section으로 지정해서 lock을 하며 MT-safe 방식을 지켜야한다.

Easy sharing but error-prone

  • thread에서는 공유가 쉬워졌지만 문제가 발생할 확률이 높아졌다.
  • 따라서 공유 변수를 최소화해야한다.

Kernel/User-level Threads

  • thread create/manage를 OS가 할 것인가 User가 할 것인가?
  • OS : system call을 이용하여 생성 및 관리
  • user : library를 이용하여 생성 및 관리
  • 여러 thread가 address space를 share하기 때문에 OS의 도움 없이 user가 관리해도 된다.

Kernel-level Threads

  • 모든 thread 관리를 OS가 한다.
  • thread 관련된 operation이 kernel에 구현되어 있다.
  • OS가 kernel에 있는 모든 thread를 scheduling을 한다. - scheduling의 최소 unit이 thread
  • thread operation이 모두 system call이기 때문에 protection boundary를 계속 지나기 때문에 너무 비싼 operation이 계속된다.
  • kernel space를 OS가 관리하는데, thread가 너무 많아지면 문제가 될 수 있다.

User-level Threads

  • thread를 조금 더 싸고 빠르게 만들기 위한 방식
  • portable하게 사용할 수 있다. 즉, 다른 system으로 library의 형태로 쉽게 porting할 수 있다.
  • 각 thread의 PC, register, stack을 user space에서 할당한다.
  • Run-time system : library라고 생각하면 된다.
  • kernel-level thread보다 최대 100배까지 빠르다.
  • user level에서 관리를 하다 보니 OS는 invisible하다. 예를 들어 1개의 thread가 I/O를 했어도 1개의 process로 인식해서 모든 thread가 block을 당할 수 있다.
  • 즉, OS가 잘못된 판단을 하는 것이 쉽다.

Implementing User-level Thread

Thread context switch

  • 현재 library에 관리하는 thread의 stack에 machine state를 저장하고 다음 thread의 context를 가져오고 하는 방식
  • 모든 것이 assembly language로 이루어져 있다.

Thread scheduling

  • library 내에 있는 thread scheduler가 thread들을 queue를 이용하여 관리한다.
  • OS가 scheduling을 하듯이 적용한다.

Non-preemptive scheduling

  • thread끼리 cooperative하게 작동을 하기 위해 사용한다.
  • alarm()과 같은 함수를 이용하여 user level에서 적용할 수 있다.

Threading Models

Many-to-One(N:1)

  • kernel thread 1개에 여러개의 user thread가 있는 방식

One-to-One(1:1)

  • 일대일 대응 방식으로, 주로 kernel level thread에 이용된다.

Many-to-Many(M:N)

  • user, kernel도 여러개인 형태
  • kernel이 돌아가면서 user과 대응하게 되고, 상황에 따라 kernel thread가 달라지는 형태

Linux Thread

  • Linux에서 기본 unit은 “task”로, process와 thread 모두 task로 불린다.
  • clone() system call을 이용하여 multi threading을 지원한다.
  • 별도의 task가 생성되며 이전 task와 어떤 부분을 공유할지 선택적으로 지정할 수 있다. - clone()
  • POSIX와 Linux는 공유관계에 대한 접근 방법이 다른 것이다.
  • NPTL 모델이 지원되고 있다.(1:1 model)

Java Thread

  • Runnable interface를 이용하여 class를 정의하고 thread class와 binding 등을 진행한다.
  • run()함수는 thread의 main function을 실행시키는 함수이다.

Creating / Destroying Mutexes

initialization

  • mutex는 초기에 unlocked 상태이다.
    1) static
  • pthread_mutex_t mymutex = THREAD_MUTEX_INITIALIZER
    2) dynamic
  • pthread_mutex_init(attr)

pthread_mutexattr_t : attr

  • NULL : default
  • protocol : inversion 문제 해결하기 위해 사용
  • prioceiling : priority ceiling 방법을 사용
  • process-shared : 서로 다른 process끼리 mutex를 사용할 때

Mutex Lock/Unlock

  • pthread_mutex_lock(mutex) : blocking operation
  • pthread_mutex_trylock(mutex) : non-blocking operation
  • pthread_mutex_unlock(mutex) : mutex 반납
    • error가 생기는 경우
      • 다른 thread가 이미 mutex를 가진 경우
      • mutex가 이미 unlock인 경우

synchronous call / Asynchronous call : blocking / non-blocking과 같은 말


Advisory Mutex & Mandatory Mutex

Advisory lock

  • synchronous의 책임이 programmer에게 있는 lock

Mandatory Mutex

  • OS가 지원하는 lock
  • file에 lock을 하는 경우(permission bit 이용)
  • 모든 접근에 대해 OS가 control을 해야하기 때문에 성능이 떨어질 수 있다.

Condition Variables

initialization

1) static

  • pthread_cond_t myconvar = PTHREAD_COND_INITIALIZER;

2) dynamic

  • pthread_cond_init (condition, attr);

Waiting / Signal on CVs

  • cond_wait시 mutex를 지정해야 제대로 작동한다.
  • cond_signal은 stack되지 않는다.
profile
안녕하세요 :) Data/AI 공부 중인 한국외대 컴퓨터공학부 조권휘입니다.

0개의 댓글