"이것이 취업을 위한 컴퓨터 과학이다 with CS 기술면접" 책을 참고했습니다.
1. 운영체제의 큰 그림
스마트폰의 운영체제인 안드로이드나 iOS나 데스크톱 PC의 윈도우나 맥OS의 경우 형태는 다른 것 같지만 제공하는 핵심적인 기능은 비슷하다. 이런 운영체제의 핵심적인 기능을 담당하는 부분을 커널(Kernel)이라고 하는데, 별다른 언급이 없다면 이제부터 운영체제라고 설명하는 부분은 이 커널을 지칭한다고 이해하면 된다.
운영체제에는 2가지의 큰 핵심 기능이 있다. 하나는 자원을 할당하고 관리하는 역할이고, 다른 하나는 프로세스 및 스레드를 관리하는 역할이다. 이제 이 핵심 기능들을 차례대로 살펴보면서 전체적인 큰 그림을 그려보며 설명하겠다.
1-2 운영체제의 역할
운영체제에서 설명하는 자원이란 프로그램 실행에 마땅히 필요한 요소를 의미한다. 실행에 필요한 데이터(소프트웨어)나 실행에 필요한 부품(하드웨어)가 자원이 될 수 있다. 앞서 2장에서 정의하고 설명한 부분들이 핵심적인 자원이라고 할 수 있겠다.
운영체제는 사용자가 직접 사용하고 있는 응용 프로그램을 대신해 CPU 등을 포함한 컴퓨터 부품에 접근하고 그 부품들이 효율적으로 사용되도록 관리한다. 또한 응용 프로그램들이 그 부품들을 효율적으로 할당받아 문제없이 실행되도록 자원을 할당한다. 아직 그게 어떤 건지 감은 안 오겠지만, 각각의 컴퓨터 부품들을 어떻게 효율적으로 관리하는지에 대해서 순차적으로 알아보며 더 접근하도록 하겠다.
(1) CPU 관리 : CPU 스케쥴링
앞서 CPU와 메모리에 대해서 설명하면서 메모리에는 현재 실행 중인 프로그램이 다수 적재될 수 있다고 했었다. 하지만 그렇다고 해서 CPU가 이 모든 프로그램에 대해서 동시에 접근할 수는 없다. CPU는 한정적인 자원이기 때문에 CPU를 할당받아 사용하기 위해서는 다른 프로그램이 CPU 사용이 종료될 때까지 기다려야한다. 이때, 운영체제는 실행 중인 모든 프로그램들이 공정하고 합리적으로 CPU를 할당받을 수 있또록 CPU의 할당 순서와 사용 시간을 결정한다. 이것이 추후 학습할 CPU 스케줄링이라고 한다. 다음 시간에 확인할 내용이지만 핵심 내용만 정리하면 다음과 같다.
- 기본 개념 : 우선순위, 스케줄링 큐, 선점형과 비선점형
- CPU 스케줄링 알고리즘
- 리눅스 CPU 스케줄링
(2) 메모리 관리 : 가상 메모리
운영체제는 새롭게 실행하는 프로그램을 메모리에 적재하고 종료된 프로그램을 적절히 삭제해야 한다. 이때 낭비되는 메모리 용량이 없도록 효율적으로 관리해야 할 노력도 필요한다. 오늘날 운영체제들 대부분은 이를 위해 가상 메모리 기술을 활용한다. 이또한 추후 학습하겠지만 핵심은 다음과 같다.
- 기본 개념 : 물리 주소와 논리 주소
- 메모리 할당
- 페이징과 페이지 교체 알고리즘
(3) 파일 / 디렉토리 관리 : 파일 시스템
보조기억장치는 용량이 더 큰 만큼 효율적으로 관리할 필요가 있다. 아무렇게나 데이터를 저장한다면 어질러진 곳에서 물건을 찾듯 탐색 시간에 비효율적으로 많은 시간을 낭비하게 된다. 운영체제는 보조 기억 장치를 효율적으로 관리하기 위해 파일 시스템을 사용한다. 파일 시스템은 크게 정보를 파일 및 폴더 단위로 접근하고 관리하도록 만들어진 운영체제의 내부 프로그램이다. 핵심은 다음과 같다.
- 파일과 디렉토리
- 파일 시스템
(4) 프로세스 및 스레드 관리
단순히 실행 중인 프로그램이라고 지금까지 지칭했던 대상들은 프로세스(process)라고 불린다. 그리고 프로세스의 내부적으로 프로세스를 이루는 실행의 단위를 스레드(thread)라고 부른다. 메모리에는 여러 프로세스가 적재될 수 있고 이 프로세스에 대해서 운영체제는 자원을 할당해 스레드를 통해서 작업을 수행할 수 있다. 운영체제는 동시다발적으로 실행되는 프로세스와 스레드가 올바르게 처리되도록 실행 순서를 제어하고 그들이 요구하는 자원을 적절히 배분해야 한다. 어떻게 다뤄야 하는지 어떤 부분을 고려해야 하는지에 대한 자세한 내용은 아래에서 좀 더 다뤄보겠다.
1-3 운영체제 학습 지도
앞에서 설명했던 부분들을 핵심적인 대화 내용을 기준으로 작성한다면 다음과 같이 정리할 수 있겠다.
운영체제
|- 운영체제의 큰 그림
|- 커널
|- 시스템 콜
|- 프로세스 및 스레드 관리
|- 프로세스와 스레드
|- 동기화와 교착 상태
|- 자원 할당 및 관리
|- CPU 관리 : CPU 스케줄링
|- 기본 개념 : 우선순위, 스케줄링 큐, 선점형과 비선점형
|- CPU 스케줄링 알고리즘
|- 리눅스 CPU 스케줄링
|- 메모리 관리 : 가상 메모리
|- 물리주소와 논리주소
|- 메모리 할당
|- 페이징과 페이지 교체 알고리즘
|- 파일 / 디렉토리 관리 : 파일 시스템
|- 파일과 디렉토리
|- 파일 시스템
1-4 시스템 콜과 이중 모드
앞에서 운영체제가 응용 프로그램들이 효율적으로 실행될 수 있도록 자원을 관리하고 할당한다는 말을 했지만, 운영체제 역시 프로그램이기 때문에 메모리에 적재되어야만 한다. 다만 일반적인 응용 프로그램과 달리 운영체제는 커널 영역(kernel space)이라는 공간에 따로 적재되어 실행된다. 메모리는 크게 커널 영역과 일반적인 응용 프로그램이 적재되는 사용자 영역(user space)으로나뉘어 적재하며, 결국 운영체제의 기능을 제공받으려면 커널 영역에 있는 운영체제 코드를 실행해야 한다.
일반적인 응용 프로그램은 자원에 접근할 권한이나 기능이 없기 때문에 운영체제가 그 역할을 대행해서 수행한다. 이때 결국 응용 프로그램이 운영체제 코드를 실행해 자원에 접근해야 하는데, 이를 위해 응용 프로그램은 시스템 콜(system call)을 호출해 실행한다.
시스템 콜은 운영체제의 서비스를 접근받기 위한 인터페이스로 호출 가능한 함수의 형태를 지닌다. 응용 프로그램이 운영체제에게 원하는 기능을 제공받고 싶다면 해당하는 시스템 콜을 호출함으로써 제공받을 수 있다. 순서는 아래와 같다.
소프트웨어 인터럽트 발생 -> | CPU의 커널 모드 전환 -> | 운영체제 코드 실행 -> | 사용자 모드로 재전환 |
다만 위의 그림에서처럼 핵심은 소프트웨어 인터럽트, 사용자 모드, 커널 모드인데 각각 설명하면 다음과 같다.
- 소프트웨어 인터럽트 : 운영체제에서 인터럽트를 발생시키는 특정 명령어들로, 자원에 접근하는 입출력 명령 같은 경우가 대표적이며 시스템 콜 역시 이 소프트웨어 인터럽트의 일종이다.
- 인터럽트가 발생하면 일반적인 과정처럼 CPU는 현재 동작을 멈추고 백업한 뒤, 인터럽트를 처리하기 위한 코드를 실행한 뒤 해결되면 다시 사용자 영역의 코드를 재개한다.
- CPU는 명령어를 실행하는 과정에서 사용자 영역과 커널 영역을 실행할 때의 모드를 나눠 설명하며, 이게 사용자 모드, 커널 모드라고 한다. 그리고 이 2개의 모드로 구분해서 실행하는 걸 이중 모드라고 부른다.
- 사용자 모드 : 운영체제 서비스를 제공받을 수 없는 실행 모드로 커널 영역의 코드를 실행할 수 없다.
- 커널 모드 : 운영체제 서비스를 제공받을 수 있는 실행 모드로 커널 영역의 코드를 실행할 수 있다.
2. 프로세스 & 스레드
컴퓨터가 실행되는 순간부터 다양한 프로세스(프로그램)들이 메모리에 적재되어 실행된다. 프로세스는 사용자가 보이는 공간에서 상호작용하는 foreground process나 보이지 않는 곳에서 실행되는 background process 등이 있으며, 백그라운드 프로세스 중에는 사용자와 상호작용 없이 자신에게 주어진 임무만 수행하는 데몬(윈도우에서는 service)도 있다.
프로세스의 유형과 상관없이, 프로세스를 구성하는 메모리 내의 정보는 아래와 같은 구조에서 크게 벗어나지 않는다.
- 커널 영역 : 프로세스 제어 블록(PCB)이 저장된다.
- 사용자 영역 : 코드, 데이터, 힙, 스택 영역으로 나뉘어 저장된다.
좀 더 이해하기 쉬운 사용자 영역부터 차례대로 하나씩 간단히 알아보자.
- 코드 영역 : 읽기 전용의 CPU가 실행 가능한 명령어가 저장되는 공간으로 텍스트 영역이라고도 불린다.
- 데이터 영역 : 프로그램이 실행되는 동안 유지할 데이터가 저장되는 공간이며, 여기에 저장되는 대표적 데이터는 정적, 전역 변수가 대표적이다.
- 힙 영역 : 프로그램을 만드는 개발자가 직접 할당 가능한 저장공간이다. 다만 반환하지 않으면 메모리 누수 같은 문제가 발생하기 때문에 주의가 필요하다.
- 스택 영역 : 데이터 영역에 적재되는 변수와 달리 일시적으로 사용되는 일반 변수들이 저장되는 공간이다. 함수 종료 시 사라지는 매개변수, 지역 변수, 함수 복귀 주소 같은 것이 대표적이다. 스택 영역에는 스택 트레이스 형태의 함수 호출 정보가 저장되는데, 이는 특정 시점에 스택 영역에 저장된 함수 호출 정보를 의미하며 문제의 발생 지점을 추적할 수 있어 디버깅 시 유용하게 사용된다.
PCB와 문맥 교환
위에서 커널 영역에 PCB가 저장된다고 설명했었는데, PCB는 구체적으로 설명하면 운영체제가 다수의 프로세스를 관리하기 위해 프로세스를 식별할 수 있는 정보이다. PCB는 프로세스와 관련한 다양한 정보를 저장하는 구조체의 일종으로 새로운 프로세스가 메모리에 적재되면 생성되고 실행이 종료되면 폐기된다.
PCB의 정보는 커널 내에서 프로세스 테이블 형태로 관리되며 이는 실행 중인 PCB의 모음을 의미한다. PCB 내부의 정보는 약간씩 차이는 운영체제마다 존재하나 대표적인 정보는 다음과 같다.
- 프로세스 ID(PID) : 프로세스 식별 번호
- 프로세스가 실행과정에서 사용한 레지스터 값
- 현재 프로세스의 상태를 나타내는 정보
- CPU 스케줄링(우선순위) 정보 : 프로세스가 언제 어떻게 CPU를 할당받는지 나타내는 정보
- 프로세스의 메모리 상 적재 위치가 표시된 메모리 관련 정보
- 프로세스가 사용한 파일 및 입출력 장치 정보
프로세스가 실행된다는 것은 운영체제의 CPU의 자원을 할당받았다는 말과 같다. 프로세스의 CPU 사용 시간은 타이머 인터럽트에 의해 제어된다. 타이머 인터럽트는 시간이 끝났음을 알리는 인터럽트로 타임아웃 인터럽트라고 불리기도 한다.
프로세스는 타이머 인터럽트 발생 전에 지금까지의 중간 정보를 백업해야 다음 차례가 되었을 때 이어서 업무를 재개할 수 있다. 여기서 백업 대상이 되는 중간 정보, 재개를 위해 기억해야 할 정보를 문맥(context)라고 한다. 이 문맥은 해당 프로세스의 PCB에 명시되며 타이머 인터럽트 발생 시에 이 부분에 백업하고 다음 실행할 프로세스의 문맥을 복구하며 자연스레 바뀌게 된다.
이처럼 기존 프로세스의 문맥을 백업하고 새 프로세스의 문맥을 복구하는 일련의 과정을 문맥 교환(context switch)라고 부른다.
프로세스의 상태
프로세스는 여러 상태를 거치며 실행된다. 운영체제는 PCB를 통해 이런 상태를 인식하고 관리한다. 운영체제 별로 상태를 표현하는 방식은 대동소이하며 다음과 같은 그림으로 표현이 가능하다.
- 생성(new) : 생성 중인 상태로, 메모리에 적재되어 PCB를 할당받은 상태이다.
- 준비(ready) : 당장 할당받아 실행 가능한 상태지만 차례가 아니라 기다리고 있는 상태를 의미한다. CPU를 할당받아 실행 상태로 전환되는 것을 디스패치(dispatch)라고 한다.
- 실행(running) : 실제 CPU를 할당받아 실행 중인 상태로 정해진 시간 동안만 실행 가능하며, 할당된 시간을 모두 사용하면 준비 상태로 돌아가게 된다. 다만 실행 도중 입출력 장치를 사용하게 된다면 입출력 신호를 발생하고 작업이 끝날 때까지 대기 상태가 된다.
- 대기(blocked) : 프로세스가 입출력 작업을 요청하거나 확보할 수 없는 자원을 요청하는 등 곧장 실행이 불가능한 조건에 놓이는 경우 대기 상태가 된다. 대기 상태는 다양한 경우가 있지만 입출력 작업이 대표적이다. 다시 실행 가능한 상태가 되면 준비 상태로 전환되어 CPU 할당을 기다린다.
- 종료(terminated) : 모든 작업이 종료되어 운영체제가 PCB와 메모리를 정리한다.
멀티프로세스와 멀티스레드
한 프로세스를 구성하는 코드를 동시에 실행하려면 어떻게 해야 될까? 한 가지는 같은 프로그램에 대해서 서로 다른 여러 프로세스를 실행하는 방법이 있겠다. 대표적으로 웹 브라우저의 탭 기능이 있으며, 이와 같이 동시에 여러 프로세스가 동시에 실행되는 것을 멀티프로세스라고 부른다. 코드를 동시에 실행하는 또 다른 방법은 여러 스레드를 이용하는 방법이 있는데, 이를 멀티스레드라고 부른다.
멀티프로세스와 멀티스레드의 가장 큰 차이점은 자원의 공유 여부에 있다. 멀티프로세스는 자원을 서로 공유하지 않고 독립적으로 실행되는 반면, 멀티스레드의 경우 하나의 프로세스의 자원을 공유한다. 같은 자원을 공유하기 때문에 쉽게 협력하고통신 가능하지만, 한 스레드의 문제가 다른 스레드를 넘어 전체 프로세스의 영향을 끼칠 수 있다.
프로세스 간 통신
앞에서 멀티프로세스에 대해서 이야기할 때, 기본적으로 자원을 공유하지 않는다고 했었다. 하지만 그럼에도 서로 자원을 공유하고 데이터를 주고받을 수 있는 방법이 있는데 이를 프로세스 간 통신(IPC : inter-Process Communication)이라고 부른다. IPC는 크게 공유 메모리와 메시지 전달 방식이 있다. 공유 메모리는 데이터를 주고받는 프로세스가 공통적으로 사용할 메모리 영역을 두는 방식이고, 메시지 전달은 프로세스 간에 주고받을 데이터를 메시지 형태로 주고받는 방법을 의미한다. 각각의 특징은 다음과 같다.
공유 메모리 기반 IPC
- 마치 자신의 메모리 영역을 읽고 쓰는 것처럼 통신한다.
- 커널 영역을 거치지 않는 경우가 많다.
- 각 프로세스가 각자의 메모리 영역을 읽고 쓰는 것 뿐이므로 속도가 메시지 전달 방식보다 빠르다.
- 동시에 공유 메모리 영역을 읽고 쓰면 데이터 일관성이 훼손될 문제가 있다. 이를 레이스 컨디션이라고 부른다.
메시지 전달 기반 IPC
- 메시지를 보낼 수단과 받는 수단이 명확하게 구분되어 있다.
- 커널의 도움을 적극적으로 받기 때문에 레이스 컨디션 문제가 적다.
- 커널을 통해 송수신되므로 공유 메모리보다는 통신 속도가 늦다.
- 대표적인 수단으로는 파이프, 시그널, 소켓, 원격 프로시저 호출 등이 있다.
- 파이프는 단방향 프로세스 간 통신 도구를 말한다.
- 시그널은 프로세스에게 특정 이벤트가 발생했음을 알리는 비동기 신호를 의미한다. 시그널이 발생하면 프로세스는 인터럽트 처리과정과 유사하게 시그널 처리를 위한 시그널 핸들러를 실행한 뒤, 해결하고 나서 실행을 재개한다.
참고
'Computer Science' 카테고리의 다른 글
[CS] 3-3. 운영체제 - 가상 메모리, 파일 시스템 (0) | 2024.10.09 |
---|---|
[CS] 3-2. 운영체제 - 동기화와 교착 상태, CPU 스케줄링 (4) | 2024.10.05 |
[CS] 2-3. 컴퓨터 구조 - 메모리, 보조기억장치, 입출력장치 (4) | 2024.09.27 |
[CS] 2-2. 컴퓨터 구조 - CPU, 레지스터, 인터럽트 (2) | 2024.09.25 |
[CS] 2-1. 컴퓨터 구조 - 개요, 정보 (0) | 2024.09.12 |