운영체제 Chapter 4 요약

Abraham Silberschatz Operating system 10th Ed.

By Hank Kim

[Chapter 4]

I. 스레드 개념

A. 프로세스 문제점

1. Heavy-weight: 프로세스는 address space, PCB등을 저장해야하기 때문에 커널이 해야하는 일 이 많아지고 메모리를 많이 차지한다.

그리고 IPC를 할때 커널의 개입이 필요해서 성능이 떨어진다.

문제를 해결하기 위해서 만든것이 선마이크로소프트의 lightweight process(lwp). 프로세스를 실행상태(주소공간,pc,sp 등)와 분리한다.

POSIX표준화 과정을 거쳐서 만들어진 것이 스레드이다. 프로세스를 따로 만들지 않고 하나의 프로세스 안에서 여러개의 path로 동작하게 한다. 스레드라고 불리는 이유는 path를 실처럼 표현하기 때문이다.

B. 멀티스레드

1. 스레드 생성: 싱글 스레드는 함수1 수행 후 함수2를 수행하는 순차적인 형식. 멀티스레드는 스레드를 함수단위로 구현한다. 함수1, 함수2를 pthread_create()와 같은 시스템콜의 인자로 넘겨줘서 스레를 생성해서 병렬적으로 동작하는것처럼 구현한다. 2개의 함수의 스레드를 만 들면 메인 스레드를 포함해서 3개의 스레드가 존재하는 것이다. 보통 멀티스레드 환경에서는 스레드로 만들 함수들을 무한루프로 구현한다.

pthread는 POSIX표준을 따르는 스레드와 관련된 라이브러리이다. pthread_create, pthread_exit, phtread_join 등의 함수가 존재한다. join은 멀티프로세스에서 동기화를 위해서 사용한 wait과 동일하다.

2. 차이점: 코드세그먼트와 data 세그먼트, 파일IO는 스레드끼리 공유해서 사용하지만, 특정 함 수를 스레드로 만드는 것이므로 스레드별로 다른 데이터를 관리하기 위해서 스택을 따로 가 지고 있다. 코드섹션은 공유하지만 수행되어야하는 instruction의 위치가 다르기 때문에 레지 스터값과 스택 포인터의 값도 스레드별로 다르다. 즉 스레드마다 레지스터 정보와 스택 영역 은 독립적이다.

3. 주소공간: 코드 세그먼트, 데이터 세그먼트, 힙은 공유하고 스택의 위에서부터 스레드가 쌓인 다. 스레드 사이에는 가드영역이 존재하고 운영체제가 그 크기를 지정한다. 각각의 스레드가 수행되다가 멈췄을때의 context를 스택이나 커널단에 저장해서 관리한다.

4. 예시:

● 웹서버: 멀티 프로세스에서는 fork로 새 프로세스를 생성해서 request를 처리했으 나 단순한 작업을 위해서는 overkill이다. 자식 프로세스에서 하던 작업을 함수로 만들고 request가 올 때마다 스레드를 새로 생성해서 request를 처리하도록 하면 동일하게 동작한다. 멀티프로세스에서는 부모 프로세스가 소켓연결을 끊고 다시 request를 기다리는데, 스레드에서는 한 프로세스 내에서 소켓을 유지해야 하므로 처리가 끝났을때 소켓을 닫는다.

멀티프로세스를 이용하는 예시로는 리눅스 아파치가 있다.

윈도우는 IIS라는 웹서버가 채택되어있는데 스레드 개념이 나온 이후의 운영체제이 기 때문에 유닉스와 달리 프로세스 간 부모자식 개념이 없어서 멀티프로세스로 구

현되어있지 않다.

● 아두이노: 임베디드 장치는 제약된 하드웨어 리소스를 가진다.

운영체제도 RTOS(real time OS)이며 크기가 작고 멀티프로세스를 감당하지 못하기 때문에 멀티스레드를 사용하는게 좋고 아두이노 라이브러리에서 스레드 함수를 제 공한다. 사용할 수 있다. 예를 들어 led를 깜빡이면서 버저를 울리게 만들기 위해 서는 두 개의 스레드가 필요하다.

아두이노는 main에서 setup함수를 한번 수행하고 loop 함수를 반복수행한다.

멀티태스크 작업할때는 보통 루프를 비워놓는다.

xTaskCreate함수를 통해서 Task를 인자로 주고 통해서 스레드를 생성하면 동시에 동작하는것처럼 수행된다.

5. 싱글프로세스/멀티프로세스/멀티스레드 비교

DoCmd 함수를 수행하는 형태를 비교한다.

싱글프로세스에서는 DoCmd함수를 수행하면 함수 수행이 끝날때까지 다음 작업을 수행할 수 없다.

멀티 프로세스 버전에서는 fork하면 새로운 프로세스가 생성되고 부모는 wait을 통해서 기다 리다가 자식이 exit을 수행하면 시그널을 받아서 wait상태에서 빠져나오고 다시 while루프가 수행된다.

스레드 버전에서는 POSIX표준 스레드 라이브러리인 pthread를 이용한다. pthread_t는 프로세 스에서 PCB와 같은 역할을 수행하는 스레드 정보를 저장할 수 있는 구조체이다. 구조체를 pthread_create의 첫번째 인자로 전달하고 두번째 인자는 스레드의 Attribute를 설 정하는 것인데 null로 하면 디폴트 상태로 생성된다. 세번째 인자로 스래드형태로 수행할 함 수를 void포인터 형태로 넣어주고, 네번째는 함수를 수행하기 위한 파라미터이다. 파라미터는 포인터 변수여야 하고 하나만 전달 가능하기 때문에 구조체를 많이 사용함.

● 조건부 컴파일: #if 조건이 0이면 #endif까지의 코드를 수행하지 않고 1이면 수행하 도록 conditional compilation 한다. 디버깅할때 많이 쓰는 기능으로 주석을 추가했 다가 삭제할 필요 없이 디버그 모드는 1 릴리즈 모드는 0으로 처리한다.

C. Multicore Programming

1. 개념: 여러개의 코어에서 병렬적으로 연산을 처리한다. 싱글코어일때는 테스크가 여러개일 때 타임쉐어링으로 동작해도 순차적으로 동작하는 것과 같다. 멀티코어의 경우에는 하드웨어 상 의존성이 없는 테스크를 병렬적으로 수행해서 효율을 올린다. Parallelism에는 두 가지 종 류가 있다.

● Task Parallelism: 전체 데이터는 공유하고 테스크만 나눠서 동작한다. pthread라이브 러리가 해당한다.

● Data Parallelism: 딥러닝과 같은 각각의 독립적인 요소에 대한 데이터 연산이 필요 할때, 의존성은 없고 데이터만 다르면 될 경우 데이터별로 나눠서 병렬적으로 수행 한다 이미지 필터링의 경우 데이터를 조각으로 나눠서 병렬적으로 연산을 수행하고 나중에 합쳐서 결과를 만들어낸다. NUMA시스템같은 기상청 슈퍼컴퓨터에 많이 사 용된다. GPGPU, CUDA, OpenCL등의 라이브러리가 있다.

II. 스레드의 형태

A. 구현 방식

1. 유저 스레드: 한 프로세스에서 여러개의 path가 존재하도록 하려면 커널을 수정하는 방식과 어플리케이션 단에서 동작하도록 하는 방식이 있다. 유저 스레드는 라이브러리 형태로 스레드가 어플리케이션 단에서 동작하도록 구현한 것이다.

프로세스 안에 런타임 시스템을 만들고 스레드를 관리한다. 프로세스에서 어떤 스레드가 동작하 고 있는지 테이블을 통해서 관리하고 런타임 시스템이 스케줄링한다. 커널은 스레드에 대한 정 보가 없다. 커널의 오버헤드가 준다는 장점이 있다.

하지만 유저 스레드 방식은 큰 문제가 있다. 여러개의 스레드가 동작하고 있는데 첫번째로 런타 임에 의해 선정된 스레드가 blocking IO요청을 한다면 다른 스레드는 수행되지 못하고 CPU에서 내려와야 한다. OS에게 스레드에 대한 정보가 없기 때문에 발생하는 문제이다. 2. 커널 스레드: 커널에서 프로세스와 스레드 테이블을 만들어서 관리하는 형식이다. 커널 스레드 형식이 현재 많이 사용되는 형식이다. 스레드 스케줄링 등을 훨씬 최적화해서 운영할 수 있다. 오버헤드가 많지만 하드웨어가 발전하면서 많이 사용하게 되었다.

B. 멀티스레딩 모델

3. Many-to-One: 하나의 커널 스레드에 여러개의 유저 스레드. 커널의 오버헤드를 줄이기 위해 서 커널 스레드를 줄인 형식이다. 위의 유저 스레드 형식이 구현된 것이다.

4. One-to-One

5. Many-to-Many: 커널 스레드가 구현된 형식

6. Two-level: Many-To-One과 One-to-One을 둘 다 사용하는 방식

C. 스레딩 이슈

1. 두 가지 버전의 fork 문제: 멀티 프로세스 멀티 스레드를 혼용할때, 하나의 프로세스에서 5개 의 스레드가 동작하고 있는데 5번 스레드에서 fork를 수행하는 경우, 두가지 경우의 수가 존재한 다. 하나는 스레드를 하나 더 만드는 방식이고 하나는 프로세스를 그대로 복제하여 만드는 방식 이다. 상황에 따라 어떤 방식이 좋은지가 다르다. 불문율로써 fork가 필요하면 fork를 먼저 하고 스레드를 만들기로 정해졌다.

2. 스레드들 각각 따로 가지는 데이터를 다른 스레드에서 접근 가능하게 할 것인가? 그렇게 했 을때의 보안상 이슈는?

3. 시그널을 프로세스 레벨에서 처리할 것인가 스레드 레벨에서 처리할 것인가 등. D. Pthreads(Posix Threads)

1. 스레드 생성/종료: phtread_create, pthread_exit, pthread_join

2. Mutex 함수: 멀티스레드가 동작할때 프로세스 안에서 공유하는 자원들에 접근할때 생기는 데 이터 왜곡 및 동기화 문제를 해결하는 방법이다. 6장에서 자세히 기술한다.

E. Thread Design Space

1. MS/DOS는 Batch 시스템이므로 하나의 프로세스와 하나의 스레드를 가진다.

2. 초기 유닉스는 하나의 프로세스 당 하나의 스레드가 존재했다. 프로세스는 여러개 존재. 3. Java같은 경우는 덩치가 큰 프로세스가 하나 올라가고 가상머신이 실행시키는 프로그램이 스 레드 형태로 동작한다. 하나의 프로세스에 여러개의 스레드가 존재하는 것.

4. 현재의 Linux, Mach등 대부분의 OS는 여러개의 프로세스에 여러개의 스레드가 존재하는 형 태이다.

Tags: OS
Share: Twitter Facebook LinkedIn