CPU
cpu에는 저장 장치인 레지스터, mode bit, interrupt line 등이 존재한다.
레지스터는 데이터를 저장하는 역할을 하고, mode bit은 현재 커널 모드(0)인지 사용자 모드(1)인지를 나타낸다. interrupt line은 하드웨어나 소프트웨어의 인터럽트를 저장하는 공간으로, 이 공간에 인터럽트가 존재하면 운영체제에게 cpu를 넘겨주게 된다.
Memory
메모리에는 사용자 프로그램과 운영체제가 저장되어 있으며 cpu가 메모리에 있는 사용자 프로그램을 번갈아가면서 실행한다.
I/O device
입출력장치를 뜻하며, disk, 키보드, 프린터, 모니터 등이 해당된다. 각 입출력장치에는 local buffer와 device controller가 존재한다. device controller는 해당 I/O 장치를 관리하는 일종의 작은 cpu이다. local buffer는 일종의 데이터 레지스터로 I/O를 모아두는 공간이다. 이에 실제 I/O는 device와 local buffer사이에 일어난다.
I/O가 끝났을 경우, 인터럽트를 발생시켜 cpu에게 그 사실을 알린다. 이 인터럽트는 위에서 설명한 것처럼 interrupt line에 저장된다.
추가로 device driver라는 것이 존재하는데, 이는 각 디바이스의 인터페이스에 맞게 접근할 수 있게 해주는 소프트웨어이다.
DMA controller & memory controller
잦은 인터럽트에 대응하기 위한 컨트롤러로 뒤에 후술한다.
timer
만약 어떠한 프로그램이 무한 루프하는 프로그램이라고 하면 그 프로그램은 cpu를 독점하게 된다. 이렇게 되면 다른 프로그램은 cpu을 할당받지 못하는 일이 생긴다. 따라서 이러한 일을 방지하기 위해 timer를 설정하여 지정한 시간이 지나면 cpu를 그 프로그램으로부터 강제로 뺏어와 다른 곳에 할당한다.
인터럽트는 하드웨어 인터럽트와 소프트웨어 인터럽트로 나뉜다.
하드웨어 인터럽트는 말 그대로 하드웨어가 발생시킨 인터럽트다.
소프트웨어 인터럽트(trap)는 두 가지 경우로 나뉜다. 첫 번째는 exception으로 프로그램이 오류를 범한 경우이고 두 번째는 프로그램이 커널 함수를 호출하는 경우다.
인터럽트를 발생시키는 경우는 대부분 운영체제가 어떠한 일을 해주길 바래서이다. 예를 들어 입출력의 수행이 있다. 모든 입출력 명령은 운영체제만이 수행가능한 특권 명령이다. 그 이유는 악의적인 사용자 프로그램이 입출력을 마음대로 사용하면 시스템을 망가뜨릴 수 있기 때문이다.
또 인터럽트를 발생시킨다는 것은 cpu의 interrupt line에 해당 인터럽트가 저장된다는 말이다. cpu가 인터럽트를 받으면, 하드웨어가 cpu의 mode bit를 0으로 바꿔 커널 모드로 세팅한다. 이러면 운영체제가 cpu를 할당받게 된다.
인터럽트는 모두 같지 않고 따라서 인터럽트의 종류(키보드, timer 등등..)도 다르므로 각 종류마다 운영체제가 해야할 일이 다르다. 해야할 일은 운영체제에 있는 코드에 정의되어 있고 그 코드를 인터럽트 처리 루틴(인터럽트 핸들러 또는 함수)라고 한다. 인터럽트 처리 루틴은 우리가 자주 볼 수 있는 함수라고 생각하면 된다. 인터럽트 처리 루틴은 메모리 주소 어딘가에 저장되어 있는데, 그 주소가 기록되어 있는 테이블 같은 곳을 인터럽트 벡터라고 한다.
인터럽트는 I/O 디바이스만 걸 수 있는 것이 아니라, 사용자 프로그램도 직접 걸 수 있다. 예를 들어 사용자 프로그램에서 어떤 파일을 읽어야된다고 할 때 disk의 I/O가 필요할 것이다. 이 때 소프트웨어 인터럽트(trap)을 사용하여 인터럽트를 걸어 해당하는 커널 함수(인터럽트 처리 루틴)를 사용할 수 있다. 이를 System call이라고 한다. trap을 사용하여 운영체제에게 I/O를 요청하면, 운영체제는 이것이 올바른 I/O요청인지 확인 후 I/O를 수행한다. 그리고 I/O 완료시 하드웨어 인터럽트를 걸어 cpu에게 작업을 종료했음을 알린다.
정리하자면, cpu는 계속해서 메모리에 올라와 있는 사용자 프로그램의 지시사항을 처리한다. 이 때 작업 하나를 실행하고 다음 작업을 실행하기 전 interrupt line을 체크하는데, 이 때 만약 인터럽트가 들어와 있다면 그 시점의 레지스터와 프로그램 카운터를 저장 후 운영체제에게 cpu제어권을 넘기고 해당하는 인터럽트 처리 루틴을 실행한다. 그리고 I/O 작업이 끝났다고 신호를 받으면, 다시 사용자 모드로 세팅하고 사용자 프로그램에 cpu를 할당하여 작업을 계속한다.
인터럽트를 계속하게 되면 문제점이 있는데, cpu는 인터럽트가 발생하면 하던 일을 멈추고 인터럽트에 관한 일을 수행한다는 것이다. 따라서 자주 인터럽트가 발생하면 심각한 오버헤드가 발생할 것이다.
이에 DMA controller가 존재한다. DMA는 direct memory access의 줄임말로, I/O 장치의 빈번한 인터럽트로 인한 cpu의 능률 저하를 막기 위해 존재한다.
보통 메모리에는 cpu만이 접근할 수 있는데, 여기에 추가로 DMA Controller도 접근할 수 있다. DMA는 cpu 대신 I/O내용을 메모리에 복사한다. 메모리에 복사할 때는 바이트 단위가 아니라 block 단위로 전송하고, 한 block 단위의 전송이 끝나면 인터럽트를 발생시켜 데이터 복사가 끝났음을 cpu에게 알린다.
위에 말했듯이 메모리에는 cpu와 DMA가 접근할 수 있다. 이에 당연히 동시성 문제가 생기는데, 이를 컨트롤해주는 것이 memory controller의 역할이다.
동기식(Synchronous) 입출력은 I/O 요청 후 입출력 작업이 완료된 후에야 제어가 사용자 프로그램으로 넘어가는 방식이다. 구현 방식에는 2가지가 있다.
구현1
구현2
현대에는 구현2 방식으로 동기식 입출력을 구현한다.
비동기식(Asynchronous) 입출력은 I/O가 시작된 후 입출력 작업이 끝나기를 기다리지 않고 제어가 사용자 프로그램에 즉시 넘어가는 것을 말한다.
위 두개의 방식은 상황에 따라 번갈아가며 쓰이고 두 경우 모두 인터럽트를 통해 I/O의 완료를 알린다.
컴퓨터의 저장공간 구조도를 나타낸 것이며, 위로 갈수록 speed, cost, volatility가 커지고 아래로 갈수록 작아진다.
먼저 파일 시스템에 존재하는 실행파일이 실행되면, 가상 메모리가 만들어지고 그 곳에 해당하는 프로세스의 Address space가 만들어진다. 주소 공간은 stack, data, code로 구성되고 역할은 다음과 같다.
code - 프로그램의 기계어 코드가 저장되는 공간
data - 변수, 전역변수 등 자료구조가 저장되는 공간
stack - 함수를 사용할 때 저장해두는 공간
또 stack, data, code는 전부 같이 저장되어야 하는 것이 아니라 따로 따로 저장되기도 한다.
가상 메모리의 주소 공간은 각 프로세스가 독자적으로 가지고 있는 메모리 주소로, 이것을 실제 메모리에 적재할 때는 진짜 메모리 주소로 변경해줘야 한다.
메인 메모리가 꽉차면 swap area로 프로세스를 내려보낸다. 이에 대한 것은 뒤에서 배울 것이다.
대략적인 내용은 위 그림과 같다. 뒤에서 더 자세히 배운다.
함수는 세 가지로 분류한다.
사용자 정의 함수
라이브러리 함수
커널 함수
반효경 교수님의 운영체제 강의를 바탕으로 작성했습니다