운영체제
개요
- 운영체제에 대한 포괄적인 개념을 정리합니다. 주로 학교 수업에서 배운 내용들입니다. 수업에서 사용한 OSTEP 교재를 기반으로 정리하였습니다.
운영체제가 하는 것
- 운영체제는 컴퓨터 위에 첫 번째로 올라가서 동작하는 프로그램입니다. 기본적으로 컴퓨터가 동시에 여러개의 프로그램을 동작시킬 수 있도록 도와주는 역할을 합니다.
- 컴퓨터(하드웨어)와 프로그램(소프트웨어) 사이를 이어주어, 소프트웨어들이 하드웨어 자원을 안전하게 사용할 수 있도록 기능을 지원해 직접접근을 제한하며 대리자 역할을 합니다.
- 운영체제의 동작 계층을 커널이라고 하며 모든 프로그램들은 이 커널(운영체제)의 도움을 받아 컴퓨터에서 동작을 하게 됩니다.
커널
- 운영체제의 주요 동작이 이뤄지는 부분입니다. 컴퓨터가 동작하면서 지속적으로 커널모드 - 유저모드 간의 교체가 이뤄집니다.
- 유저모드에서는 일반적인 애플리케이션들이 실행될 때의 상태이며 하드웨어에 제한적 접근이 가능한 상태입니다.
- 커널모드에서는 운영체제의 주로 동작이 이뤄지며, 하드웨어에 직접적인 접근이 가능합니다.
- 애플리케이션이 하드웨어와의 상호작용을 하기 위해 커널은 시스템 콜이라는 API를 제공합니다.
시스템 콜
- 애플리케이션이 하드웨어에 접근하고 싶을 때 운영체제 커널이 제공하는 API를 호출해 간접적으로 접근합니다.
(e.g. printf호출시, 내부적으로 write 시스템콜 호출되어 콘솔에 출력) - 이를 Limited Direct Execution라고 함.
- 애플리케이션이 시스템콜을 호출하면, 인터럽트가 발생해 커널모드에 진입하고 커널이 호출된 API에 따라 하드웨어와의 상호작용을 대신합니다.
프로세스
- 프로그램이 실행되면 메모리에 올라가 하나의 동작 상태를 가집니다. 이를 프로세스라고 합니다.
- 프로세스는 pcb에 레지스터 상태, 코드주소, 프로세스ID, 부모pid, 가상메모리를 위한 오프셋 및 테이블, 동작 상태(run, sleep, ready...), 커널 스택 주소... 등의 상태를 가집니다.
스레드
- 프로세스가 소유하는 각 코드 실행 흐름 단위입니다.
- 프로세스 시작시에 메인스레드가 생성되 엔트리포인트(main)에서부터 코드를 하나씩 실행합니다.
- 프로세스는 추가적으로 스레드를 생성할 수 있으며, 각 스레드는 tcb에 각 스레드의 정보를 저장합니다.
- 스레드는 tcb에 PC(Program Counter), Stack Pointer, register context 등과 같은 상태를 담고있습니다.
- 프로세스의 스레드끼리는 힙메모리와 정적 메모리를 공유할 수 있기에 서로간에 경쟁상태가 발생할 수 있습니다. 이를 동시성 제어를 통해 잘 조작해야합니다.
가상화
- 가상화는 개발자가 제작한 프로그램들이 컴퓨터 위에서 잘 돌아가기 위해 OS가 제공하는 기본 개념입니다. 모든 프로그램은 오직 자신만의 컴퓨터를 온전히 점유하고 있다는 착각 하에서 돌아갑니다.
- 실제로는 운영체제가 프로그램을 프로세스로 다뤄 메모리에 올린 뒤 지속적으로 스케줄링을 통해 프로세스(스레드)를 cpu에 올렸다 내렸다 반복합니다.
- 또한, 프로세스가 메모리에 접근할 때 운영체제가 알아서 오프셋을 조절하여 프로세스가 컴퓨터의 메모리를 안전하게 사용할 수 있도록 조절합니다.
- 이를 각각 스케줄링과 메모리 가상화라고 합니다.
스케줄링
- OS는 지속적으로 cpu에서 돌아갈 프로세스를 선택해서 cpu위에 올립니다. 이를 스케줄링이라고 합니다.
- 주기적으로 하드웨어적으로 인터럽트가 발생하면서 cpu에 올라가있는 프로세스가 내려오고서 운영체제가 정책적으로 다음에 올라갈 프로세스에 올라갈 프로세스를 선택해 올리는 방식을 선점형 스케줄링이라고 합니다.
- 반면, 프로세스가 한 번 CPU에 올라가면 프로세스가 스스로 CPU에서 내려오지 않는 한 지속적으로 실행되는 시스템을 비선점형 스케줄링이라고 합니다.
- 현재 대부분의 운영체제는 선점형 스케줄링 방식으로 동작합니다.
비선점형 스케줄링
FIFO (First-In First-Out)
- 프로세스가 실행되면 queue구조로 대기열에 쌓여서 하나씩 순차적으로 실행됩니다.
- 프로세스가 종료되거나 sleep상태가 될 때까지(yield, IO의 경우) 계속 cpu를 점유합니다.
- 무조건적으로 순차적 실행이 되기 때문에 무거운 프로세스가 자리잡고 있다면 뒤에 도착한 프로세스들이 자신의 차례까지 너무 오래 기다려야한다는 단점이 있습니다.
SJF (Shortest Job First)
- 운영체제가 대기중인 프로세스를 탐색하며 실행시간이 제일 짧게 걸릴만한 프로세스들을 우선적으로 스케줄링하는 방식입니다.
- 비선점형 방식이기에 앞서 무거운 프로세스가 먼저 실행되고 있다면 뒤의 프로세스들이 한없이 밀린다는 단점이 여전히 있으며,
계속해서 실행시간이 짧은 프로세스만 선택해 실행시키면 무거운 프로세스는 한없이 뒤로 밀려 **기아상태(starvation)**가 될 수 있다는 문제점이 있습니다.
선점형 스케줄링
STCF (Shortest Time to Completion First)
- OS가 실행중인 프로세스를 ready상태로 cpu에서 내릴 수 있는 선점형 방식입니다.
- 실행을 원하는 프로세스가 도착시 실행시간을 계산하여 제일 짧은 프로세스를 cpu로 올립니다.
- 이 또한, 기아상태의 프로세스가 발생할 수 있다는 문제점이 있습니다.
RR (Round Robin)
- 큐에 프로세스들을 담아, 주기적으로 인터럽트가 발생하면서 프로세스를 순차적으로 실행시키는 방식입니다.(컴퓨터가 인터럽트 발생시켜줌)
- 한 주기를 time slice라고합니다.
- 프로세스들이 빠르게 cpu에 올라가 동작해 반응성이 좋게 느껴지는 장점이 있습니다.
MLFQ (Multi-Level Feedback Queue)
- 라운드 로빈 스케줄링에서, 큐에 우선순위들을 부여한 스케줄링 방식입니다.
- 작업 시간이 긴 무거운 프로세스는 우선순위가 낮은 큐에 넣어서 빠른 작업의 프로세스들을 빠르게 실행시켜 빨리 끝냅니다.
- 한 프로세스가 자신의 타임 슬라이스동안에 작업이 끝나지 않았다면, 타임슬라이스가 다 소모될 때마다 우선순위를 하나씩 낮춥니다.
- 계속해서 낮추다간 해당 프로세스가 기아 상태가 될 수 있으니, 주기적으로 모든 프로세스들의 우선순위를 높여주는 priority-boost가 있습니다.
정리
- 비선점형 스케줄링은 구닥다리, 선점형 스케줄링이 정상적. 또한 타임슬라이스에 따라 주기적으로 스케줄링이 됨.
- 빠르게 프로세스가 교체되는 스케줄링은 사용자 입장에서 반응성이 좋다고 느껴짐. 하지만 스케줄링이 너무 빈번히 일어난다면 컨텍스트 스위칭 또한 자주 일어남. 이는 작업이 느려지게 되는 원인
- 사실 현대 OS는 프로세스가 스케줄링 되는게 아니라 프로세스의 흐름 단위인 스레드가 스케줄링 되는 것.
- 멀티스레드 프로그래밍 할 때 중요한 요소.
메모리 가상화
- 프로그램은 컴파일될 때 고정된 메모리주소 체계를 참조합니다. 운영체제는 여러개의 프로세스들을 공평하게 동작시켜야하기에,
프로세스별로 추가적인 오프셋을 지정해 실제론 서로 다른 물리메모리를 참조하도록 조작합니다. - 프로세스가 보는 메모리 주소를 가상 메모리 주소, 실제 참조하는 주소를 물리 메모리 주소라 하고, 이를 메모리 가상화라고 합니다.
프로세스의 메모리 체계
- 코드영역 - 데이터 영역 - 힙 메모리 - 스택 메모리
- 코드영역은 하나의 프로그램이 여러개 프로세스로 동시 실행시 공유 가능
정적 메모리 할당
- OS가 sw수준만으로 메모리 가상화를 처리. 단순히 하나의 프로세스에 대해 모든 메모리 접근을 + OFFSET 해주는 방식이라 빠름
메모리 할당 문제
- 외부 단편화: 메모리에 일정량 할당을 해야하는데, 메모리가 조각조각 문덩문덩 나있어서 할당할 공간이 없음
- 내부 단편화: 메모리 덩어리를 할당 받았는데, 해당 메모리를 프로세스가 안 씀.
세그멘테이션
- 프로세스의 메무리를 논리단위(코드, 데이터, 힙, 스택)으로 조각내서 메모리 할당. 각 세그먼트마다 base(시작주소), size(용량)값 존재. 가변크기를 가짐
- 스택 메모리는 위에서 아래로(큰 주소에서부터 낮아지는 방향으로) 성장하기 때문에, 세그먼트 주소 접근할 때 역방향(음수)으로 접근해야 함.
- 세그멘테이션을 통해, 코드영역을 서로 다른 프로세스가 공유 가능함. 내부 단편화 문제가 해소됨.
페이징
- 물리 메모리를 특정 규격에 맞춰서 조각냄. 외부 단편화 문제 해소.
- 페이징 디렉토리 기법 사용 가능. 메모리 n차 참조. 메모리 용량 아낄 수 있지만 오버헤드(참조비용) 커짐.
TLB 캐시
- 가상 메모리 주소를 cpu 내부 MMU에 저장. pid(tid)와 가상 메모리 - 물리 메모리 테이블을 통해 실제 메모리에 빠른 접근.
- 접근한지 오래된 엔트리를 제거(대체)함
스왑
- 물리메모리 부족하면 2차 저장 장치(ssd, hdd)로 메모리 페이지를 이동시켜 저장함. 나중에 재접근하려고 하면 페이지폴트 나면서 메모리로 가져옴, 이를 쓰레싱이라고 함.
- 스왑 대상은, 힙 혹은 스택 및 데이터 영역(변경점 있을 시)
동시성
스레드
- 운영체제가 고도화되면서 추가된 시스템. CPU가 코어 하나의 속도 향상이 한계에 다다르면서 단일 프로세스의 실행으로는 성능 향상이 어려워짐.
- 코어 수를 늘리는 방식의 멀티코어 cpu가 등장하기 시작하면서 cpu 성능을 하나의 고비용 프로세스가 최대한 활용하기 위해 , 프로그램 실행 흐름을 여러개 만들어 병렬 실행을 가능하게 하기 위해 스레드라는 개념이 등장
- 스레드를 생성할 때 함수 포인터와 인자를 넘겨주면, 해당 함수가 실행되는 흐름이 추가적으로 생김.
- 프로그램 시작시에, main함수를 실행하는 메인스레드가 생성되면서 프로그램이 시작됨.
스레드의 특성
- 스레드는 프로세스의 자원을 공유(코드영역, 데이터영역, 힙 메모리). 스택 메모리는 각 스레드마다 소유
- 각 스레드는 자신만의 스택 메모리(유저 스택, 커널 스택), 레지스터 상태, 우선순위 등의 상태를 가짐. 이를 스레드 컨트롤 블록에 저장
- 스레드는 동시에 실행될 수 있고, 데이터 영역과 힙 메모리를 공유하기에 서로간에 상태 불일치가 존재할 수 있음. 이를 경쟁상태라고 함.
경쟁 상태(race condition)
- 2개의 스레드가 하나의 공유 자원에 접근할 때, cpu 연산 순서에 의해 결과가 달라질 수 있는 상황.
- 매번 실행시마다 스레드 스케줄링이 달라질 수 있기에 결과가 예측 불간능해 프로그램 동작이 비결정적이게 됨.
- 공유 자원에 접근을 해, 경쟁상태가 발생할 수 있는 구간을 임계 영역이라고 함. 임계 구역에 진입할 때, 다른 스레드가 해당 구역에 진입하지 못하도록 하는 상호 배제가 필요함.
- 상호 배제를 위해, 원자적 연산, 락, 상태 변수, 세마포어 등의 스레드 동시성 제어 기법이 존재함.
원자적 연산 (Atomic Operation)
- 명령어 하나가 실행될 때 중간에 인터럽트가 안 끼어드는 '쪼개지지 않는' 연산.
- 하드웨어 레벨에서
Compare-And-Swap(CAS)같은 명령어를 지원해줌. - 원자적 연산을 활용해 스핀락 구현 가능
락 (Lock)
- 임계 영역(Critical Section)에 "한 번에 한 스레드만" 들어오게 문 잠그는 도구.
- 스핀락 (Spin-lock): 락 얻을 때까지 CPU 빡세게 돌리면서 무한 대기. 컨텍스트 스위칭은 안 일어나서 짧은 작업엔 유리하지만 자원 낭비 심함.
- 뮤텍스 (Mutex): 락 없으면 스레드를 아예 잠재우고(Sleep), 나중에 깨워줌(Wakeup).
상태 변수 (Condition Variable)
- 무작정 기다리는 게 아니라 "어떤 조건이 만족될 때까지" 스레드를 효율적으로 재우는 기법.
wait()으로 잠들고, 조건 충족한 스레드가signal()보내서 깨워주는 방식. (생산자-소비자 패턴의 핵심)
세마포어 (Semaphore)
- 공유 자원의 개수를 나타내는 정수 값.
- 락보다 더 범용적인 개념임. 자원이 하나면 바이너리 세마포어(락이랑 비슷), 여러 개면 카운팅 세마포어.
wait(P연산)으로 값 깎고,post(V연산)으로 값 올리면서 동기화함.
영속성 (Persistence)
- 전원 꺼져도 데이터 안 날아가게 하드웨어랑 소프트웨어(파일 시스템)가 협떡하는 영역.
HDD vs SSD
- HDD: 플래터 돌리고 헤드 움직이는 기계식. 순차 읽기는 빠른데 랜덤 접근은 쥐약임.
- SSD: 플래시 메모리 기반. 물리적 움직임 없어서 개빠름. 대신 쓰기 전에 지워야 하는(Erase-before-write) 특성 때문에 Wear Leveling 같은 관리가 필요함.
파일 시스템 (File System)
- 파일을 어떻게 구조화해서 저장할 것인가의 문제.
- 아이노드 (Inode): 파일의 메타데이터(크기, 위치, 권한 등)를 담고 있는 핵심 데이터 구조.
- 저널링 (Journaling): 쓰기 작업 도중 전원 나가서 파일 시스템 깨지는 거 막으려고, 일단 로그(Journal)부터 남기고 실제 데이터 쓰는 기법. 무결성 보장의 핵심임.
OSTEP(Operating System: Three Easy Peace)
- 대학교에서 운영체제 수업에 쓰는 대표적인 책입니다. 다른 하나로는 공룡책이라고 Operating System COncepts라고 있습니다.
- xv6라는 mit에서 교육목적으로 만든 운영체제를 이용해 실습할 수 있습니다. MIT OS수업 사이트