-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
277 additions
and
1 deletion.
There are no files selected for viewing
2 changes: 1 addition & 1 deletion
2
_posts/Computer-Science/OS/2024-04-07-shell-job-session-pgid-and-daemon.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,276 @@ | ||
--- | ||
title: "[OS] 스레드와 스레드 주소 공간" | ||
date: 2024-04-08 00:39:00 +0900 | ||
categories: [Computer-Science, OS] | ||
tags: ['OS', '프로세스', '스레드'] | ||
--- | ||
{% assign img_path = "/assets/img/posts/OS/2024-04-08-thread" %} | ||
|
||
|
||
|
||
## 1. 프로세스의 문제점 | ||
|
||
프로세스는 여러 프로그램을 동시에 실행시키기 위해 고안된 개념이지만, 이것을 그대로 사용하기에는 다음과 같은 **문제점**이 있다. | ||
|
||
<br> | ||
|
||
- **프로세스 생성 오버헤드가 크다.** | ||
- 메모리 할당 → `fork()` → PCB → Page(segment) 매핑 테이블 → ... 으로 이뤄지는 **복잡한 과정**을 거쳐야 한다. | ||
|
||
<br> | ||
|
||
- **프로세스 간 통신이 어렵다.** | ||
- 프로세스들은 **완전히 독립적인 주소 공간**을 가지고 있다. | ||
- 서로 다른 프로세스끼리는 **간섭이 불가능**하다. | ||
- 프로세스간의 통신을 위해 **별도의 방법이 필요**하다. | ||
- Shared memory | ||
- socket | ||
- message queue 등 | ||
|
||
**하나의 작업**을 **여러 모듈 단위로 쪼개서 일할 때**, 위와 같은 문제점으로 인해 작업이 어려워진다. | ||
|
||
![프로세스의 문제점]({{ img_path }}/1. 프로세스의 문제점.png){: width='580'} | ||
_프로세스간 다양한 통신 방법_ | ||
|
||
--- | ||
|
||
**예를 들어** 미디어 플레이어의 경우, 아래 기능들이 **동시에**[^concurrent] 이뤄져야 한다. | ||
|
||
- 영상 처리 | ||
- 소리 처리 | ||
- 자막 처리 | ||
- 기타 등등.. | ||
|
||
각 기능들이 프로세스 기반 멀티테스킹이라면, context switching 하기에는 **하나하나가 너무 무거우며**, **따라서 시분할 사이 시간이 길어질 수밖에 없다!** | ||
|
||
[^concurrent]: 사실 진짜 동시는 아니고, 시분할을 통해 번갈아가며 실행된다. | ||
|
||
### 1.1. → 스레드의 등장 배경 | ||
|
||
위와 같은 프로세스의 문제점을 해결하기 위해 고안된 것이 바로 **스레드**이다. | ||
|
||
- **스레드란?** | ||
- 프로세스보다 **'더 작은' 실행 단위** | ||
- 현대 OS가 **작업을 '스케줄링'하는 단위** | ||
- CPU 스케줄러가 **CPU에 작업을 전달하는 단위**[^work-manage-unit] | ||
- **스레드 사용으로 인한 효과** | ||
- 프로세스 생성/소멸에 따른 **오버헤드를 감소** | ||
- **빠른 Context switching** | ||
- **손쉬운 통신** | ||
- 스레드는 또한 **lightweight process** 라고도 불림 | ||
|
||
<br> | ||
|
||
- 참고 | ||
- **OS**의 작업 단위: **Process** | ||
- **CPU**의 작업 단위: **Thread** | ||
|
||
![스레드 등장 배경]({{ img_path }}/1.1. 스레드 등장 배경.png){: width='400'} | ||
_작업 단위의 구분_ | ||
|
||
[^work-manage-unit]: 참고: OS 입장에서 작업 관리 단위는 프로세스이다. | ||
|
||
|
||
|
||
## 2. 프로세스: 스레드들의 컨테이너 | ||
|
||
- **스레드는 곧 함수**이며, 따라서 프로세스는 반드시 **1개 이상의 프로세스로 구성**됨 | ||
- **메인 스레드(main)**: 프로세스 생성될 때 OS에 의해 자동으로 생성된 최초 1개의 스레드 | ||
- **멀티 스레드**: 하나의 컨테이너가 여러 개의 스레드를 가진 것 | ||
- **다른 스레드**들은 **함수를 스레드로 만들어줄 것을 요청**하여 **생성**된다. | ||
- 각 스레드별로 **TCB(Thread Control Block)가 생성**되며, **TCB는 PCB에 등록**된다. | ||
|
||
![TCB 구조]({{ img_path }}/2. TCB 구조.png){: width='650'} | ||
|
||
- 프로세스는 **스레드들의 공유 공간(환경)을 제공**함 | ||
- 모든 스레드는 프로세스의 **코드, 데이터, 힙을 공유**함 | ||
- 스레드 사이 통신이 용이 | ||
|
||
### 2.1. 스레드/프로세스 생명 | ||
|
||
- **스레드 종료 시점**: 스레드로 만든 함수가 종료될 때 | ||
- 스레드가 종료되면 **TCB도 제거**된다. | ||
- **프로세스 종료 시점**: 프로세스에 속한 모든 스레드가 종료될 때 | ||
- 프로세스가 강제로 종료되면 **스레드도 당연히 종료**된다. | ||
|
||
![스레드 프로세스 생명]({{ img_path }}/2.1. 스레드 프로세스 생명.png){: width='300'} | ||
|
||
### 2.2. 스레드 예제 (for xNIX OS) | ||
|
||
```c | ||
#include <pthread.h> // pthread lib | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
|
||
//쓰레드 간 동시 접근 | ||
int sum = 0; // global variable | ||
|
||
//20만번(count)만큼 실행 | ||
void *myThread1(void *p) { // for the thread 1 | ||
printf("\t myThread 1 starts\n"); | ||
|
||
int *i = (int *)malloc(sizeof(int)); | ||
for (i = 0; i < (*(int *)p); i++) | ||
sum += 1; | ||
return (void *)i; | ||
} | ||
|
||
void *myThread2(void *p) { // for the thread 2 | ||
printf("\t myThread 2 starts \n"); | ||
|
||
int *i = (int *)malloc(sizeof(int)); | ||
for (i = 0; i < (*(int *)p); i++) | ||
sum -= 1; | ||
return (void *)i; | ||
} | ||
|
||
int main() { | ||
pthread_t tid1, tid2; // thread id | ||
int count = 200000; | ||
int *ret1, *ret2; | ||
|
||
// create thread (id, attribute, function_pointer, argument) | ||
pthread_create(&tid1, NULL, myThread1, &count); | ||
printf("myThread1's tid: %0X \n", (int)tid1); | ||
pthread_create(&tid2, NULL, myThread2, &count); | ||
printf("myThread2's tid: %0X \n", (int)tid2); | ||
|
||
pthread_join(tid1, (void **)&ret1); // waiting for 'tid1' | ||
pthread_join(tid2, (void **)&ret2); // waiting for 'tid2' | ||
|
||
printf("myThreads have been finished \n"); | ||
printf("sum = %d\n", sum); | ||
printf("ret1 = %d\n", (int)ret1); | ||
printf("ret2 = %d\n", (int)ret2); | ||
return 0; | ||
} | ||
``` | ||
#### **실행 결과** | ||
{: data-toc-skip=''} | ||
```terminal | ||
myThread1's tid: CC38A640 | ||
myThread2's tid: CBB89640 | ||
myThread 1 starts | ||
myThread 2 starts | ||
myThreads have been finished | ||
sum = 0 ## 이 값이 0일 수도 아닐 수도 있다. | ||
ret1 = 200000 | ||
ret2 = 200000 | ||
``` | ||
|
||
#### 2.2.1. 생각해보아야 할 점 | ||
|
||
- 프로세스는 스레드들간 **공유 자원을 제공**한다. | ||
- **Data 영역**은 확실히 공유되며, **Stack 영역**은 각자 별개로 가진다. | ||
- 하지만 **스레드의 실행 순서**는 알 수 없다! | ||
|
||
- **공유 자원**(전역변수) 합이 **0이 되지 않는다.** | ||
- **하나의 자원**을 여럿이서 쓰려 하면 **문제가 발생**한다 → OS의 문제 | ||
|
||
### 2.3. 스레드 장점 | ||
|
||
- CPU **응답성 향상** | ||
- 자원 공유, 효율성 향상 | ||
- 다중 CPU의 운용 용이 | ||
|
||
### 2.4. 스레드 단점 | ||
|
||
- 모든 자원을 공유한다는 것 → 하나의 스레드만 잘못되도 **프로세스 전체가 모두 죽어버릴 수 있다!** | ||
- 너무 많은 스레드 → **너무 많은 context switching** | ||
|
||
![단일 멀티 스레드 구조]({{ img_path }}/2.2. 단일 멀티 스레드 구조.png){: width='400'} | ||
|
||
|
||
|
||
|
||
## 3. 스레드 주소 공간 | ||
|
||
**스레드 주소 공간**이란 스레드가 생성/실행되는 동안 접근 가능한 메모리 영역으로, **프로세스의 주소 공간 내에 형성**된다. | ||
|
||
### 3.1. 스레드 사적 공간 | ||
|
||
- 스레드 코드(Thread code) | ||
- 스레드 로컬 스토리지(TLS, Thread Local Storage) | ||
- 스레드 스택 | ||
|
||
### 3.2. 스레드 사이의 공유 공간 | ||
|
||
- 프로세스 코드 | ||
- 프로세스의 데이터 공간(로컬 스토리지 제외) | ||
- 프로세스 힙 영역 | ||
|
||
![스레드 주소 공간]({{ img_path }}/3. 스레드 주소 공간.png){: width='500'} | ||
|
||
#### **TLS example (for xUNIX OS)** | ||
{: data-toc-skip=''} | ||
|
||
```c | ||
#include <pthread.h> // pthread lib | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
int gsum = 0; | ||
int __thread tsum = 1; | ||
|
||
void func(int a) { | ||
printf("5_%d. gsum= %d / tsum= %d \n", a, gsum, tsum); | ||
int b = a + 10; | ||
gsum += b; | ||
tsum += b; | ||
printf("6_%d. gsum= %d / tsum= %d \n", a, gsum, tsum); | ||
} | ||
|
||
void *myThread(void *p) { | ||
int a = (*(int *)p); | ||
printf("2_%d. gsum= %d / tsum= %d \n", a, gsum, tsum); | ||
for (int i = 0; i < 30000000 / a; i++) | ||
; | ||
gsum += a; | ||
tsum += a; | ||
printf("3_%d. gsum= %d / tsum= %d \n", a, gsum, tsum); | ||
|
||
func(a); | ||
printf("7_%d. gsum= %d / tsum= %d \n", a, gsum, tsum); | ||
} | ||
|
||
int main() { | ||
pthread_t tid[2]; | ||
int arg[2] = {1000, 3000}; | ||
printf("1_main. gsum= %d / tsum= %d \n", gsum, tsum); | ||
pthread_create(&tid[0], NULL, myThread, &arg[0]); | ||
pthread_create(&tid[1], NULL, myThread, &arg[1]); | ||
pthread_join(tid[0], NULL); | ||
pthread_join(tid[1], NULL); | ||
printf("8_main. gsum= %d / tsum= %d \n", gsum, tsum); | ||
return 0; | ||
} | ||
``` | ||
#### **실행 결과** | ||
{: data-toc-skip=''} | ||
```terminal | ||
# 실행시마다 출력이 바뀐다. | ||
1_main. gsum= 0 / tsum= 1 | ||
2_3000. gsum= 0 / tsum= 1 | ||
3_3000. gsum= 3000 / tsum= 3001 | ||
5_3000. gsum= 3000 / tsum= 3001 | ||
6_3000. gsum= 6010 / tsum= 6011 | ||
7_3000. gsum= 6010 / tsum= 6011 | ||
2_1000. gsum= 0 / tsum= 1 | ||
3_1000. gsum= 7010 / tsum= 1001 | ||
5_1000. gsum= 7010 / tsum= 1001 | ||
6_1000. gsum= 8020 / tsum= 2011 | ||
7_1000. gsum= 8020 / tsum= 2011 | ||
8_main. gsum= 8020 / tsum= 1 | ||
``` | ||
|
||
|
||
|
||
|
||
<br><br><br><br> | ||
|
||
--- | ||
#### 각주 | ||
{: data-toc-skip=''} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.