-
운영체제 : CPU, 메모리, 디스크 할당 및 반환 등 각종 자원 관리
커널 : 운영체제의 핵심부 (kernel.org 에서 리눅스 커널 소스코드 확인 가능)
+ 운영체제 중에서 커널이 아닌 것 : UI
=> 메모리에서 운영체제가 적재되는 공간 = 커널 영역
모든 응용 프로그램은 자원에 임의 접근이 불가능 = 반드시 운영체제를 통해서만 가능
=> 운영체제는 일종의 문지기 역할을 수행함
이중 모드
- 커널 모드 : 운영체제 서비스를 제공 받을 수 있음 (입출력 가능) = 커널 영역의 코드 실행 가능
- 사용자 모드 : 운영체제 서비스를 지공 받을 수 없음 (입출력 불가능) = 커널 영역의 코드 실행 불가능
운영체제 서비스 : 프로세스 관리, 자원(CPU, 메모리, 디스크) 관리 접근 및 할당, 파일 시스템 관리
시스템 콜 : 운영체제 서비스를 제공받기 위해 커널 모드로 전환하는 것
- 응용 프로그램이 자원에 접근하기 위해 거치는 과정 => 인터럽트이기 때문에 응용프로그램 작업을 중단한 뒤, 운영체제의 코드 실행 후 작업 재개
<LINUX 기반 관련 명령어>
strace : 시스템 콜을 추적하기 위한 도구
PID = Process ID => PID 활용해서 시스템 콜 트레이스 가능
$ strace -p (PID) $ strace -t # 타임스탬프 $ strace -tt # 밀리세컨드 타임스탬프 $ strace -T # 각 시스템 호출 소요 시간 $ strace -c # 시스템 호출 요약 결과 출력 $ strace -e trace= # 시스템 호출 결과 필터링 $ touch 파일명 # 파일 생성 $ rm 파일명 # 해당 파일 삭제
대표적인 리눅스 시스템 콜
# 파일(디렉토리, 소켓 등) 여는 시스템 콜, 파일 디스크립터 반환 $ open # 파일 닫는 시스템 콜 = 파일 관련 자원 해제 역할 $ close # 파일 디스크립터에서 데이터 읽어들이는 시스템 콜 $ read # 파일 디스크립터에 데이터를 쓰는 시스템 콜 $ write # 프로세스를 복제하여 자식 프로세스 생성 => 프로세스를 계층적으로 구성하고 관리함 $ fork # 현재 프로세스의 주소 공간을 새로운 프로세스로 덮어 씀 => 자식 프로세스는 다른 코드 실행 $ exec # PID 반환하는 시스템 콜 $ getpid # 부모 프로세스의 PID를 반환하는 시스템 콜 $ getppid # 시스템 로그 메시지 남기기 $ syslog # 실행 중인 프로그램 종료 $ exit
프로세스 : 실행 중인 프로그램 => 같은 프로그램도 별도의 프로세스가 될 수 있음
- 포그라운드 프로세스 : 사용자와 직접 상호작용 중인 프로세스
- 백그라운드 프로세스 : 사용자와 상호작용 하고 있지 않은 프로세스
$ systemctl list-units --type service # 각 서비스 상태 및 설명 출력
프로세스 제어 블록(PCB) : 동시 다발적으로 실행되는 프로세스들 관리를 위한 프로세스 식별 및 상태 확인용 데이터
- 같은 프로그램이더라도 다른 프로세스로 실행 중이라면 PCB 값 다름 => 운영체제가 프로세스를 식별하는 기준
PCB가 가지고 있는 값
- PID(PPID)
- 레지스터
- 스케줄링 정보
- 메모리 정보
- 사용한 파일 정보
- 입출력장치 정보
문맥(context) : 실행을 재개하기 위해 기억해야할 정보
문맥 교환 : 여러 프로세스들이 번갈아가면 실행될 때 이루어짐 = 프로세스 전환 시 CPU 내에 문맥이 교환되는 것
=> 할당 시간이 종료된 프로세스의 문맥을 저장하고, 실행될 프로세스의 문맥을 불러오는 것
<<문맥 교환이 너무 빠르게 이루어지면 비용이 증가해 시스템이 느려질 수 있으니 적당한 속도로 이루어지는 것이 좋음>>
프로세스 커널 영역에서는 PCB로 구성됨
사용자 영역 구성 요소
- 스택 영역 : 임시로 저장되는 영역 (매개 변수, 지역 변수 등)
- 힙 영역 : 사용자(개발자)가 직접 할당 가능한 공간 - 메모리 영역 할당, 사용 완료 후에는 해제할 것 (직접 해제 or 자동 해제(가비지 컬렉션)) => 할당 미해제 시 메모리 누수 발생할 수 있음
- 데이터 영역 : 프로그램이 실행되는 동안 유지할 데이터 (전역 변수 등)
- BSS 영역 : 초기값이 없는 데이터는 여기에 저장, 초기값이 있는 데이터는 데이터 영역에 저장
- 코드 영역 (텍스트 영역) : 실행 가능한 코드, 즉, 기계어로 이루어진 명령어 적재 (Read Only)
동적 영역
- 힙 영역 : 낮은 주소에서 높은 주소로 할당
- 스택 영역 : 높은 주소에서 낮은 주소로 할당
정적 영역
- 데이터 영역
- 코드 영역
$ top # 리소스 모니터링 시 활용되는 명령어
대표적인 프로세스 상태
- 생성 (new)
- 준비 (ready) : 바로 실행 가능한 상태이지만 기다리고 있는 상태
- 실행 (running)
- 대기 (blocked) : 지금 바로 실행할 수 없는 상태
- 종료 (terminated)
리눅스 프로세스(태스크) 상태
- R (Running) : 실행
- S (Sleeping) : 대기
- W (Waiting) : 준비
- S (Stopped) : 종료
- Z (Zombie) : 프로세스 종료 및 자원 반환 후에도 커널 영역에 프로세스가 남아있는 상태
프로세스는 계층적 구조로 관리됨
$ pstree # 프로세스 계층적 구조 확인 명령어
fork-exec : 프로세스 생성 시 세트로 실행되는 명령어 => 복사본 만들고(fork), 새로운 코드로 덮어쓰기(exec)
스레드 : 프로세스를 구성하는 실행 흐름 단위
한 프로세스에 여러 스레드 존재할 수 있음 = 멀티 스레드
스레드는 각기 다른 스레드 ID, 프로그램 카운터, 레지스터, 스택 정보 가짐
<리눅스에서는 프로세스와 스레드를 명시적으로 구분하지 않음>
멀티프로세스 vs 멀티스레드 : 가장 주된 차이점은 자원 공유 여부
- 프로세스 간에는 "기본적으로" 자원 공유되지 않음 => 메모리 소모 큼
- 프로세스 간 통신(IPC)으로는 프로세스 간 자원 공유 가능
- 스레드 간에는 프로세스 자원 공유됨 => 하나의 스레드에 문제 발생 시 프로세스 자체가 멈춤
CPU 스케줄링 : 운영체제가 공정하고 합리적으로 자원을 배분하는 방법
CPU 자원은 한정되어 있으므로 프로세스 별 우선순위에 따라 할당함
$ ps -el # 프로세스 우선 순위 확인 가능 # PRI : 운영체제 기준 우선순위, NI : 사용자가 조정 가능한 우선순위
일반적으로 I/O bound process가 CPU bound process 보다 우선순위 높음
=> I/O bound process 가 수행되는 시간이 CPU bound process 보다 오래 걸리기 때문
CPU 사용 구간 : CPU burst
입출력 장치 사용 구간 : I/O burst
CPU 스케줄링 알고리즘 : 프로세스 우선순위를 토대로 CPU를 할당 받는 방법
스케줄링 큐 : 한정적인 자원을 프로세스들의 요구사항에 따라 일목요연하게 관리하는 방법
=> 각 자원 별로 큐를 분리해서 줄을 세우는 것
단, 큐라고 해서 무조건 FIFO로 구성될 필요는 없음
- 준비 큐 : CPU 이용을 기다리는 프로세스들의 큐
- 대기 큐 : 대기 상태 프로세스들의 큐, 입출력 종료 신호를 기다림 => 입출력 종료되면 준비 큐로 삽입됨
큐라고 하더라도 우선순위에 따라 처리됨 => 우선순위 낮은 프로세스가 먼저 삽입되었어도 우선순위 높은 프로세스가 먼저 처리될 수 있음
선점형 스케줄링 : 현재 실행 중인 프로세스 자원을 빼앗아 다른 급한 프로세스에 할당
- 장점 : 프로세스에 자원 고루 할당 가능
- 단점 : 문맥 교환 과정에서 오버헤드 많음
비선점형 스케줄링 : 현재 실행 중인 프로세스 실행이 끝날 때까지 대기
- 장점 : 문맥 교환 과정에서 오버헤드 적음
- 단점 : 고르지 않은 자원 분배
CPU 스케줄링 알고리즘
- 선입 선처리 스케줄링(FIFO 스케줄링) : CPU를 먼저 요청한 프로세스부터 CPU를 할당하는 준비 큐에 삽입된 순서대로 실행되는 비선점형 스케줄링 => 부작용 : 호위 효과(convoy effect)
- 최단 작업 우선 스케줄링(SJF 스케줄링) : 준비 큐 프로세스 중 CPU 이용 시간이 짧은 프로세스부터 실행함 => 호위효과 방지
- 라운드 로빈 스케줄링(Round Robin(RR) 스케줄링) : 선입 선처리 스케줄링 + 타임 슬라이스 = 준비 큐에 삽입된 순서대로 실행하되, 타임 슬라이스만큼 실행하는 선점형 스케줄링
- 최소 잔여 시간 우선 스케줄링(SRT 스케줄링) : 최단 작업 우선 스케줄링 + 라운드 로빈 스케줄링 = 작업 시간이 짧은 프로세스부터 처리하되, 타임 슬라이스만큼 실행함
- 우선순위 스케줄링 : 프로세스마다 우선순위 부여 후 우선순위가 높은 순으로 스케줄링하는 범용적인 방법
- SJF 스케줄링 : 작업시간 짧은 순으로 우선순위 부여
- SRT 스케줄링 : 남은 시간 짧은 순으로 우선순위 부여
- 문제점 : 아사(starvation) 현상
- 모든 우선순위 스케줄링 알고리즘의 근본적인 문제
- 우선순위 낮은 프로세스의 실행이 계속 연기되는 현상으로 우선순위 높은 프로세스 실행으로 인해 우선순위 낮은 프로세스들의 실행이 불가능한 것
- 해결책 : 에이징(aging) - 대기시간이 길어지면 우선순위를 점차 높이는 방식
- 다단계 큐 스케줄링 : 우선순위 별로 준비 큐를 여러 개 사용하는 스케줄링
- 프로세스 유형별로 큐 구분 가능 - CPU 바운드, I/O 바운드, 백그라운드, 포그라운드, 실시간 프로세스 등
- 큐 별로 다른 스케줄링 알고리즘 적용 가능
- 큐 별로 다른 타임 슬라이스 적용 가능
- 기본적으로 프로세스는 한번 삽입되면 큐 간에 이동이 불가능 => 아사 현상 발생
- 다단계 피드백 큐 스케줄링 : 다단계 큐의 아사 현상 발생 가능성을 개선한 것
- 프로세스가 큐 간에 "이동 가능"
- 우선순위가 높더라도 정해진 타임 슬라이스 내에 끝나지 않으면 우선순위가 점점 낮아짐
- 우선순위가 낮더라도 대기시간 길어질수록 우선순위 높아짐 = 에이징
- 즉, CPU를 주로 사용해야하는 CPU 바운드 프로세스는 우선순위가 점점 낮아지고, 입출력 장치를 주로 사용해야하는 I/O 바운드 프로세스는 우선순위가 점점 높아짐
리눅스 스케줄링 정책
- 실시간 정책 스케줄링: 우선순위 높지만 데드라인 있음
- SCHED_FIFO
- SCHED_RR
- 일반 정책 스케줄링 : 우선순위 낮음
- SCHED_OTHER / SCHED_NORMAL
- SCHED_BATCH
- SCHED_IDLE
CFS (Completely Fair Scheduler) : 비실시간 프로세스를 대상으로 하는 스케줄링 방식
vruntime(virtual runtime) : 프로세스가 그 동안 실행한 시간을 정규화한 정보
vruntime 값이 작은 프로세스를 다음 실행할 프로세스로 삼음
vruntime 별 태스크를 고르는 과정에서 RB tree 사용함
타임 슬라이스 => nice 값에 비례해서 가중치를 할당하고 그 가중치 값을 바탕으로 타임 슬라이스를 할당함
nice : 사용자 영역에서 설정한 프로세스 우선순위로 -20 ~ 19 범위 내에서 설정 가능
nice 값이 커널 영역으로 넘어가는 순간 0 ~ 139 범위 내의 값으로 바뀜
=> 실시간 스케줄링되는 프로세스 : 0 ~ 99 / CFS 프로세스(비실시간) : 100 ~ 139
# nice 명령어 : 새 프로세스 실행 시, 해당 프로세스에 우선순위 부여 (기본값 0) $ nice -n [우선순위] [program] # renice 명령어 : 이미 실행 중인 프로세스의 우선순위 변경 $ renice [우선순위] [PID]
'프로그래밍 > CS' 카테고리의 다른 글
운영체제 (2) (0) 2024.08.31 컴퓨터 구조 (2) (0) 2024.08.05 컴퓨터 구조 (1) (0) 2024.07.25