1. 동기식 입출력(synchronous I/O)
I/O 요청 후 입출력 작업이 완료된 후에야 제어가 사용자 프로그램에 넘어가는 방식입니다.
그림에는 user와 kernel 두 가지로 나뉘어진 것을 확인할 수 있습니다. 이전 포스팅에서, 만약 user 부분에서 I/O작업을 진행하려면 I/O작업은 커널에서 수행하기에 사용자 프로그램이 I/O작업을 진행할 수 없다고 했습니다.
그래서 사용자 프로그램은 운영체제의 커널에 I/O 요청을 하게 됩니다. 이후 해당 I/O에 맞는 Device driver를 거치게 되고, Device Controller를 통해 입출력 작업을 하게 됩니다. 작업이 끝나면 Device Controller 가 CPU에게 인터럽트를 걸어 작업이 완료되었다는 것을 알리고 사용자 프로그램의 다음 작업을 수행합니다.
동기식 입출력에는 두 가지 구현 방법이 있습니다.
1. I/O가 끝날 때까지 CPU를 낭비시키며, 매시점 하나의 I/O만 일어날 수 있다.
2. I/O가 완료될 때까지 해당 프로그램에서 CPU를 빼앗고, I/O 처리를 기다리는 중에 그 프로그램을 줄 세워놓고 다른 사용자 프로그램에 CPU 제어권을 넘겨준다.
I/O는 오래 걸리는 작업이기에 작업동안 CPU 제어권을 가진채 아무것도 안 하고 기다리면 CPU가 굉장히 낭비가 됩니다.
그래서 보통 2번째 구현 방법을 많이 사용합니다. 2번째 구현 방법을 사용하면, I/O 처리 과정 중 해당 I/O장치에 작업을 주어도 일을 하지 못하기 때문에 다른 프로그램에 CPU를 넘겨줍니다. 넘겨받은 프로그램이 I/O 요청을 보내면 또다시 다른 프로그램에 CPU를 넘길 수 있게 됩니다. 이 과정은 CPU가 효율적으로 일을 할 수 있다는 장점도 있지만 I/O 장치들도 여러 개가 동시에 일을 할 수 있다는 장점이 있습니다.
2. 비동기식 입출력(asynchronous I/O)
I/O 요청 후 입출력 작업이 끝나는 걸 기다리지 않고 CPU 제어권이 사용자 프로그램에 즉시 넘어가는 방식입니다.
그림을 보시면 위의 동기식 입출력과 가장 큰 차이는 사용자에서 커널로 향하는 화살표가 다른 것을 확인할 수 있습니다. 비동기식 입출력은 kernel에 I/O 요청만 해놓고 바로 CPU 제어권을 얻어 다른 작업을 진행한다는 차이가 있습니다. I/O 작업이 완료가 되었다면 동기식과 마찬가지로 CPU에게 인터럽트를 통해 알려줍니다.
3. 저장장치 계층 구조
저장 장치의 계층 구조는 크게 CPU에서 직접 접근할수 있고 실행 가능한 Primary 계층과 CPU에서 직접 접근이 불가능한 Secondary 계층으로 나뉩니다.
우선 Primary 계층부터 살펴보겠습니다. 사진의 맨 위에는 CPU가 있다고 생각하면 됩니다. CPU 아래에 Registers가 위치하고 아래에 Cashe Memory, D램으로 구성된 Main memory가 위치합니다.
Primary 계층은 휘발성(Volatility)으로 전원이 꺼지면 사라지는 특성이 있습니다. 또 CPU에서 직접 접근이 가능한 바이트의 저장 단위로 구성되어 CPU에서 바로 실행이 가능합니다.
Secondary 계층은 하드디스크, 플래시메모리, 마그네틱 테이프로 구성되어 있습니다.비휘발성 메모리로 전원이 꺼져도 내용이 유지되는 특징이 있습니다.
위 사진에서 위로 갈수록 속도가 빠르지만 단위 공간당 가격이 바쌉니다. 그래서 용량이 적은 특징이 있습니다.
4. 프로그램의 실행
일반적으로 프로그램은 실행 파일(. exe) 형태로 File System에 저장됩니다. 실행 파일을 실행하면 메모리로 로드되어 프로세스가 됩니다. 그러나 실제로는 물리적인 메모리에 직접 로드되는 것이 아니라, Virtual Memory(가상 메모리)라는 중간 단계를 거칩니다.
프로그램을 실행하면 해당 프로그램의 독자적인 Address space(메모리 주소 공간)이 생성됩니다. 이 주소 공간은 코드, 데이터, 스택으로 구성되어 있습니다. 코드는 프로그램의 기계어 코드를 담고, 데이터는 변수와 전역 변수, 자료 구조를 저장하며, 스택은 함수 호출과 관련된 데이터를 쌓아두는 용도로 사용됩니다.
가상 메모리 공간은 이후 물리적인 메모리에 할당돼서 프로그램이 실행됩니다. 커널은 항상 메모리에 상주하고 있지만, 프로그램이 종료되면 해당 메모리는 반환됩니다. 프로그램을 실행할 때에 프로그램 파일 전부를 로드하는 것이 아니라 필요한 부분만 물리적인 메모리에 할당하여 자원이 낭비되는 것을 방지합니다.
프로그램 파일 중 필요한 것은 물리적인 메모리에 할당되고, 아닌 것은 Swap area에 보관됩니다. 파일 시스템에 저장된 데이터는 전원이 꺼져도 유지가 되지만, Swap area는 휘발성으로 일종의 메모리 연장 공간으로서 사용됩니다.
그래서 프로그램의 메모리는 스택, 데이터, 코드의 한 집합으로 묶여있는 것이 아니라 필요한 것에 따라 두 가지로 나뉘어서 위치하게 되는 것입니다. 그래서 가상 메모리라는 뜻은 논리적인 메모리의 개념을 의미하는 것이기도 합니다.
가상 메모리의 논리적인 메모리 주소가 물리적인 주소로 바뀌는 과정은 Address Translation(주소 변환)이라고 하는데 이는 이후 메모리 관리 부분 포스팅에서 다루겠습니다.
5. 커널 주소 공간의 내용
운영 체제 커널의 주소 공간은 여러 영역으로 나뉘어 있습니다.
운영 체제의 커널 주소 공간은 우선 운영체제의 역할(효율적인 자원 관리, 사용자에게 편리한 인터페이스 제공, CPU 자원할당을 위한 인터럽트 처리)을 상기한다면 쉽게 이해할 수 있습니다.
우선, 코드 영역은 운영체제가 수행하는 주요 작업들을 담은 곳입니다. 이는 자원을 효율적으로 관리하고 사용자에게 편리한 서비스를 제공하기 위한 코드로 이루어져 있습니다. 또한, CPU가 언제 어떻게 동작해야 하는지 결정하는 부분으로, 인터럽트가 발생할 때 각각의 인터럽트를 어떻게 처리해야 하는지에 대한 코드도 포함돼 있습니다.
데이터 영역에는 운영체제가 사용하는 다양한 자료 구조들이 위치합니다. CPU, 메모리, 디스크 등의 하드웨어를 효율적으로 관리하기 위해, 각 하드웨어 종류에 따라 추상적인 이미지를 만들어 자료 구조를 생성하고 관리합니다. 또한, 프로세스를 관리하기 위한 자료구조도 여기에 속하며, 현재 실행 중인 프로그램이 각자 독립적인 주소 공간을 가지더라도 해당 프로그램을 관리하기 위한 PCB(Process Control Block)와 같은 구조가 필요합니다. PCB는 각 프로세스의 상태와 관련된 정보를 담고 있으며, 이에 대한 자세한 설명은 다음 포스팅에서 다룰 예정입니다.
스택 영역은 운영체제 코드 자체도 함수 구조로 되어 있기 때문에 스택을 활용합니다. 여러 프로그램이 요청을 하고 사용할 수 있도록 운영체제는 사용자 프로그램마다 별도의 스택을 유지합니다. 이는 운영체제가 함수 호출과 관련된 데이터를 효과적으로 관리하기 위한 조치로 이해할 수 있습니다.
6. 사용자 프로그램이 사용하는 함수
사용자 프로그램은 일반적으로 함수 구조로 설계되어 있습니다. 어떤 프로그래밍 언어를 사용하든 간에, 해당 언어의 문법에 따라 작성된 코드는 컴파일되어 기계어로 변환됩니다. 이때, 기계어 부분에서 함수의 범위가 어디서 시작하고 어디까지인지 명시됩니다.
1. 사용자 정의 함수 : 개발자가 직접 작성한 함수로, 프로그램 내에서 특정 기능을 수행하도록 정의된 부분입니다. 사용자가 필요에 따라 자유롭게 정의하고 활용할 수 있습니다.
2. 라이브러리 : 유용한 기능이 이미 구현되어 있는 함수 집합으로, 다른 프로그래머가 만들어놓은 것을 가져다가 사용하는 것입니다. 이러한 함수들은 자신의 프로그램 실행 파일에 포함되어 있거나, 필요할 때 동적으로 불러와 사용할 수 있습니다.
3. 커널 함수 : 운영체제 내에서 정의된 함수로, 시스템 콜(System Call)을 통해 사용자 프로그램이 이를 호출하여 사용할 수 있습니다. 이러한 함수들은 운영체제의 핵심 기능을 수행하며, 일반적으로 인터럽트 라인을 통해 호출됩니다. 사용자 프로그램은 운영체제의 기능을 활용하기 위해 커널 함수를 호출하여 필요한 작업을 수행할 수 있습니다.
7. 프로그램의 실행 단계
1. 프로그램 로드 및 실행
- 사용자가 작성한 프로그램은 실행 파일(.exe)로 컴파일되어 저장됩니다.
- 프로그램을 실행하면 운영체제는 해당 프로그램의 가상 주소 공간을 생성하고, 코드, 데이터, 스택 등의 섹션을 할당합니다.
- 프로그램 코드가 실행되면 사용자 모드로 진입하게 됩니다.
2. 사용자 모드에서 커널 모드로 전환
- 프로그램이 특정 시스템 기능을 필요로 할 때, 시스템 콜을 호출합니다.
- 시스템 콜을 호출되면, 사용자 모드에서 실행 중인 프로그램이 커널 모드로 전환되어 운영체제 명령을 수행할 수 있도록 해줍니다.
3. 커널 모드에서의 작업 수행
- 운영체제는 해당 요청에 대한 작업을 수행합니다. ex) 하드웨어 관리, 파일 시스템 접근, 메모리 할당
- 운영체제는 이러한 작업을 완료한 후, 결과를 사용자 모드로 반환하고 다시 프로그램이 실행되는 사용자 모드로 전환됩니다.
4. 프로그램 종료
- 종료되면 운영체제는 해당 프로세스가 사용한 자원을 회수하고, 프로그램이 소유한 메모리 공간 등을 정리합니다.
참고자료
[KOCW 이화여대 반효경 교수님 - System Structure & Program Execution 2]
https://core.ewha.ac.kr/publicview/C0101020140314151238067290?vmode=f
[Operating System Concepts - Abraham Silberschatz]
https://www.yes24.com/Product/Goods/89496122