세종대학교 - 운영체제 및 보안 수업
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)
- 목적 스레드가 자신이 종료되어야하는지 지속적으로 점검
- 비동기식 취소 (asynchronous 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 처리기에서 실행
'학교수업 > 운영체제' 카테고리의 다른 글
운영체제 및 보안 - Lecture 6. Process synchronisation (0) | 2025.04.19 |
---|---|
운영체제 및 보안 - Lecture 5. cpu scheduling (0) | 2025.04.19 |
운영체제 및 보안 - Lecture 3. Process Concept (0) | 2025.04.14 |
운영체제 및 보안 - Lecture 2. System structures (3) | 2025.04.13 |
운영체제 및 보안 - Lecture 1. Introduction (2) | 2025.04.13 |