Chapter 4. 쓰레드 (Threads) Questions of the day 스레드가 공유하는 것과 공유하지 않는 것은? 스레드의 2 가지 수준은? 스레드 매핑 모델은? 운영체제
스레드 (Threads) 개요 프로세스 = 자원 + PC 스레드 : 새 PC (a thread of control)로 같은 address space를 실행하는 fork와 유사 경량 프로세스(LWP; lightweight process) = 스레드 CPU를 이용하는 기본 단위 thread ID, PC, 레지스터 세트, 스택 영역을 가짐 스레드들은 서로 같은 프로세스의 code section, data section, OS resources-open files, signals를 공유 (그림 4.1) (예1) web browser image와 text를 display하는 thread network에서 데이터를 가져오는 thread (예2) word processor graphics를 display하는 thread keystrokes를 읽어오는 thread spelling과 grammar를 검사하는 thread 중량 프로세스(heavyweight process) = 1 thread를 가진 task 비교 경량 프로세스의 문맥교환 : CPU switching, thread context switch 레지스터 세트 교환만 중량 프로세스의 문맥교환 : process switching, context switching 레지스터 세트교환과 메모리 관련 작업도(virtual memory page table 변경 등) 운영체제
Single and Multithreaded Processes 운영체제
Single and Multithreaded Processes 운영체제
스레드(Threads) 개요 제어 다중 스레드 제어(multiple-thread control) 자신의 PC, stack, 비독립적(no protection) 다중 프로세스 제어(multiple-process control) 자신의 PC, stack, address space, 독립적(protection) 스레드의 특성 CPU공유 준비, 수행, 대기상태 자식 thread생성 block (예1) 웹 서버 구현 a single process : client의 대기시간이 매우 기어짐 multiple process : client의 요청이 올 때마다 새 process 생성, overhead multithreaded single process : 서버는 thread 생성하여 client의 요청 기다리다 요청이 오면 새 thread 생성하여 서비스, 효율적 (예2) 생산자 소비자 문제 2 threads 로 구현하면 좋음(better if on 2 processors) Thread의 장점 빠른 응답(responsiveness) 자원 공유(resource sharing) 경제성(economy) 다중 처리기 구조 이용(utilization of multiprocessor architecture) 운영체제
사용자 스레드와 커널 스레드 (User and kernel Threads) 사용자 스레드 (User Threads) user level의 thread library로 구현: 라이브러리가 thread 생성, 스케줄링, 관리 담당 불공평한 스케줄링(unfair scheduling) 스위칭이 빠름(switching is fast) single thread인 kernel에서 사용자 수준 스레드가 blocking system call을 수행할 경우 system call 완료까지 다른 모든 스레드들은 대기해야 함 Three primary thread libraries: POSIX Pthreads: POSIX (Portable Operating System Interface) standard (IEEE 1003.1c) APIs (Solaris, Linux, Mac OS X) Java Threads Win32 Threads 커널 스레드 (Kernel Threads) 커널이 thread생성, 스케줄링, 관리 담당 공평한 스케줄링(fair scheduling) 스위칭 시간이 김(switching is time consuming) : interrupt 처리 때문 blocking system call 수행시 커널이 다른 thread 실행 시킬 수 있음 Examples Windows XP/2000 Solaris Linux Tru64 UNIX Mac OS X 혼합 접근 (hybrid approach) : Solaris 2 운영체제
사용자 스레드와 커널 스레드 (User and kernel Threads) kernel 자체 (system call 수행 방법) single tasking : 초기 Unix kernel : 공유 자료 접근 동기화 불필요 multi tasking 동기적인 시스템 Mach kernel : 스레드들은 동기적임(threads are synchronous), 다른 스레드가 제어를 넘겨준 다음에만 수행 가능(공유 데이타 변경 중에는 제어를 넘겨주지 않음) 비동기적인 시스템 : 잠금 기법(locking mechanism)필요 운영체제
다중 스레드 모델 (Multithreading Models) Many-to-One Model : 초기 Solaris Green threads, GNU Portable Threads 다수의 user-level thread가 하나의 kernel thread로 매핑 한 thread가 blocking system call하면 전체 프로세스 block One-to-One Model : Solaris 9과 이후 버전, Linux, OS/2, Windows 95, 98, NT, 2000, XP 각 user-level thread 가 하나의 a kernel thread로 매핑 한 thread가 blocking system call 해도 다른 thread 실행 가능 User thread 생성마다 kernel thread 생성해야 함 동시성이 좋음(more concurrency): multiprocessors에서 병렬 처리(parallel processing) 가능 Many-to-Many Model : Solaris 9 prior, Windows NT/2000 ThreadFiber package 다수의 user-level thread들이 더 적거나 같은 수의 kernel threads들로 매핑(multiplexed) 동시성이 덜 좋음(less concurrency): 커널은 한 순간에 하나의 kernel thread만 스케줄 특별한 경우: two-level model: 하나의 user-level thread가 하나의 kernel thread로 연결되는 경우도 지원: Solaris 8과 이전 버전, IRIX, Digital Unix, HP Tru64 UNIX 운영체제
Many-to-One Model 운영체제
One-to-one Model 운영체제
Many-to-Many Model 운영체제
Two-level Model 운영체제
Solaris 2 Threads 솔라리스(Solaris) 2.x SunOS Release 4.x - Solaris 1.x kernel수준과 user수준에서 스레드 지원 symmetric multiprocessing(대칭적 다중 프로세싱) 각 processor가 OS가짐 Master-slave의 반대 real-time scheduling(실시간 스케줄링) 스레드들 : LWP(Light Weight Processes) = a virtual CPU user-level (thread API들의 library) thread ID, register set(PC, stack pointer), stack, 우선순위 switching(linking with thread)이 빠르다 문맥 교환 없고 CPU switching(스레드 문맥교환) 있음 종류 bound: LWP에 영원히 연결됨, quick response 가능 unbound: LWP 풀에 multiplexed intermediate-level = LWP kernel 자료구조, user level thread의 register set, 메모리와 계정정보 비교적 느리다. kernel-level(CPU 스케줄링 대상) 약간의 자료구조: stack, kernel registers, LWP pointer, 우선순위, 스케줄링 정보 switching이 비교적 빠르다. 운영체제
Solaris 2 Threads Solaris 2의 스레드 Many LWP, many CPU N user-level thread <-> l LWP 1 LWP <-> 1 Kernel-level thread = 1 system call N Kernel-level thread <-> 1 CPU Solaris 에서 ps –eLc 해보면 LWP 정보 알 수 있음 (예) 동시에 5개 화일 읽기 5 LWP필요 각 화일 읽기는 Kernel 안에서 I/O완료를 기다려야 할 경우 Solaris 2에서 한 task는 한 I/O완료를 기다리는 동안 block될 필요가 없음 : 어떤 작업의 한 LWP(kernel thread)가 I/O 완료를 기다리게 되더라도 CPU는 그 작업의 다른 LWP(kernel thread)로 이동하여 작업 수행을 계속 thread library가 최적의 성능을 지원하도록 동적으로 LWP 풀의 수 조절 한 프로세스 안의 모든 LWP 봉쇄되어 실행 가능 LWP 없는데 대기 스레드 있으면 새 LWP 생성 일정 기간(예, 5분) 사용되지 않은 LWP 삭제 참조 Solaris 2.x : System Administrator’s Guide, Threads Primer: A Guide to Multithreaded Programming, Bil Lewis, Daniel J.Berg, Prentice Hall, 1996. 운영체제
Solaris 2 Threads 운영체제
Solaris Process (PCB) Solaris 10 Unix: /usr/include/sys/proc.h (176행) 참고 운영체제
Threads 지원 방법 Kernel(OS) 지원 Solaris Threads fork1(): fork()와 달리 호출한 스레드와 연관된 자료구조만 복제 fork() + exec(): exec()로 주소공간 제거 fork() + POSIX threads library (-lpthread) Linux Threads Linux refers to them as tasks rather than threads Thread creation is done through clone() system call clone() allows a child task to share the address space of the parent task (process) Library 지원 POSIX phread: /usr/include/pthread.h Solaris thread: /usr/include/thread.h Windows XP thread: Win32 library multithreading APIs Implements the one-to-one mapping Each thread contains A thread id Register set Separate user and kernel stacks Private data storage area The register set, stacks, and private storage area are known as the context of the threads The primary data structures of a thread include: ETHREAD (executive thread block) KTHREAD (kernel thread block) TEB (thread environment block) Language 지원: Java Thread 뿐 main(): a single thread 다른 thread들 생성, 관리하는 명령 지원 운영체제
Linux Threads Linux는 프로세스와 스레드를 특별히 구분하지 않음 태스크 사용
Windows XP Threads
Threading Issues Semantics of fork() and exec() system calls Does fork() duplicate only the calling thread or all threads? 목표 스레드(target thread; 취소되어야 할 스레드)의 스레드 종료(Thread cancellation ) 비동기식 취소(Asynchronous cancellation): 즉시 목표 스레드를 강제 종료 지연 취소(Deferred cancellation): 목표 스레드가 주기적으로 강제 종료되어야 할 지를 체크 시그널 처리(Signal handling) A signal handler is used to process signals 디폴트 신호 처리기 사용자 정의 신호 처리기 어느 스레드에게 전달? 신호가 적용될 스레드에게 모든 스레드에게 몇몇 스레드에게 특정 스레드가 전달 받도록 지정 스레드 풀(Thread pools) 새 스레드를 만들기 보다 기존 스레드로 서비스하는 것이 빠름: Win32API PoolFunction() 존재할 스레드 개수에 제한을 둠 스레드별 데이터(Thread-specific data) 각 스레드가 자기 자신만의 데이터를 가짐 스레드 풀 사용 등 스레드를 생성할 수 없을 때 유리함 스케줄러 액티베이션(Scheduler activations) 다대다 모델(Many-to-Many models)과 두수준 모델(Two-level models) 은 응용에 할당할 커널 스레드를 적정 수준으로 유지하기 위한 통신 필요 스케줄러 액티베이션 방법은 커널 스레드로부터 스레드 라이브러리로의 통신 메커니즘 upcalls 지원 LWP = 가상처리기(virtual processor)
Lab 3 threads ~mysung/thread/pthread.c 프로그램과 ~mysung/thread/thread.c 프로그램을 코딩하여 실행해 보세요 /usr/include/pthread.h 와 /usr/include/thread.h 참조 pthread Summation 프로그램을 코딩하여 실행해 보세요 Windows XP thread (Win32 thread) Summation 프로그램을 코딩하여 실행해 보세요 Java thread를 생성하는 두 가지 방법인 First.java와 Second.java 프로그램을 코딩하여 실행해 보세요 자식 스레드를 만들어 자식 스레드로 하여금 리눅스에서는 “$ ls -al” 을, 윈도우 환경에서는 “> dir /a” 명령을 수행시키는 프로그램을 아래 버전으로 각각 만들어 숙제 디렉토리에 제출하세요. 과제 4 pthread 버전 Win32 thread 버전 운영체제
pthreads (thrd-posix.c) #include <pthread.h> #include <stdio.h> int sum; /* this data is shared by the thread(s) */ void *runner(void *param); /* the thread */ main(int argc, char *argv[]) { pthread_t tid; /* the thread identifier */ pthread_attr_t attr; /* set of attributes for the thread */ /* get the default attributes */ pthread_attr_init(&attr); /* create the thread */ pthread_create(&tid,&attr,runner,argv[1]); /* now wait for the thread to exit */ pthread_join(tid,NULL); printf("sum = %d\n",sum); } void *runner(void *param) { int upper = atoi(param); int i; sum = 0; if (upper > 0) { for (i = 1; i <= upper; i++) sum += i; pthread_exit(0); 운영체제
Win32 Threads /** int main(int argc, char *argv[]) * This program creates a separate thread using the CreateThread() system c * * Figure 4.10 * @author Gagne, Galvin, Silberschatz * Operating System Concepts - Eighth Edition * Copyright John Wiley & Sons - 2009. */ #include <stdio.h> #include <windows.h> DWORD Sum; /* data is shared by the thread(s) */ /* the thread runs in this separate function */ DWORD WINAPI Summation(PVOID Param) { DWORD Upper = *(DWORD *)Param; for (DWORD i = 0; i <= Upper; i++) Sum += i; return 0; } int main(int argc, char *argv[]) { DWORD ThreadId; HANDLE ThreadHandle; int Param; // do some basic error checking if (argc != 2) { fprintf(stderr,"An integer parameter is required\n"); return -1; } Param = atoi(argv[1]); if (Param < 0) { fprintf(stderr, "an integer >= 0 is required \n"); // create the thread ThreadHandle = CreateThread(NULL, 0, Summation, &Param, 0, &ThreadId); if (ThreadHandle != NULL) { WaitForSingleObject(ThreadHandle, INFINITE); CloseHandle(ThreadHandle); printf("sum = %d\n",Sum); 운영체제
Java Threads Java threads are managed by the JVM Java threads may be created by: Extending Thread class Implementing the Runnable interface Java Thread 생성 Thread class로부터 새 class 유도하고 run() 재정의 start()가 (1) 메모리 할당하고 새 thread 초기화, (2) run() 실행 절대로 직접 run()호출하지 말고 start()를 호출할 것! (초기화 때문) Runnable interface를 구현하는 class를 정의하고 새 Thread 객체 주로 class가 이미 유도된 경우 이용 (예) public class ThreadedApplet extends Applet implements Runnable { … } Java는 multiple inheritance 불가 운영체제
Thread class 확장으로 thread 생성 (First.java) class Worker1 extends Thread { public void run() { System.out.println(“I am a Worker Thread”); } public class First public static void main(String args[]) { Worker runner = new Worker1(); runner.start(); System.out.println(“I am the main thread”); 운영체제
Runnable interface를 구현하여 thread 생성 (Second.java) public interface Runnable { public abstract void run(); } /* Runnable interface 코딩 않음 */ class Worker2 implements Runnable public void run() { System.out.println(“I am a Worker Thread”); public class Second public static void main(String args[]) { Runnable runner = new Worker2(); Thread thrd = new Thread(runner); thrd.start(); System.out.println(“I am the main thread”); 운영체제
(참고) Java Threads Thread 관리 Java의 thread 관리 APIs suspend() sleep() resume() stop() multithreading 예: applet 일반적으로 graphics, animation, audio 등 처리 처음 applet 실행될 때 start(), display 않는 동안 stop() ClockApplet 참조 Thread 상태 New: new 문으로 thread 객체 생성 Runnable: start() 호출로 메모리 할당하고 run() 호출한 상태 Blocked: I/O, sleep(), suspend()로 block된 상태 Dead: stop() 호출된 상태 운영체제
(참고) Applet standalone application : 지금까지 본 자바 프로그램들 applet : web page에 삽입되어 실행되는 자바 프로그램 No main() Constructor: init() AppletViewer FirstApplet.html 로 실행 FirstApplet.java 파일 import java.applet.*; import java.awt.*; public class FirstApplet extends Applet { public void init(){ //initialization code goes here } public void paint(Graphics g){ g.drawString(“Java Primer Now Brewing!”,15,15); FirstApplet.html 파일 <applet Code = FirstApplet.class Width = 400 Height = 200> </applet> 운영체제
(참고) 날짜와 시간을 출력하는 ClockApplet import java.applet.* ; import java.awt.* ; public class ClockApplet extends Applet implements Runnable { public void run() { while(true) { try { Thread.sleep(1000); } catch(InterruptedException e) { } repaint(); public void start() { if(clockThread == null) { clockThread = new Thread(this); clockThread.start() ; } else clockThread.resume(); public void stop() { if(clockThread != null) clockThread.suspend() ; } public void destroy() { if(clockThread != null) { clockThread.stop() ; clockThread = null ; public void paint(Graphics g) { g.drawString(new java.util.Date().toString(), 10, 30) ; private Thread clockThread; <applet code = ClockApplet width = 250 height = 50> </applet> 운영체제
(참고) Java Threads Thread와 JVM JVM의 system-level threads garbage-collector thread timer events handling thread: sleep() graphics control thread: 버튼 입력, 스크린 갱신 JVM과 호스트 OS Java threads = user threads JVM이 Java threads 관리 Windows NT: one-to-one model Solaris 2.1~2.5: many-to-one model Solaris 2.6~: many-to-many model Multithreaded 해법 예제: Mailbox(IPC)를 이용하는 생산자-소비자 문제 The class Server 참조 Producer thread 참조 Consumer thread 참조 운영체제
(참고) Java Thread States 운영체제
(참고) Driver.java class Sum { private int sum; public int get() { return sum; } public void set(int sum) { this.sum = sum; class Summation implements Runnable private int upper; private Sum sumValue; public Summation(int upper, Sum sumValue) { if (upper < 0) throw new IllegalArgumentException(); this.upper = upper; this.sumValue = sumValue; public void run() { int sum = 0; for (int i = 0; i <= upper; i++) sum += i; sumValue.set(sum); public class Driver { public static void main(String[] args) { if (args.length != 1) { System.err.println("Usage Driver <integer>"); System.exit(0); } Sum sumObject = new Sum(); int upper = Integer.parseInt(args[0]); Thread worker = new Thread(new Summation(upper, sumObject)); worker.start(); try { worker.join(); } catch (InterruptedException ie) { } System.out.println("The sum of " + upper + " is " + sumObject.get()); 운영체제
(참고) Java Threads: Extending the Thread Class class Worker1 extends Thread { public void run() { System.out.println("I Am a Worker Thread"); } public class First public static void main(String args[]) { Worker1 runner = new Worker1(); runner.start(); System.out.println("I Am The Main Thread"); 운영체제
(참고) Java Threads: The Runnable Interface public interface Runnable { public abstract void run(); } Implementing the Runnable Interface class Worker2 implements Runnable { public void run() { System.out.println("I Am a Worker Thread "); } public class Second public static void main(String args[]) { Runnable runner = new Worker2(); Thread thrd = new Thread(runner); thrd.start(); System.out.println("I Am The Main Thread"); 운영체제
(참고) Java Threads: Joining Threads class JoinableWorker implements Runnable { public void run() { System.out.println("Worker working"); } public class JoinExample public static void main(String[] args) { Thread task = new Thread(new JoinableWorker()); task.start(); try { task.join(); } catch (InterruptedException ie) { } System.out.println("Worker done"); 운영체제
(참고) Java Threads: Thread Cancellation public class InterruptibleThread implements Runnable { public void run() { while (true) { /** * do some work for awhile */ if (Thread.currentThread().isInterrupted()) { System.out.println("I'm interrupted!"); break; } // clean up and terminate Thread Cancellation 1 public class CancelExample { public static void main(String[] args) Thread thrd = new Thread (new InterruptibleThread()); thrd.start(); // now interrupt it thrd.interrupt(); } 운영체제
(참고) Thread Specific Data class Service { private static ThreadLocal errorCode = new ThreadLocal(); public static void transaction() { try { /** * some operation where an error may occur */ } catch (Exception e) { errorCode.set(e); * get the error code for this transaction public static Object getErrorCode() { return errorCode.get(); Thread Specific Data 2 class Worker implements Runnable { private static Service provider; public void run() { provider.transaction(); System.out.println(provider.getErrorCode()); } 운영체제