이전에 프로세스와 멀티쓰레딩에 관련된 게시물을 올렸는데, 인터럽트 시스템콜에 대한 정의와 동작 과정에 대한 설명 없이 컨텍스트 스위칭, 동기 비동기 IO에 대한 내용을 다루니 글이 난잡하고 가독성이 참 떨어진다는 생각을 했습니다.
프로세스, 멀티쓰레딩, 동기화문제 운영체제의 중요한 챕터를 공부하기 전에 반드시 알아야 하는 컴퓨터 시스템의 간단한 구조와 커널모드, 사용자모드, 인터럽트, 시스템 콜 등 꼭 중요한 개념을 이 포스팅에서 정리하려고 합니다.
1. 간략한 컴퓨터 시스템
인터럽트와 시스템콜에 내용을 정리하기 전에, 컴퓨터 시스템의 대략적인 구조를 살펴보도록 하겠습니다. 구성 요소를 소개하면서 그 역할에 대해 간략히 소개할 것인데, 자세한 개념은 뒤에 다시 설명할 예정입니다.
CPU 구조
CPU
CPU는 프로그램 연산을 실행하고 처리하는 가장 핵심적인 컴퓨터의 제어장치입니다. 매 클락 사이클마다 메모리에서 기계어를 한줄씩 읽어서 실행합니다.
레지스터
CPU 연산에 필요한 데이터와 명령어를 저장하는 공간입니다. 값을 저장하는 데이터 레지스터, 메모리 접근을 위해 메모리 주소를 저장하는 주소 레지스터, 그리고 다음에 실행할 명령어의 주소를 저장하는 program counter 등 여러 종류의 레지스터가 있습니다. 레지스터는 CPU 안에 있는 메모리이기 때문에 위 그림의 메인 메모리보다 접근하는 시간이 빠르지만, CPU는 연산에 특화돼 있는 장치이기 때문에 저장할 공간이 매우 부족합니다.
메모리
CPU 밖에 바로 옆에 붙어서 저장 공간 역할을 합니다. 램이라고도 부르는데, 전기가 있을 때만 유지되는 휘발성 메모리입니다. 일반적으로 프로세스를 정의할 때 "메모리에 올라가 있는 프로그램" 에서 통상 말하는 메모리입니다. CPU는 오직 메모리만 소통하는 일꾼입니다. CPU는 program counter가 가리키는 메모리 위치에서 명령어를 읽어와서 연산을 수행합니다. 연산을 수행한뒤 pc가 가리키는 주소가 4바이트씩 순차적으로 증가하고 명령어를 순차적으로 진행합니다. 꼭 순차적으로 실행되는 것은 아니고 반복문이든 다른 함수 호출이든 다른 메모리 공간으로 점프하기도 합니다. 그리고 인터럽트가 일어나도 다른 메모리 공간으로 점프합니다. 인터럽트에 대한 개념은 뒤에서 설명하겠습니다.
인터럽트 라인 (interrupt line)
인터럽트라인은 CPU가 인터럽트의 발생을 감지하는데 사용됩니다. 뒤에서 자세히 설명하겠지만 인터럽트를 간단하게 설명하자면, CPU에서 실행되고 있는 프로그램을 일시 정지시키는 것입니다. CPU는 한 순간에 하나의 프로그램만 실행시킬 수 있고, 인터럽트의 목적 중 하나는 가장 효율적으로 CPU를 사용시키기 위함으로 우선 이해하면 좋을 것 같습니다.
그렇다면 인터럽트가 발생한다는 것을 어떻게 감지할 수 있을까요? 프로그램이든 하드웨어든 어떤 이유로 인터럽트를 발생시키면 인터럽트 라인에 몇 번 라인의 명령어에서 인터럽트가 일어났는지, 인터럽트 종류 별로 어떤 명령어를 실행할지작성합니다. 그리고 CPU는 연산만 계속 수행하는 것이 아니라 연산을 어느 정도 수행하고 나면 주기적으로 인터럽트라인을 확인하고 인터럽트가 발생하면 하던 연산을 멈추고, 제어권을 운영체제로 넘깁니다.
모드비트
사용자 프로그램이 모든 명령어를 실행할 수 있는 것은 아닙니다. 하드웨어나 시스템에 직접 접근하는 명령어는 안정성과 보안의 이유로 운영체제만이 가능합니다. 모드비트는 CPU에게 해당 명령어가 사용자 모드의 명령어인지 커널 모드의 명령어인지 가르쳐주는 역할을 합니다.
I/O devices
컴퓨터가 요청한 데이터를 입력받거나, 결과물을 출력하는 장치입니다.
device controller
IO device의 동작을 CPU가 컨트롤하는 것은 아닙니다. 각 디바이스마다 I/O 장비를 전담하는 작은 CPU가 있는데 이를 device controller라고 부릅니다. CPU가 메모리를 가지고 있는 것처럼 Device controller도 local buffer를 가지고 있습니다. 일종의 data buffer로 예를 들어 키보드에서 들어온 값을 메인 메모리에 보내기 전에 임시로 저장하는 공간입니다.
2. 커널모드 사용자모드 모드비트
커널모드, 사용자모드
커널은 운영체제의 핵심이 되는 컴퓨터 프로그램으로, 시스템의 전반을 관리 감독하는 역할을 합니다. CPU, memory, device 등의 하드웨어와 응용 프로그램 사이에서 인터페이스를 제공하고, 한정된 시스템 자원을 효율적으로 관리하는 역할을 합니다.
위에서 언급했듯이, 사용자 프로그램은 하드웨어나 시스템에 직접 접근할 수 없습니다. 프로그램이 함부로 하드웨어를 점유해서 다른 프로세스에 영향을 끼칠수도 있고, 접근하면 안되는 메모리에서 보안 문제를 일으킬 수도 있기 때문입니다.
그래서 CPU는 사용자모드와 커널모드를 분리해서 사용자모드에서는 시스템콜, 인터럽트 등 커널(운영체제)이 실행하는 커널코드를 사용하지 못하게 제한합니다. 커널코드를 호출하려면 그 전에 커널모드로 바꿔서 CPU가 모든 기계어를 실행할 수 있는 상태로 만들어야합니다. 그렇다면 CPU는 명령어를 수행하기 전에 커널모드인지 사용자모드인지 어떻게 알 수 있는 것일까요?
모드 비트 (mode bit)
CPU 내부에서 모드 비트를 저장해서 커널모드와 사용자모드를 구분합니다. 커널모드에서는 모드비트가 0이고, 사용자모드에서는 모드비트가 1입니다.
사용자 프로그램 중에 Interrupt나 Exception 발생하면 프로그램을 멈추고 커널모드로 바꾸기 위해 모드비트를 0으로 바꿉니다. 모드비트가 0이 됐으므로 CPU가 운영체제에게 넘어가고 메모리나 I/O 디바이스에 접근할 수 있습니다. 그리고 커널코드가 끝나고 처리가 완료되면 다시 유저모드로 바꿉니다. 모드비트를 1로 바꾸고 중단됐던 프로그램의 CPU상태를 복원하고 사용자 프로그램이 CPU를 점유하게 됩니다.
유저모드에서 I/O 디바이스에 접근하는 명령어를 사용할 수 없다면, 사용자 프로그램은 어떻게 I/O 호출을 할 수 있는것일까요?
3. 시스템콜(System call)
시스템콜이란?
시스템콜은 사용자 프로그램이 운영체제에 있는 함수를 사용하기 위해 운영체제에게 서비스를 요청하는 것을 말합니다. 모든 입출력 명령은 운영체제만이 실행할 수 있다고 했습니다. 그리고 CPU는 메모리에서 명령어를 읽고 수행하고 PC가 순차적으로 메모리 주소를 4바이트씩 옮기면서 순차적으로 명령을 실행한다고 했습니다. 순차적으로만 실행되지는 않고 CPU에서 어떤 함수를 실행하다가 다른 함수를 호출하면 명령어의 메모리 위치가 다른 곳으로 점프합니다.
그런데 사용자 모드에서 사용할 수 있는 함수가 아닌 I/O 요청이 와서 PC의 포인터를 커널 명령어가 차지는 메모리로 가리켜야한다고 합시다. 사용자 프로그램은 모드비트가 1이기 때문에 직접 PC를 커널 명령어의 메모리로 옮길 수 없습니다. 그래서 프로그램이 직접 운영체제에게 I/O 작업을 요청하기 위해 인터럽트를 겁니다. 그러면 CPU는 명령어를 실행하던 중에 작업을 멈춥니다.
프로그램이 interrupt line에 인터럽트가 발생했다고 보고했기 때문에, 모드비트를 0으로 바꾸고 CPU의 제어권을 운영체제에게 넘깁니다. 사용자 프로그램이 시스템콜로 운영체제에게 I/O 작업을 부탁했기 때문에, 위의 과정을 거쳐서 디스크 컨트롤러에게 무언가 가져오라고 명령할 수 있고 혹은 화면에 무엇인가 출력할 수 있게 됐습니다.
4. 인터럽트 (Interrupt)
시스템에서 발생한 다양한 종류의 이벤트 혹은 이벤트를 알리는 메커니즘입니다. CPU는 매 순간 하나의 프로그래밍만 실행할 수 있습니다. 인터럽트는 다양한 이유로 발생하는데, I/O작업을 요청하거나 완료됐을 때 혹은 프로그램이 어떤 값을 0으로 나눴을 때, 잘못된 메모리 공간에 접근을 시도할 때, 다양한 이유로 발생합니다. 그렇게 인터럽트가 발생하면 당한 시점의 cpu 상태를 저장하기 위해 그 시점의 레지스터 정보와 program counter를 저장하고 커널 모드로 바꾼 뒤에 CPU의 제어권을 인터럽트 처리 루틴으로 넘깁니다.
하드웨어 인터럽트
아래 이미지는 처음에 간단하게 컴퓨터 시스템을 설명하기 위해 첨부한 이미지입니다. DBA controller와 timer에 대해 설명하지 않았는데, DBA controller와 timer가 어떤 역할을 하는지, 어떻게 인터럽트를 발생시키는지 소개하겠습니다.
타이머
앞에서 인터럽트의 목적 중 하나가 CPU를 효율적으로 사용하기 위함이라고 설명했습니다. 만약 어떤 프로그램이 I/O작업을 요청하고 아무일도 안한다거나 무한루프를 돌면서 CPU 제어권을 넘겨주지 않는다고 가정합시다. 특정 프로그램이 CPU를 독점하는 상황이 생겨서 CPU가 그 프로그램을 멈추고 다른 프로그램을 사용할 수 있도록 타이머라는 하드웨어를 만들었습니다.
프로그램이 CPU를 점유하기 전에 타이머에 최대 프로그램을 실행할 수 있는 시간을 미리 정해둡니다. 미리 설정한 시간이 지나면 타이머가 CPU에게 인터럽트를 걸어줍니다. 그러면 운영체제가 사용자 프로그램으로부터 CPU를 뺒을 수 있고 여러 프로그램을 번갈아가면서 실행할 수 있게 됩니다.
DMA
앞에서 메인메모리에 접근할 수 있는 하드웨어는 CPU뿐이고, I/O 디바이스의 소형 CPU역할을 하는 각자의 device controller는 각자의 임시 버퍼에 접근할 수 있다고 설명했습니다. 하지만 CPU뿐만 아니라 메모리에 접근할 수 있는 다른 하드웨어가 있는데 그것이 바로 DMA입니다.
DMA의 역할은 무엇일까요? 인터럽트는 사용자 프로그램이 I/O 작업을 요청할 때 뿐만 아니라, I/O device가 요청한 작업을 끝마치고 메인 메모리에 버퍼에 있는 내용을 복사할 때도 발생합니다. 그런데 키보드 입력처럼 한 자 한 자 입력할 때마다 CPU에 인터럽트를 걸면, 인터럽트가 너무 많이 생겨서 상당한 오버헤드가 생기고 CPU의 효율이 감소합니다.
그래서 인터럽트의 빈도를 줄이기 위해 DMA라는 하드웨어를 도입합니다. 디바이스가 I/O 처리를 할 때마다 메인메모리에 복사하는 것이 아니라, DMA가 디바이스의 내용을 메모리에 직접 카피하면서 일정 용량만큼 블럭에 해당하는 I/O처리가 끝나면 인터럽트를 한번 걸어서 CPU에게 보고합니다. 이런 과정을 통해 CPU가 더 효율적으로 일할 수 있게 됩니다.
소프트웨어 인터럽트
소프트웨어 인터럽트를 보통 Trap이라고 부릅니다. 프로그램이 오류를 내거나, 시스템콜 즉 I/O 작업을 위해 커널 함수를 호출할 때 해당됩니다.
인터럽트 벡터
인터럽트 관련 용어를 간략하게 소개하겠습니다.
- 인터럽트 벡터 : 인터럽트의 처리 루틴을 저장하는 메모리의 주소를 가리키는 벡터
- 인터럽트 처리 루틴 : 해당 인터럽트를 처리하는 커널 함수
I/O 요청과 처리에서 인터럽트의 수행 과정은 아래와 같습니다.
- 사용자 프로그램이 운영체제에게 I/O 작업을 요청합니다.
- Trap을 사용해서 사용자 프로그램을 인터럽트합니다. 인터럽트 벡터가 해당하는 인터럽트를 처리하는 루틴의 주소를 가리킵니다.
- 제어권이 인터럽트 벡터가 가리키는 인터럽트 서비스 루틴으로 이동합니다.
- 올바른 I/O 요청인지 확인한 뒤에 I/O 작업을 수행합니다.
- I/O 작업 완료 시 제어권을 시스템콜의 다음 명령으로 다시 옮깁니다.
지금까지 인터럽트가 무엇인지, 하드웨어와 소프트웨어에서 인터럽트가 어떻게 들어가는지 예시와 과정을 살펴봤습니다.
5. 요약
컴퓨터 시스템 구조를 통해서 CPU의 역할과 레지스터, 인터럽트 라인, 모드 비트에 대해 알아봤습니다. 그리고 모드 비트에 따른 커널모드, 사용자모드에 대해 알아봤고, 사용자모드에서 어떻게 시스템콜을 통해 I/O작업을 수행하는지, 그리고 하드웨어와 소프트웨어에서 인터럽트가 어떻게 발생하는지 예시와 과정을 살펴봤습니다. 아래에 전체 내용을 요약해서 정리하겠습니다.
컴퓨터 시스템
컴퓨터 시스템은 CPU와 메모리로 구성돼 있습니다. 그리고 I/O 디바이스를 통해 데이터를 입력하거나 결과를 출력할 수 있습니다. 각 디바이스마다 전담하는 device controller가 있고, device가 작업을 수행할 때 데이터를 버퍼에 저장합니다. CPU에 I/O 작업이 끝난 것을 알려주기 위해 device controller가 인터럽트를 걸고, CPU가 버퍼의 데이터를 메인 메모리에 복사합니다.
인터럽트라인
CPU는 일꾼입니다. PC가 가르키는 메모리의 명령어를 읽어서 실행하는 일만 합니다. 그래서 CPU는 명령어를 언제 멈추고 다른 프로그램을 실행시킬지 알 수 없습니다. 그래서 인터럽트 라인에 인터럽트에 대한 정보를 저장하고 CPU는 명령어를 실행하고 인터럽트 라인을 체크합니다. 인터럽트 라인에 인터럽트가 발생한다고 적혀있으면 CPU를 누가 쓰고 있던 상관없이 제어권을 운영체제에게 넘기고 운영체제가 인터럽트를 상황에 맞게 처리합니다.
인터럽트 처리 루틴
인터럽트에 대한 함수가 커널 함수에 정의 돼있는데, 인터럽트 종류별로 어떤 명령어를 실행해야하는지 저장돼있습니다. 그래서 실제 인터럽트가 발생하면 PC가 인터럽트 처리 루틴이 저장된 메모리를 가르킵니다.
모드 비트
하지만 사용자 모드에서는 명령어 포인터를 함부로 커널이 저장된 메모리로 옮길 수 없습니다. 운영체제에게 대신 일을 처리해달라고 부탁하기 위해 커널모드로 바꾸기 위해 모드비트를 0에서 1로 바꿉니다. (0이 사용자모드 1이 커널모드) 커널모드로 바뀌면 CPU는 모든 기계어를 실행할 수 있습니다.
시스템콜
위처럼 PC가 커널의 영역으로 점프를 하기 위해 사용자 프로그램이 운영체제에게 서비스를 요청하는 것을 시스템콜이라고 합니다. 운영체제에 있는 함수를 사용자 프로그램이 요청하는 것인데, 아직 사용자 모드이기 때문에 점프할 수 없습니다. 그래서 의도적으로 인터럽트를 일으켜서 CPU는 하던 일을 멈추고 CPU의 제어권을 운영체제로 넘깁니다.
인터럽트의 종류
I/O 작업을 마치고 CPU에게 보고하기 위한 하드웨어 인터럽트가 있고, 사용자 프로그램의 필요에 의해 요청하는 시스템콜과 같은 소프트웨어 인터럽트(trap)가 있습니다.
이번 포스팅이 앞으로 운영체제를 공부하는데 필요한 기본 개념을 익히는데 도움이 됐으면 좋겠습니다.
reference
2. 쉬운코드
4. Operating System Concepts 10th edition
'CS > 운영체제' 카테고리의 다른 글
[OS] 뮤텍스, 세마포어, 모니터 (0) | 2023.01.18 |
---|---|
[OS] 동기화 문제, Peterson's solution, 하드웨어 명령어 (0) | 2023.01.15 |
[OS] CPU scheduling (0) | 2023.01.11 |
[OS] Threads & Concurrency (0) | 2023.01.11 |
[OS] 프로세스 : 프로세스 개념, 컨텍스트 스위칭, 생성 및 통신 (0) | 2023.01.08 |