스레드
멀티스레딩 모델
One to One
- 일대일
- 하나의 커널 스레드에 하나의 유저 스레드를 매핑하는 모델링
- 다대일 모델보다 concurrency가 높음
- 프로세스 내 스레드 개수가 오버헤드로 인해 제한됨(총 시스템에 의해 동작하는 스레드의 개수는 유저+커널, 기존에 비해 2배가 되므로 생성 스레드 개수가 제한되기도 함)
- Windows, Linux 등
Many to Many
- 다대다
- 여러 유저 레벨 스레드가 여러 커널 레벨 스레드로 매핑되는 모델링
- OS가 충분한 양의 커널 스레드 생성 가능
스레드 관련 이슈
1. fork(), exec() 시스템 콜
- fork(): 부모 프로세스 → 자식 프로세스 생성하는 시스템 콜
fork() 호출 시 프로세스 내 스레드는 어떻게 될까? → OS에 따라 다르다. UNIX에서는 fork() 옵션을 통해 조절할 수 있다!
- exec(): fork() 후 프로세스를 실행하는 시스템 콜. 기존 모든 스레드를 대신해서 실행하는 것인지, 어떤 스레드를 실행할 것인지 확인하는 자료구조 필요
- clone(): 스레드를 복사하는 개념
2. 스레드 취소
- 스레드가 ternimate하기 전 종료하는 과정, Cancellation
- 비동기적 취소는 타깃 스레드를 즉각적으로 terminate
- deferred 취소는 타깃 스레드가 주기적으로 삭제되어야 하는지를 스스로 확인하도록 만듦
- UNIX의 Pthread 코드
- 스레드 취소 → 모드 + 상태에 따라서 타입 결정
- 디폴트 타입은 deferred: 스레드 취소는 스레드가 취소 포인트에 도달했을 때에만 일어남
- Linux: 스레드 취소는 시그널을 통함
3. 스레드 풀
- 스레드가 작업을 멈추고 기다리는 장소인 풀 Pool에 여러 개의 스레드를 미리 만들어놓고 사용자 요청에 따라서 호출
- 사용자 요청에 따라 새로운 스레드를 바로 생성하는 것보다 속도 빠름
- 하나의 어플리케이션에 따라 생성 가능한 스레드의 개수가 미리 정해짐
- 스레드에 번호를 할당, 다양한 strategy에 따라서 태스크를 할당 가능
4. 스레드 간의 IPC
- 스레드는 프로세스 내 주소공간 공유 → 공유 메모리 형식의 IPC가 가장 적합
- 고성능, 동기화 문제를 해결하는 게 주요 이슈
- 스레드는 일반적으로 IPC 필요를 낮춤
- 다른 프로세스 내 스레드가 서로 통신하는 경우: 프로세스 간 IPC와 차이가 없음(스레드를 사용하는 이유 X). 프로그램 디자인 상 좋지 않은 모델
5. 시그널 핸들링
- UNIX 기반 시스템에서 시그널이 프로세스에게 특정한 이벤트가 발생했음을 알려주는 행위
- 시그널 핸들러는 시그널을 처리하기 위한 방법: (1). 이벤트에 의해 생성 (2). 프로세스로 시그널 전달 (3). 디폴트/유저 정의한 시그널 핸들러 중 하나에 의해 시그널 처리
- 모든 시그널: 디폴트 시그널 핸들러, 커널 레벨에서 처리됨
- 유저 정의한 시그널 핸들러가 있을 경우 디폴트 핸들러가 오버라이드
- 멀티 스레드 환경의 시그널 핸들링: (1). 시그널이 적용된 스레드에만 시그널 전달 (2). 스레드 속한 프로세스 자체에 시그널 전달 (3). 스레드 속한 프로세스의 일부 스레드에 시그널 전달 (4). 프로세스 내에 시그널 수신만을 전담하는 스레드 할당
스레드 구현
- 스레드 라이브러리: OS에 따른 서로 다른 구현 방법
- POSIX(pthread_create), Windows API(CreateThread), Linux(clone), Java(Thread class)
Linux의 스레드
- 태스크: 실행흐름, 프로세스 + 스레드 개념
- 플래그에 따라 행동 방식이 결정
- clone() 시스템 콜 → 스레드 생성, 부모 태스크의 주소 공간을 공유하도록 하는 구조이므로 스레드와 개념적 상동
동기화
- 여러 개의 프로세스 동시(concurrent) 실행 가능
- 공유 데이터 동시 접근 시 data inconsistency 문제 발생
- 하나의 데이터에 여러 실행 흐름 접근 시 동기화 처리 필요
프로세스의 실행은 인터럽트에 의해 "언제든지" 중지, 종료될 수 있기 때문에 동기화는 필수적 이슈!
동기화 문제
- 은행의 입출금 문제: 잔금 1000원, A는 500원 입금, B는 500원 출금 동시에 작업
- Process A:
Balance += 500
- Process B:
Balance -= 500
어떤 순서로 처리하느냐에 따라서 값이 바뀔 수도 있다!
- 각 프로세스가 사용하는 레지스터에 저장된 잔고 값이 다를 수 있기 때문. Concurrent한 작업 → Race condition 해결
동기화가 필요한 까닭
- 스레드 프로그래밍: 데이터 섹션 공유
- 공유 메모리: 여러 개의 프로세스가 접근 가능
- 커널 자료 구조