더보기
운영체제 SystemCall Shell
프로세스 생명주기
스케줄링 라운드로빈 FCFS SJF 컨텍스트스위칭 PCB TCB
멀티스레드 동기화문제 RaceCondition CriticalSection 뮤텍스 세마포어
1. 운영체제
1-1. 운영체제가 하는 일
운영체제는 하드웨어와 소프트웨어를 관리하기 위해 만들어졌다. OS 규격과 관리 목적은 제품에 따라 다르지만 대부분 아래 기능을 수행한다.
- 메모리, CPU, 저장장치, 네트워크 등을 관리
- 파일 시스템
- 프로세스 실행 관리
- 하드웨어 추상화 계층 (HAL)
- 프로그래머가 하드웨어에 신경쓰지 않게 해주는 것
- 운영체제와 운영체제가 컴퓨터 장치를 사용하기 위한 드라이버 사이에서, 사용자가 필요한 동작들에 대하여 표준적인 접근 방법을 제공하는 것
1-2. 운영체제의 종류
- 유닉스: 벨 연구소
- macOS, iOS
- 리눅스: 대학원생 리누스 토르발스
- 안드로이드
- 윈도우: MS
- Mac OS: apple
1-3. 운영체제의 구조

- 커널
- 운영체제의 핵심. 항상 메모리에 상주
- Linux의 경우 OS가 항상 메모리에 상주하기 때문에 운영체제와 커널이 같다
- System Call
- 운영체제의 기능을 사용할 수 있게 해주는 API
- 커널 모드, 유저모드
- 보안을 위해 존재
- 프로세스는 유저 모드와 커널 모드 두 가지 모드로 작동됨.
- 시스템콜이 이뤄지면 커널 모드로 전환. 할 일이 끝나면 다시 유저 모드로 돌아감
- Shell
- 커널과 사용자 사이에 끼어서 명령을 해석하고 처리 결과를 보여주는 프로그램
- 명령줄 셸 : cmd, powershell, sh, bash
- 그래픽 셸 : 윈도우 탐색기
2. 프로세스
2-1. 프로그램과 프로세스
| 프로그램 | 하드디스크에 저장된 (프로그램의) 실행 파일 |
| 프로세스 | 프로그램이 실행되고 있는 상태 즉, 하드디스크에서 메인 메모리로 코드와 데이터를 가져와 현재 실행되고 있는 상태 (프로세스를 실행하려면 독자적인 메모리 공간과 CPU가 필요) 동시에 여러 개가 존재할 수 있음 cmd에 tasklist 명령어를 입력하면 현재 실행 중인 프로세스 목록을 볼 수 있음 프로세스 상태는 상황에 따라 변화함 (생명 주기) |
2-2. 프로세스의 상태 (생명 주기)

- 생명 주기
- Created : 막 생성해서 실행 준비를 하는 단계, 실행 중인 프로세스와 우선순위를 비교
- Waiting : 순서에 맞춰 실행을 기다리는 상태
- Running : 운영체제로 부터 CPU를 할당 받아 실행되고 있는 상태 (순서에 맞춰 실행을 기다리는 상태)
- Dispatch 디스패치 : 다음으로 실행될 프로세스에 CPU를 할당하는 것
- Preemption 프리엠션 : 실행 중이던 프로세스에서 CPU를 해제하는 것
- Blocked : I/O(읽고 쓰기처럼 디스크에서 정보를 불러오는 아주 간단한 일) 같이 오랜 시간 걸리는 작업에 들어간 경우 해당 상태로 변경됨 (I/O 작업이 끝나기 전까지 실행 불가능, 이때 실행 가능 상태의 프로세스 중 하나가 CPU를 할당받음)
- Termiated : 종료, 소멸
2-3. 스케줄링
디스패치와 프리엠션이 일어날 때 운영체제가 어떤 프로세스에게 CPU를 할당할까?
- 스케줄링 : 운영체제가 여러 프로세스의 CPU 할당 순서를 결정하는 것
- 스케줄러 : 스케줄링을 하는 프로그램
- 스케줄링 종류
- 선점형 : 어떤 프로세스가 실행 중에 있어도 스케줄러가 강제로 프로세스를 중지하고 다른 프로세스를 실행(CPU에 할당)
- 비선점형 : 실행 중인 프로세스가 종료되거나, I/O 작업에 들어가거나, CPU를 반환하기 전까지는 계속해서 실행됨. 우선순위가 높은 프로세스가 생성되어도 실행 중인 프로세스가 자발적으로 CPU를 양보하지 않으면 사용하지 못함
- 스케줄링 알고리즘의 종류 (제품에 따라 다름)
- 우선순위(Priority) : 우선순위를 매겨 높은 우선 순위부터 실행
선점형- 기아 상태(starvation) : 낮은 우선순위가 계속 일을 하지 못하는 상황
- 에이징(aging) : 오랫동안 실행되지 못한 프로세스의 우선순위를 높여 기아 상태 방지
- 라운드로빈(Round-Robin) : 일정 시간동안 순서대로 할당
비선점형- 일정시간 : 타임 슬라이스(time slice) 또는 퀀텀(quantum)이라고 불림
- FCFS(First Come First Served) : 실행 가능 상태에 먼저 들어온 순서대로 처리
비선점형 - SJF(Shortest Job First) : 평균 대기시간을 최소화하기 위해 CPU 할당 시간이 가장 짧을 것으로 예측되는 것부터 처리
선점형&비선점형- 단점 : 할당 시간이 긴 프로세스는 계속해서 실행되지 않는 기아 상태에 빠질 수 있고, CPU의 실제 할당 시간을 알 수 없으니 예측에 의존해야 함
- 우선순위(Priority) : 우선순위를 매겨 높은 우선 순위부터 실행
2-4. 컨텍스트 스위칭
- 컨텍스트 스위칭 : 실행되고 있는 프로세스가 변경될 때(디스패치, 프리엠션이 일어날 때) CPU에 프로세스를 실행하기 위한 정보(PCB)가 교체되는 것
- 이때 현재 CPU의 레지스터 값들을 전환, 컨텍스트 스위칭이 일어날 때 기존 프로세스의 CPU 상태 정보는 PCB에 기록해 둠
- CPU의 상태 정보 : 다음에 CPU를 점유하면 실행할 다음 인스트럭션의 위치(프로그램 카운터 정보), 현재 쌓인 스택 프레임의 정보(스택 포인터와 프레임 포인터의 정보), 현재 연산 중인 데이터가 저장된 레지스터 값(범용 레지스터 정보) 등
- 잦은 컨텍스트 스위칭은 성능을 떨어트림
- 이때 현재 CPU의 레지스터 값들을 전환, 컨텍스트 스위칭이 일어날 때 기존 프로세스의 CPU 상태 정보는 PCB에 기록해 둠
- PCB(Process Control Block) : 프로세스의 CPU 상태와 프로세스의 상태를 저장해 둔 메모리 블록
- 프로세스의 상태 : 현재 실행 중인 인스트럭션, 스택 프레임, 프로그램 카운터 값, 스택 포인터와 프레임 포인터와 범용 레지스터의 값
- 이 정보는
커널에 위치함 (OS가 사용하는 영역)
3. 멀티 스레드
3-1. 멀티 프로세스와 멀티 스레드
| 멀티 프로세스 | 멀티 스레드 | |
| 그게 뭐죠? | 프로세스 : 알잖아 멀티 프로세스 : 프로세스 여러 개를 동시에 실행하는 것 |
스레드 : 프로세스 안의 실행 흐름 단위로, 스레드 단위로 CPU를 할당받을 수 있다. 멀티 스레드 : 스레드 여러 개를 동시에 실행하는 것 |
| 가진 것 | PCB | TCB (PCB랑 거의 같음) |
| 이미지 | ![]() |
![]() |
| 통신 방법 | 소켓, 메시지 큐, 파이프 | 공유되는 메모리 공간 (ex. 전역변수) |
| 장점 | 독립된 구조로 안정성이 있음 | 컨텍스트 스위칭이 단순해 성능이 좋아짐 |
| 단점 | 프로세스 컨텍스트 스위칭이 일어나 성능이 떨어짐 (통신 비용이 큼) | 자원을 공유할 때 동기화 문제 발생 가능성이 있음 (아래에서 설명할 공유 자원 문제) |
3-2. 멀티 스레딩을 Python으로 구현하기
아래는 공유 자원 문제(동기화 문제)가 일어나는 코드이다.
# 공유 자원 문제가 일어나는 코드
import threading
g_count = 0
def thread_main():
global g_count
for i in range(1000000):
g_count += 1
threads = [ ]
for i in range(50): #3
th = threading.Thread(target = thread_main)
threads.append(th)
for th in threads:
th.start()
for th in threads:
th.join()
print('g_count :{:,}'.format(g_count))

이러한 문제가 발생하는 이유는?

위의 경우 단순히 변수에 1을 더하는 작업이지만 instruction 기준으로 보면 여러 단계. 어느 시점에 컨텍스트 스위칭이 일어나는지에 따라 결과가 잘못 나온다.
- 경쟁 조건(race condition) : 스레드 여러 개가 공유 자원에 동시에 접근하는 것
- 임계 영역(critical section) : 공유 자원에 접근해 변경을 시도하는 코드 영역
3-3. 공유 자원 문제(동기화 문제) 해결
문제를 해결하기 위해, 공유 자원에 여러 스레드가 동시에 접근하지 못하도록 제어해야 한다.
# 문제 해결 코드
import threading
g_count = 0
def thread_main():
global g_count
lock.acquire()
for i in range(1000000):
g_count += 1
lock.release()
lock = threading.Lock()
threads = [ ]
for i in range(50):
th = threading.Thread(target = thread_main)
threads.append(th)
for th in threads:
th.start()
for th in threads:
th.join()
print('g_count :{:,}'.format(g_count))
이것을 상호 배제라고 한다.
- 뮤텍스 (상호배제, Mutual Exclusion = Mutex)
- 스레드 하나가 공유 자원을 이용하는 동안 다른 스레드가 접근하지 못하게 막는 것
- 1칸 짜리 화장실
- 세마포어 (더 좋은 방법, Semaphore)
- 공유 자원에 최대 접근 가능한 스레드 개수를 제한하는 것
- 자원에 접근 중인 스레드를 count해서 구현
- 최대 접근 가능한 스레드 개수를 1로 하면 뮤텍스가 됨
- 세마포어는 뮤텍스가 될 수 있지만, 뮤텍스는 세마포어가 될 수 없음
- N칸 짜리 화장실
3-4. 교착 상태 (DeadLock)

둘 이상의 스레드가 다른 스레드가 점유하고 있는 자원을 서로 기다릴 때와 같이 무한 대기에 빠지는 상황
'스터디 노트 > 컴퓨터 과학 기초' 카테고리의 다른 글
| 컴퓨터과학 기초 > 05장 변수/정수/실수/문자열 (0) | 2022.04.02 |
|---|---|
| 컴퓨터과학 기초 > 04장 프로그래밍 언어/컴파일러 (0) | 2022.02.26 |
| 컴퓨터과학 기초 > 03장 운영체제 - 메모리 (0) | 2021.11.24 |
| 컴퓨터과학 기초 > 01장 CPU (0) | 2021.11.16 |

