StudyRepository
article thumbnail
Published 2023. 4. 10. 15:24
4. 스레드와 멀티스레딩 운영체제
728x90

 

 

 

 

 

 

01 프로세스의 문제점

 

 

 

 


프로세스 생성 오버헤드


- 프로세스를 위한 메모리 할당, 부모프로세스로부터 복사
- PCB 생성, 매핑 테이블(페이지 테이블) 생성 등

 

 

 


프로세스 컨텍스트 스위칭 오버헤드


- CPU 레지스터들을 컨텍스트로 PCB에 저장,
새 프로세스 컨텍스트를 PCB에서 CPU로 옮기는 시간
- CPU가 참고할 매핑 테이블(페이지 테이블)의 교체 시간
- CPU 캐시에 새 프로세스의 코드와 데이터가 채워지는데 걸리는 시간 등

 

 

 

 

프로세스 사이 통신의 어려움


- 프로세스가 다른 프로세스의 메모리에 접근 불가
- 프로세스 사이의 통신을 위한 제 3의 방법 필요
- 커널 메모리나 커널에 의해 마련된 메모리 공간을 이용하여 데이터 송수신
신호, 소켓, 메시지 큐, 세마포, 공유메모리, 메모리맵 파일 등
- 코딩이 어렵고, 실행 속도는 느리고, 운영체제 호환성 부족

 

 

 

 

 

 

02 스레드의 출현

 

 


프로세스 기반 멀티태스킹의 문제점


- 커널의 부담 증가(처리시간, 메모리 공간) -> 시스템 전체 속도 저하

 

 


효율적인 새로운 실행 단위: 스레드


- 프로세스보다 크기가 작아 생성과 소멸이 빠름
- (스레드) 컨텍스트 스위칭이 빠름
- 통신이 용이함

 

 

 

 

 

 

03 스레드의 특징

 

 

 


실행 단위, 스케쥴링 단위로서의 스레드


- 실질적인 응용 프로그램 작업의 단위
> 하나의 응용 프로그램에 여러 작업(스레드) 작성 가능
- 운영체제 입장에서는 실행 및 스케쥴링 단위
- 스레드의 정보를 저장하는 구조체 TCB(Thread Control Block)가 존재함

 

 


컨테이너로서의 프로세스


- 프로세스는 스레드들의 컨테이너로서의 역할을 수행
- 하나의 프로세스에는 반드시 1개 이상의 스레드가 포함됨
> 프로세스가 생성될 때 운영체제에 의해 자동으로 1개의 스레드를 생성함
> 메인 스레드(main thread)라고 부름

 

 

 

 

 

 

04 프로세스와 스레드

 

 


프로세스: 회사, 스레드: 직원 

 

- 직원은 회사의 목적을 위해 일을 함

 

 

 

 

프로세스는 스레드들의 공유 환경을 제공 

 

- 모든 스레드는 프로세스의 코드, 데이터, 힙공간을 공유함
> 공유 공간을 이용하여 통신을 용이하게 함
- 각 스레드는 프로세스의 스택 공간을 나누어 사용함

 

 

 

함수 기반 스레드 운용


- 함수를 실행할 스레드를 운영체제에게 요청하여 생성
- 운영체제는 TCB를 생성하며, 함수의 주소를 스레드 실행 시작 주소로 TCB에 등록

 

 


스레드 스케쥴링


- 스레드 단위로 스케쥴링 수행, TCB중에서 하나를 선택
- TCB에 기록된 스레드의 시작 주소를 CPU에 적재하여 실행 시작

 

 


스레드 종료


- 함수가 종료되면 스레드도 종료됨
- 스레드 종료 후 TCB 등 스레드와 관련한 모든 정보가 제거됨
- 프로세스에 속한 모든 스레드가 종료되면, 프로세스도 종료됨

 

 


05 pthread


- POSIX Thread
- 유닉스 / 리눅스 계열 POSIX 시스템에서 제공하는 스레드 구현 API

 

 

pthread 기반 스레드 구현 예제 (1)


main() 함수
- main 스레드 코드
- calcThread를 생성하여 1-100 까지의 합을 구함
- calcThread 종료 후, 합(전역 변수 sum)을 출력


calcThread() 함수
- 합을 수행하는 스레드 코드
- 정수를 매개변수로 받아서 1부터 매개변수까지의 합을 구하여 전역 변수 sum에 저장


전역 변수 sum
- calcThread 와 main 스레드 모두 접근 가능

 

 

 

#include <pthread.h> // pthread 라이브러리를 사용하기 위해 필요한 헤더 파일
#include <stdio.h>
#include <stdlib.h>
void* calcThread(void *param); // 스레드로 작동할 코드(함수)
int sum = 0; // main 스레드와 calcThread가 공유하는 전역 변수
int main() {
pthread_t tid; // 스레드의 id를 저장할 정수형 변수
pthread_attr_t attr; // 스레드 정보를 담을 구조체
pthread_attr_init(&attr); // 디폴트 값으로 attr 초기화
pthread_create(&tid, &attr, calcThread, "100"); // calcThread 스레드 생성
// 스레드가 생성된 후 커널에 의해 언젠가 스케줄되어 실행
pthread_join(tid, NULL); // tid 번호의 스레드 종료를 기다림
printf("calcThread 스레드가 종료하였습니다.\n");
printf("sum = %d\n", sum);
}
void* calcThread(void *param) { // param에 "100" 전달 받음
printf("calcThread 스레드가 실행을 시작합니다.\n");
int to = atoi(param); // to = 100
int i;
for(i=1; i<=to; i++) // 1에서 to까지 합 계산
sum += i; // 전역 변수 sum에 저장
}

 

 

 

 

프로세스 생성 후 자동으로 main 스레드 생성
- main() 함수를 통해 스레드 코드 실행


calcThread() 스레드 생성
- pthread_create() 등 라이브러리 함수와 System Call을 통해 생성


스레드마다 TCB 1개 생성
- 스레드 컨텍스트 스위칭이 발생하는 경우 TCB에 컨텍스트 정보를 저장


스레드 공유 공간
- main 스레드와 calcThread는 전역 변수 sum을 공유함

 

 

 

 

 

 

 

06 Concurrency와 Parallelism

 

 

 

 

 


Concurrency(동시성)


- 1개의 CPU에서 2개 이상의 스레드를 실행 중인 상태
- 스레드가 입출력으로 실행이 중단되면 다른 스레드를 실행함
- 타임 슬라이스를 기준으로 CPU를 할당하여 스레드를 번갈아가면서 실행

 

 

 

 

Parallelism(병렬성)


- 2개 이상의 스레드를 다른 CPU에서 같은 시간에 동시 실행

 

 

 

07 스레드 주소 공간

 


스레드 코드 영역
- 프로세스 코드 영역에 존재함
- 스레드가 실행할 함수가 포함됨
- 스레드는 프로세스의 코드 영역에 있는 다른 모든 함수를 호출할 수 있음


스레드 데이터 영역
- 프로세스의 데이터 영역에 있는 전역 변수를 공유함
- 스레드 로컬 스토리지(TLS, Thread Local Storage)
> 각 스레드에게 주어진 독립 전용 변수 공간
> static __thread 키워드 사용
> 프로세스 힙 혹은 스택 영역에 할당됨


스레드 힙 영역
- 스레드가 동적으로 할당받는 공간, malloc()을 호출하여 할당


스레드 스택
- 프로세스 스택을 나누어 스레드 사용자 스택 할당
- 커널 공간에 스레드 별 커널 스택 할당
- 스레드 종료 시, 스레드의 사용자 스택과 커널 스택 공간 반환

 

 

 

스레드 Life Cycle



- 스레드도 생성, 중단, 실행, 소멸의 여러 상태를 거치면서 실행됨
> 준비 상태(Ready): 스레드가 스케쥴되기를 기다림
> 실행 상태(Running): 스레드가 CPU에 의해 실행됨
> 대기 상태(Blocked): 스레드가 입출력을 요청, sleep() System Call 발생
> 종료 상태(Terminated): 스레드가 종료됨


- 스레드 상태는 TCB에 저장됨





스레드 컨텍스트 



- 스레드의 현재(실행중인) 상황
- CPU 레지스터에 저장됨






스레드 컨텍스트 정보 



- PC 레지스터: 실행중인 스레드 코드 주소 

- SP 레지스터: 실행중인 스레드 스택 주소

- 데이터 레지스터: 스레드 관련 정보 저장 

- TCB에 저장










스레드 제어 블록, TCB(Thread Control Block)

- 스레드에 관한 정보를 담은 구조체
- 스레드 엔티티(Thread Entity) 혹은 스케쥴링 엔티티(Scheduling Entity)라고 함
- 커널 영역에 생성되며 커널에 의해 관리됨











스레드 컨텍스트 스위칭 발생 경우



스레드가 자발적으로 다른 스레드에게 양보
- yield() 등의 시스템 호출(혹은 라이브러리 호출)을 통해


스레드가 시스템 호출을 실행하여 블록되는 경우
- read(), sleep(), wait() 등 I/O가 발생하거나 대기할 수 밖에 없는 경우


스레드의 타임 슬라이스(시간 할당량)를 소진한 경우
- 타이머 인터럽트에 의해 진행


I/O 장치로부터 인터럽트가 발생한 경우

- 현재 실행중인 스레드보다 높은 순위의 스레드가 I/O 작업을 끝낸 경우 등






스레드 컨텍스트 스위칭 과정

스레드 컨텍스트 스위칭(스레드 스위칭)


- 스레드 스케쥴링 후,
- 현재 실행중인 스레드를 중단하며, 선택된 스레드에게 CPU를 할당함
> CPU 레지스터에 저장된 스레드 컨텍스트를 TCB에 백업함
> 선택된 스레드 TCB의 컨텍스트 정보를 CPU 레지스터에 적재함

 

 

 

 

 

 

 

 

컨텍스트 스위칭 오버헤드

 

 

 


스레드 컨텍스트 스위칭 오버헤드 (동일한 프로세스의 스레드로 스위칭)


- 컨텍스트 정보 백업 및 복구
> CPU의 컨텍스트(PC, SP, 데이터 레지스터 등)를 TCB에 백업
> TCB로부터 실행할 스레드의 컨텍스트 정보를 CPU에 복구
- TCB 리스트 수정
- 캐시 플러시 및 채우기

 

 


프로세스 컨텍스트 스위칭 오버헤드 (다른 프로세스의 스레드로 스위칭)


- MMU(Memory Management Unit) 내에 현재 실행 중인 프로세스의 매핑 테이블을

새로운 프로세스의 매핑 테이블로 교체

- CPU의 TLB(Translation Look-aside Buffer) 교체
- CPU 캐시의 코드와 데이터를 교체
> 새 프로세스이므로 초반에는 CPU 캐시 미스(miss) 발생
> 캐시가 채워지는데 상당한 시간이 소요됨



 

 

 

커널 레벨 스레드와 사용자 레벨 스레드

 


커널 레벨 스레드(Kernel-Level Thread)
- 스레드의 정보(TCB)가 커널 공간에 생성되며 커널에 의해 소유됨
- 응용프로그램이 System Call을 통해 커널 레벨 스레드를 생성
- main 스레드는 커널 스레드임
> 프로세스 생성 시 커널이 자동으로 main 스레드를 생성함
> main 스레드의 TCB는 커널에 생성됨

 


사용자 레벨 스레드(User-Level Thread)
- 응용 프로그램이 라이브러리 함수를 호출하여 사용자 레벨 스레드를 생성
- 스레드 라이브러리가 스레드 정보(U-TCB)를 사용자 공간에 생성
> 커널은 사용자 레벨 스레드의 존재를 알지 못함
- 스레드 라이브러리에 의해 스케쥴됨

 

 

 


순수 커널 레벨 스레드(Pure Kernel-Level Thread)

- 부팅 때부터 커널의 기능을 돕기 위해 만들어진 스레드
- 커널 코드를 실행하는 커널 스레드
- 스레드의 주소 공간이 모두 커널 공간에 형성됨
- 커널 모드에서 작동됨

 

 

 

멀티 스레드 구현


3가지 구현 방식


- N:1 매핑
> N개의 사용자 레벨 스레드를 1개의 커널 레벨 스레드로 매핑


- 1:1 매핑
> 1개의 사용자 레벨 스레드를 1개의 커널 레벨 스레드로 매핑


- N:M 매핑
> N개의 사용자 레벨 스레드를 M개의 커널 레벨 스레드로 매핑

 

 

 

 

 

N:1 매핑 개념
- 모든 스레드를 단일 스레드로 다룸
- 커널은 프로세스 당 1개의 커널 레벨 스레드를 생성
- 프로세스의 모든 사용자 레벨 스레드(N개)가 1개의 커널 스레드에 매핑
- 사용자 레벨 스레드는 스레드 라이브러리에 의해 스케쥴되고 스위칭 됨

 

 

 

장점
- 단일 코어 CPU에서 멀티 스레드 응용 프로그램 실행 속도가 빠름
> 스레드 생성, 스케쥴, 동기화를 위해 커널에 진입할 필요없이 사용자 공간에서만 운영되므로


단점
- 멀티 코어 CPU에서는 비효율적
> 프로세스에 속한 여러 사용자 레벨 스레드의 병렬 처리 불가
- 하나의 사용자 레벨 스레드가 블록되면 프로세스 전체가 블록됨
> 프로세스 내 다른 사용자 레벨 스레드로 스위칭되지 못함

 

 

 

 

1:1 매핑 개념
- 사용자 레벨 스레드 당 1개의 커널 레벨 스레드를 생성
- 사용자 레벨 스레드는 매핑된 커널 레벨 스레드가 스케쥴될 때 실행

 

 

장점
- 개념이 단순하여 구현이 용이함
- 멀티 코어 CPU에서 멀티 스레드 응용프로그램에게 높은 병렬성을 제공함
- 하나의 사용자 레벨 스레드가 블록되어도 프로세스 전체가 블록되지는 않음


단점
- 사용자 레벨 스레드가 많아지면 커널의 부담이 크게 증가함

 

 

 

N:M 매핑 개념
- N개의 사용자 레벨 스레드를 M개의 커널 레벨 스레드에 매핑

 

 

장점
- 1:1 매핑과 비교하여 커널 엔티티 개수가 적어서 커널의 부담이 작아짐


단점
- 구현하기 복잡함 (현대의 운영체제에서는 거의 사용되지 않음)

 

 

 

멀티 스레딩의 장점


높은 실행 성능
- 병렬 실행


사용자에 대한 우수한 응답성
- 한 스레드가 블록되어도 다른 스레드를 통해 사용자와의 입출력 가능


서버 프로그램의 우수한 응답성
- 웹 서버나 파일 서버에 대한 많은 사용자들의 동시 접근 처리 용이

- 병렬적으로 서비스하는데 우수함


시스템 자원 사용의 효율성

- 스레드는 프로세스에 비해 생성, 유지 시 메모리나 자원 적게 사용


응용프로그램 구조의 단순화
- 작업 기준으로 응용프로그램을 여러 함수로 분할하고, 각 함수 별로 스레드를 만들어 동시 실행
- 새로운 기능 추가 용이, 프로그램의 높은 확장성

 

작성이 쉽고 효율적인 통신

 

 

 

 

 

 

멀티 스레딩 구현 시 주의 사항

 


스레드가 fork()를 호출한 경우
- 새로 생성된 프로세스는 fork()를 호출한 스레드로만 구성됨


스레드가 exec()를 호출한 경우
- 현재 프로세스에 새 응용프로그램이 적재되어 모든 스레드가 사라지게 됨


스레드 사이의 동기화(Synchronization)
- 프로세스의 공유 데이터를 여러 스레드가 접근하는 경우 데이터 불일치 발생

728x90
profile

StudyRepository

@Minseo26262

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!