현재 포스팅은 반효경 교수님의 운영체제 강의 + 추가적인 내용을 바탕으로 정리된 글입니다 :)
1. 컴퓨터 시스템의 구조
컴퓨터 시스템은 컴퓨터 내부 구조는 컴퓨터 내부장치(CPU,Memory)와 컴퓨터 외부 장치(Disk,KeyBoard,Mouse,Monitor,Network Device)인 입출력 장치들로 구성되어있습니다.
이러한 메모리 및 입출력 장치 등의 각 하드웨어 장치에는 컨트롤러라는 것이 붙어있습니다. 컨트롤러란 하드웨어를 제어하는 CPU라고 할 수 있습니다.
ex) 메모리를 제어한 컨트롤러 -> 메모리 컨트롤러, 디스크를 제어하는 컨트롤러 -> 디스크 컨트롤러
2. CPU 연산과 I/O 연산
각 장치마다 설치되어 있는 컨트롤러에는 장치로부터 들어오고 나가는 데이터를 임시로 저장하기 위한 작은 메모리를 가지고 있는데 이를 로컬 버퍼라고 부릅니다.
만약 디스크나 키보드 등에서 데이터를 읽어 오는 경우, 로컬 버퍼에 컨트롤러가 데이터를 임시로 저장된 후 메모리에 전달됩니다. 이때 원하는 데이터를 다 읽어오면, 컨트롤러는 인터럽트를 발생시켜 CPU에 보고하게 됩니다(나 일 다했어!! 너가 이제 일해줘야 해!)
여기서 인터럽트란 컨트롤러들이 CPU의 서비스가 필요할때 이를 통보하는 방식을 의미합니다.
조금 더 상세히 말씀 드리자면 키보드 입력 혹은 요청된 디스크 입출력 작업 완료 등 CPU에 알려줄 필요가 있는 이벤트가 발생한 경우 컨트롤러가 발생시키는 것입니다.
그렇다면 CPU들은 인터럽트가 발생했을때 이를 어떻게 인지할까요??
CPU는 명령 하나를 수행할때마다 인터럽트가 발생하였는지 자신의(CPU) 옆에 있는 인터럽트 라인(Interrupt Line)을 확인하게 됩니다.
확인하였을 때 인터럽트 신호가 있다면, CPU는 다음 명령을 수행하기 전에 인터럽트 처리를 하게 되고, 그렇지 않으면 다음 명령을 계속 수행하게 되는 것입니다.
3. 인터럽트의 일반적인 기능
앞서 설명드린 것처럼, 프로그램이 요청한 데이터를 컨트롤러가 로컬 버퍼로 읽어온 후 인터럽트를 발생시키면 CPU는 인터럽트가 들어옴을 인지하고(인터럽트 라인 확인) 인터럽트 관련 업무를 수행합니다.
이때 업무 내용은 어디에 보관되어 있을까요??
인터럽트에 따라 해야할 일들은 운영체제 커널에 프로그래밍 되어있습니다(개발자들에 의해)
그렇다면 업무 내용은 어떤 내용일까요??
예를 들어 디스크 컨트롤러가 인터럽트를 발생시켰다고 해봅시다. 이때 CPU는 하던 일을 잠시 맘추고 인터럽트가 발생했을 때 수행해야 하도록 정의된 코드(인터럽트 처리 루틴 or 인터럽트 핸들러) 를 찾아 수행합니다.
이때 수행하는 일은 디스크의 로컬 버퍼에 있는 내용을 사용자 프로그램의 메모리로 전달하고, 해당 프로그램이 CPU를 할당받을 경우 다음 명령을 수행할 수 있음을 표시해 두는 일입니다.
운영체제는 할 일(Interrupt Service Routine, Interrupt Handler)을 쉽게 찾아가기 위해 인터럽트 벡터를 가지고 있습니다.
인터럽트 벡터란 인터럽트 종류마다 번호를 정해서, 번호에 따라 처리해야 할 코드가 위치한 부분을 가리키고 있는 자료구조입니다. 이때 처리해야 할 코드는 앞서 설명드린 것처럼 인터럽트 처리루틴 또는 인터럽트 핸들러에 정의되어있습니다.
인터럽트에는 하드웨어 인터럽트와 소프트웨어 인터럽트가 있습니다.
둘다 CPU의 서비스가 필요한 경우, CPU 옆에 있는 인터럽트 라인에 신호를 보내어 인터럽트가 발생했다는 것을 알려주는 방식은 동일합니다.
하지만 하드웨어 인터럽트는 컨트롤러 등 하드웨어 장치가 CPU의 인터럽트 라인을 세팅하는 반면, 소프트웨어 인터럽트는 소프트웨어가 인터럽트 라인을 세팅한다는 차이점을 가지고 있습니다.
통상적으로 인터럽트라고 하면 하드웨어라는 인터럽트를 의미하고, 소프트웨어 인터럽트는 trap이라는 용어로 주로 불립니다.
이러한 소프트웨어 인터럽트에 대해 좀더 상세히 알아보기 위해 예외 상황과 시스템 콜에 대해 알아보겠습니다 !
예외 상황이란 사용자 프로그램이 0으로 나누는 연산 등 비정상적인 작업을 시도하거나, 자신의 메모리 영역 바깥에 접근하려는 시도 등 권한이 없는 작업을 시도할 때 , 이에 대한 처리를 위해 발생시키는 인터럽트를 말합니다
시스템콜은 사용자 프로그램이 운영체제 내부에 정의된 코드를 실행하고 싶을 때 운영체제에 서비스를 요청하는 방법입니다.
즉 사용자 프로그램 자신의 코드는 직접 CPU를 할당받아 실행하지만, 사용자 프로그램에 정의되지 않고 운영체제 커널에 있는 코드를 사용자 프로그램이 실행하고자 할때, 인터럽트 라인 세팅을 통해 CPU 제어권을 운영체제로 넘겨 실행하게 되는 것입니다
이러한 예외 상황과 시스템콜 모두 사용자 프로세스로 부터 CPU의 제어권이 운영체제로 넘겨져 처리되는데, 이 과정에서 프로그램 코드가 직접 인터럽트 라인을 세팅하는 명령을 실행하여 인터럽트를 발생시킨 후 제어권이 넘어가게 되므로 넓은 의미에서는 인터럽트에 범주에 포함시키는 것입니다!
위 그림과 함께 인터럽트를 받을 때 CPU가 어떻게 동작하는지 좀더 상세히 알아보겠습니다 :)
- B프로그램이 실행 중인데, I/O 작업이 필요하여 시스템 콜을 통해 이를 운영체제에 요청합니다.
- 이 요청을 받은 운영체제는 CPU에게 디스크 컨트롤러에 I/O 작업을 요청하도록 명령합니다.
- CPU는 이 명령을 디스크 컨트롤러에게 전달하고, 디스크 컨트롤러는 이를 수행하겠다는 응답을 CPU에게 전달합니다. 이후 B 프로그램은 대기 상태가 되며, CPU는 다른 작업을 수행하게 됩니다(여기서는 A 프로그램 실행하는 작업)
- A라는 프로그램이 실행되고 있고, CPU내 PC(Program Counter)는 현재 실행 중인 A프로그램의 명령어 위치를 가리키고 있습니다.
- 디스크 컨트롤러가 B 프로그램의 I/O 작업을 완료하면, CPU에게 인터럽트를 발생시킵니다.
- CPU는 현재 수행 중인 A 프로그램의 작업 상태를 저장합니다. 이는 레지스터의 내용을 스택이나 프로세스 제어 블록(PCB)에 저장하는 것을 의미합니다. 이후 CPU는 인터럽트 벡터를 통해 해당 인터럽트에 대응하는 인터럽트 핸들러(또는 인터럽트 처리루틴)을 찾아갑니다. 이 과정에서 컨텍스트 스위칭이 발생합니다.
- CPU의 PC는 이제 해당 인터럽트를 처리하는 인터럽트 핸들러(커널 내부에 정의되어 있음)의 위치를 가리키게 되고, CPU는 해당 위치에서의 작업을 수행합니다. 이 작업은 주로 I/O 작업의 결과를 B 프로그램의 메모리 공간으로 복사하는 등의 작업을 포함합니다.
- B 프로그램의 인터럽트 작업이 끝나면, CPU는 이전에 저장해두었던 A 프로그램의 상태를 다시 불러와 복원합니다.
- 복원된 A프로그램의 상태로 CPU는 A 프로그램의 작업을 이어서 수행합니다.
4. 인터럽트 핸들링
앞서 그림과 함께 설명드렸던 예시에 대해 좀더 깊게 하기 위해 우선 간단한 용어 몇가지를 짚고 넘어가겠습니다 :)
우선 Register란 CPU 내부에 있는 매우 빠른 저장 공간으로, 계산에 사용되는 데이터나 중간 결과, 명령어 주소등을 보관합니다.
이러한 Register의 에로는 프로그램 카운터(PC), 명령어 레지스터, 누산기, 일반 레지스터 등이 있습니다!
Program Counter(PC)는 현재 CPU가 실행중인 명령어의 주소를 가리키는 레지스터입니다. CPU는 PC가 가리키는 주소에 있는 명령어를 읽어와 실행하고, PC 값을 증가시켜 다음 명령을 가리키게 되며, 이러한 과정은 프로그램이 종료될 떄 까지 반복됩니다.
Process Control Block(PCB)는 운영체제가 각 프로세스를 관리하기 위해 사용하는 자료구조로 각각의 프로그램마다 하나씩 존재합니다. 이러한 PCB에는 코드의 메모리 주소와 레지스터 값, 하드웨어 상태 등이 저장됩니다. 프로세스가 인터럽트를 받아 일시 중지되었다가 나중에 다시 작업을 개시할때, PCB는 프로세스의 실행 상태를 재현하는데 사용됩니다.
CPU에서 명령어 실행될 때는 CPU 내부에 있는 임시 기억장치인 레지스터에 데이터를 읽거나 쓰면서 작업을 하는데, 이때 인터럽트가 발생해 새로운 명령을 실행하면 기존의 레지스터 값들이 지워지게 되므로 CPU내 이러한 상태를 저장해두어야 합니다.
따라서 프로그램 실행 상태(ex:실행중이던 코드의 메모리 주소, 레지스터 값 등)를 PCB에 저장한 후 CPU의 제어권이 인터럽트 처리루틴으로 넘어가게 되며, 인터럽트 처리가 끝나면 저장된 상태를 PCB로 부터 CPU상에 복원해 인터럽트 당하기 직전의 위치부터 실행이 이어 지게 되는 것입니다.
위 설명을 다른 관점에서 보자면, 인터럽트가 발생할 때에만 운영체제 코드 부분으로 CPU가 이양되어 인터럽트 처리를 수행하게 됩니다. 즉 운영체제가 CPU를 점유하는 경우는 인터럽트에 의하지 않고는 발생하지 않습니다!!
5. 입출력 구조
입출력(I/O)이란 컴퓨터 시스템이 컴퓨터 외부의 입출력 장치들과 데이터를 주고받는 것을 의미합니다.
이러한 입출력 방식에는 동기식 입출력과 비동기식 입출력이 있으며 이에 대해 상세히 알아보겠습니다 :)
동기식 입출력은 프로그램이 입출력 요청을 하면 해당 요청이 완료될 때까지 후속 작업을 수행하지 않는 방식을 말합니다.
이러한 방식은 CPU의 리소스를 비효율적으로 사용하게 만들 수 있습니다. 왜 그럴까요???
입출력 장치의 처리 속도는 CPU의 처리 속도에 비해 상당히 느리기 때문에 CPU는 입출력 작업이 완료될 떄까지 대기 상태가 됩니다. 이로 인해 CPU의 자원이 낭비되는 상황이 발생합니다.
이러한 문제를 해결하기 위해, 운영체제는 입출력 작업이 수행중인 프로세스의 상태를 봉쇄 상태(Blocked State)로 바꾸고, CPU를 다른 프로세스에게 할당합니다.
입출력 작업이 완료되면, IO 컨트롤러는 인터럽트를 발생시켜 CPU에게 입출력 작업 완료를 알립니다. 이후 운영체제는 봉쇄 상태에 있던 프로세스의 상태를 해제하여 CPU를 다시 할당 받을 수 있게 합니다.
어떤 프로그램이 동기식 입출력을 수행중일때 CPU를 다른 프로그램에게 할당하지 않는다면, 동기화가 자동으로 이루어질 것입니다.
혹시 동기화(synchronization)라는 말이 익숙하지 않으신 분들을 위해 간단히 설명드리자면, 여러 프로세스나 스레드가 특정 자원에 접근하거나 특정 작업을 수행할때 순서나 시간을 조절하여 동시에 접근하거나 작업을 수행하는 것을 막는 것을 말합니다.
근데 만약 동기화 입출력에서 CPU를 다른 프로세스에게 할당하게 되면, 해당 프로세스의 입출력 요청이 완료되지 않은 상태에서 다른 프로세스가 동일한 자원에 접근하려는 현상이 발생할 수 있습니다.
이 말이 좀 와닿지 않으실수 있어 간단히 예시를 들어보겠습니다.
예를들어 프로그램 A가 1이라는 파일에 대한 입출력 작업을 요청하고 블록 상태가 되었습니다. 디스크 컨트롤러는 이 작업을 하고 있는데, 그 도중에 CPU의 제어권이 프로그램 B로 이동했습니다.만약 프로그램 B도 1이라는 파일에 대한 입출력을 요청한다면, 동일한 자원에 대한 동시 접근 문제가 발생할 수 있는 것입니다.
만약 A가 1이라는 파일에 대해 하는 작업이 1을 더하는 것이고, B가 하려는 작업은 값을 3으로 바꾸는 것이라고 해봅시다.
개발자는 당연히 1->2->3 이런식으로 값이 변경되기를 원하는 것이였지만, B작업이 먼저 끝난다면 1 -> 3 -> 4(3+1) 식으로 작업이 진행될 수 있다는 것입니다.
이를 관리하기 위한 방법은 여러가지이지만 3가지에 대해 간단히 알아보고 가겠습니다.
- Lock을 사용하는 방법 : 자원에 접근하려는 프로세스는 먼저 락을 획득해야 하며, 락을 소유한 프로세스만 해당 자원에 접근 가능. 이 방법을 통해 동시에 여러 프로세스가 동일한 자원에 접근하는것 방지
- Semaphore를 사용하는 방법 : Lock과 비슷하지만, 여러 프로세스가 동시에 자원에 접근할 수 있는 기능이 추가되어있음. 세마포어는 허용된 동시 접근 수를 가지고 있으며, 이 숫자가 0이 될떄까지 다른 프로세스들이 자원에 접근 가능
- Queue를 사용하는 방법 : 동시에 여러 프로세스 또는 스레드에서 특정 자원을 요청할때, 요청 순서에 따라 순차적으로 처리(FIFO : First In First Out)
비동기식 입출력은 프로그램이 입출력 요청을 한 후에도 입출력 작업 완료를 기다리지 않고 후속 작업 완료를 기다리지 않고 후속 작업을 진행하는 방식을 의미합니다. 즉 CPU의 제어권을 입출력 연산을 호출한 그 프로그램에게 곧바로 다시 부여하는 방식인 것입니다!
이는 프로그램이 입출력 작업과 무관한 다른 작업을 입출력 작업이 완료되기를 기다리지 않고 바로 수행할 수 있게 해줍니다.
예를들어 프로그램이 디스크로부터 데이터를 읽는 작업을 요청하였지만, 이 데이터를 이용한 작업이 아니라 다른 작업을 먼저 수행할 수 있는 경우, 비동기식 입출력이 유용하게 사용될 수 있습니다
마지막으로 예시와 함께 정리를 해보겠습니다! 차근차근이 읽어가면서 이해를 해보시고, 이해가 안된다면 위의 글들을 한번더 읽어 보시는 것을 추천드립니다 :)
프로그램 A가 실행 중에 디스크에서 어떤 데이터를 읽어오는 명령을 만나면 프로그램 A는 시스템 콜을 통해 CPU에게 일종의 소프트웨어 인터럽트를 발생시킵니다. 이러한 시스템 콜은 프로그램 A가 직접 실행할 수 없는 작업을 운영체제에게 요청하는 방법으로, 이 요청은 CPU의 제어권을 운영체제로 넘김과 동시에 커널모드로 전환합니다.
그러면 CPU는 지금까지 프로그램 A의 코드를 실행하던 일을 멈추고 현재 상태를 PCB에 저장합니다. 그 후, 인터럽트를 처리하기 위해 운영체제의 인터럽트 처리 루틴으로 제어권을 넘깁니다. 이때 운영체제는 커널 모드에서 동작하며, 시스템 리소스에 대한 전체적인 접근 권한을 가집니다.
처리 루틴으로 이동하면 CPU는 컨트롤러에게 입출력 연산을 요청합니다.
그러면 컨트롤러는 A가 요청한 데이터를 디스크로 부터 자신의 로컬 버퍼로 읽어옵니다.
운영체제는 프로그램 A가 입출력 연산을 요청했으므로, CPU를 할당해도 명령을 수행하지 못한다는 사실을 봉쇄 상태로 표시합니다.그리고 CPU를 다른 프로그램 B에 할당해 계속 CPU가 일을 할 수 있도록 합니다.
원하는 정보가 로컬 버퍼로 다 들어오면 컨트롤러는 CPU에게 입출력이 완료되었다고 인터럽트(하드웨어 인터럽트)를 발생시켜 알립니다.
프로그램 B를 수행중이던 CPU는 수행하던 지점 및 상태 저장 후, 인터럽트를 처리하게 됩니다.
인터럽트 처리루틴은 로컬 버퍼에 있는 A가 요청한 데이터를 A의 메모리 영역으로 읽어오고, A의 봉쇄 상태를 해제시켜 이제 A에게 CPU를 할당해도 된다고 표시를 합니다.
이후 A는 CPU를 기다리는 줄에 다시 서게되고, CPU는 수행중이던 프로그램인 B로 돌아가 하던 업무를 계속 수행하게됩니다.
이후 프로그램 A는 CPU를 기다리는 줄에서 기다리다가 자신의 차례가 되면 CPU를 할당받고 입출력 연산 이후의 작업을 수행하게 됩니다.
6. DMA(Direct Memory Access)
원칙적으로 메모리는 CPU에서만 접근할 수 있는 장치입니다.
즉 CPU 외의 장치가 메모리의 데이터에 접근하기 위해서는 CPU에게 인터럽트를 발생시켜 CPU가 이를 대행하는 식으로만 가능합니다.
앞서 살펴본것 처럼 컨트롤러가 CPU에게 인터럽트를 발생시키면 CPU는 컨트롤러의 로컬버퍼와 메모리 사이에서 데이터를 옮기는 일을 합니다.
하지만 이 경우 어떠한 문제가 생길수 있을까요????
모든 메모리 연산이 CPU에 의해서만 이루어질 경우 입출력 장치가 메모리 접근을 원할때 마다 인터럽트에 의해 CPU의 업무가 방해를 받아 CPU 효율성이 떨어지는 문제점이 발생합니다.
이러한 비효율성을 극복하기 위해 CPU 이외에 메모리 접근이 가능한 장치를 하나 더 두는데, 이와 같은 장치를 DMA(Direct Memory Access)라고 부릅니다.
이러한 DMA를 사용하면 로컬 버퍼에서 메모리로 읽어오는 작업을 CPU가 담당하는 것이 아니라, DMA가 대행함으로써 CPU는 원래 하던 작업을 멈추고 인터럽트를 처리할 필요가 없어집니다.
DMA는 바이트 단위가 아니라, 블록이라는 큰 단위로 정보를 메모리로 읽어온 후에 CPU에게 인터럽트를 발생시켜 해당 작업의 완료를 알려줍니다. 이러한 방식으로 CPU에 발생하는 인터럽트 빈도를 줄여 CPU를 좀더 효율적으로 관리하고 입출력 연산을 빠르게 수행할 수 있습니다.
7. 저장장치의 구조
컴퓨터 시스템을 구성하는 저장장치에는 크게 주기억장치와 보조기억장치가 있습니다.
주기억 장치는 보통 메모리라고 부르며, 전원이 나가면 내용이 모두 사라져버리는 휘발성인 RAM을 주로 사용합니다.
이에 반해 보조기억 장치는 전원이 나가도 저장된 내용을 기억할 수 잇는 비휘발성의 마그네틱 디스크를 주로 사용하며, 이 외에도 플래시 메모리, CD, 마그네틱 테이프 등이 있습니다.
보조 기억 장치의 용도는 크게 두가지로 구분됩니다.
첫번째는 파일시스템 용이며, 두번째로는 스왑 영역 용입니다.
파일시스템 용이란, 전원이 나가도 유지해야 할 정보가 있으면 그것을 파일 형태로 보조기억 장치에 저장하는 것을 뜻합니다.
왜냐하면 메모리는 휘발성 매체이기에 비휘발성 매체인 디스크를 파일 시스템 용으로 사용하는 것이죠!
스왑 영역 용이란, 프로그램 실행에 당장 필요한 부분만 메모리에 올려놓고, 그렇지 않은 영역은 디스크의 스왑 영역에 둔다는 뜻입니다.
메모리는 크기가 한정되고, 가격이 상대적으로 비싸고, 용량이 적은 경우가 많습니다. 그러므로 다수의 프로그램이 메모리에 올라가면 수행될 시, 메모리 부족 현상이 발생할 수 있습니다.
따라서 운영체제는 프로그램 실행에 당장 필요한 부분만 메모리에 올려놓고, 그렇지 않은 부분을 디스크의 스왑 영역에 내려 놓는 것(스왑 아웃이라고 부름)이죠.
메모리의 연장 공간으로서의 역할을 담당하므로, 파일 시스템처럼 비휘발성 용도로 사용되는 것과는 역할이 구분됩니다.
8. 저장장치의 계층 구조
컴퓨터 시스템을 구성하는 저장장치의 게층구조는 다음과 같습니다.
빠른 저장장치는 보통 단위 공간당 가격이 높기에 적은 용량을 사용하고, 느린 저장장치는 가격이 저렴해 대용량을 사용하는 반면 접근 속도가 느리다는 약점이 있습니다.
따라서 가장 필요한 정보는 빠른 저장장치에 넣어두어 수행 속도를 높이고, 그렇지 않은 정보는 상대적으로 느린 저장장치에 보관하게 됩니다.
상위 저장장치 계층으로 갈수록 용량은 적지만, 당장 필요한 정보만을 선별적으로 저장하면 하위에 있는 큰 용량의 저장장치를 가지고 있는 것과 비슷한 성능 효과를 낼 수 있습니다.
예를들어 캐시 메모리는 레지스터와 메인 메모리 사이에 존재하여, 가격적인 측면에서는 메인 메모리보다 비쌉니다!
그러나, 여러 가지 캐싱 기법을 이용해 적은 용량의 캐시 메모리를 사용해서도 메인 메모리와 같이 큰 용량을 가진 것 처럼 효율적으로 동작하도록 만들 수 있습니다.
캐싱기법에 대해 모르시는 분들을 위해 간단히 설명드리겠습니다 :)
캐싱 기법이란 상대적으로 용량이 적은 빠른 저장장치를 이용해 느린 저장장치의 성능을 향상시키는 총체적 기법을 일컫습니다.
즉 상대적으로 느린 저장장치에 있는 내용 중 당장 사용되거나 빈번히 사용될 정보를 빠른 저장장치에 선별적으로 저장함으로써 두 저장장치의 속도를 완충시키는 것입니다.
9. 하드웨어의 보안
우리가 흔히 사용하는 운영체제는 여러 프로그램이 동시에 실행될 수 있는 다중 프로그래밍 환경에서 동작합니다.
이러한 환경에서는 프로그램 간의 상호 간섭이나 충돌을 방지하기 위해 하드웨어에 대한 강력한 보안이 필요합니다.
운영체제는 하드웨어의 보안성을 위해 기본적으로 두가지 모드를 지원하는데, 바로 커널모드와 사용자 모드입니다.
커널모드란 운영체제가 CPU의 제어권을 가지고 운영체제 코드를 실행하도록 하는 모드로, 이 모드에서는 모든 종류의 명령을 다 실행할 수 있습니다.
반면 사용자모드란 일반 사용자 프로그램이 실행되는 모드로, 이 모드에서는 제한적인 명령만 실행 가능하며, 시스템에 중요한 영향을 미치는 연산은 커널모드에서만 실행될 수 있도록 설정되어있습니다.
그런데 문제는 사용자 프로그램이 CPU를 사용하고 있는 동안, 운영체제는 CPU를 제어하지 못하므로, 사용자 프로그램이 운영체제만 수행 가능한 중요한 연산을 수행할 가능성이 있다는 것입니다!!
이러한 문제를 해결하기 위해 컴퓨터 시스템은 CPU 내부에 모드 비트(mode bit)라는 것을 두어 사용자 프로그램을 감시합니다.
모드 비트가 0으로 세팅되어 있으면 커널모드로서 모든 명령을 수행할 수 있고, 모드 비트가 1로 세팅되어있으면 사용자모드로서 제한된 명령만을 수행할 수 있습니다.
만약 인터럽트가 발생하여 운영체제로 CPU 제어권이 넘어간다면 모드 비트는 자동으로 0으로 세팅되므로, 운영체제는 서비스에 필요한 모든 명령을 다 수행할 수 있게 됩니다. 이후 요청된 작업이 모두 끝난 후에는 모드 비트를 다시 1로 만들어 사용자 프로그램에게 CPU를 넘겨줍니다.
시스템 보안과 관련된 명령들을 보통 특권 명령이라고 부르는데, 이는 모드 비트가 0인 상태, 즉 커널 모드에서만 실행할 수 있습니다. 모든 입출력 명령도 특권 명령으로 규정되어 사용자 프로그램이 직접 입출력하는 것을 차단합니다.
만약 사용자 프로그램이 디스크에 저장된 파일에 자유롭게 접근한다면 어떨까요???
이 경우엔 자신의 소유가 아닌 다른 사람의 파일까지 접근할 수 있을 것입니다. 따라서 입출력 명령은 특권 명령으로 규정되어 있습니다.
따라서 사용자 프로그램이 입출력을 하고 싶으면 시스템 콜로 운영체제에게 요청을 해야하는 것이죠
10. 메모리 보안
여러 프로그램이 메모리에 동시에 올라가게 되는 경우, 한 프로그램이 다른 프로그램이나 운영체제가 위치하는 메모리 영역에 침범하지 않도록, 메모리에 대해서도 보안을 유지할 필요가 있습니다.
이러한 메모리 보호는 기준 레지스터(base register)와 한계 레지스터(limit register)를 이용하며, 이 두 레지스터를 사용해서 프로그램이 접근하려는 메모리 부분에 접근해도 문제가 없는지 체크함으로서 이루어집니다.
기준 레지스터는 프로그램이 접근 할 수 있는 메모리 상의 가장 작은 주소를 보관하고 있고, 한계 레지스터는 그 프로그램이 기준 레지스터 값부터 접근할 수 있는 메모리의 범위를 보관하고 있습니다.
즉 사용자 프로그램은 기준 레지스터에 있는 주소부터 기준 레지스터의 값과 한계 레지스터 값을 더한 값 사이의 영역에만 접근할 수 있으며, 접근하려는 주소가 이 범위에 벗어나면, 예외상황이라는 일종의 소프트웨어 인터럽트가 발생하게 됩니다. 이렇게 예외 상황이 발생하면, CPU의 제어권이 운영체제로 넘어가게 되며, 운영체제는 프로그램을 강제 종료 시킵니다.
다만 위에서 살펴본 것처럼 기준 레지스터와 한계 레지스터를 통한 메모리 보호 기법은 하나의 프로그램이 메모리의 한 영역에 연속적으로 위치하는 경우에만 적용이 가능하겠죠????
이후 포스팅에서 하나의 프로그램이 메모리의 여러 영역에 나누어 위치하는 페이징 기법 등에서 좀더 살펴보게 될것인데, 이 경우 2개의 레지스터 뿐 아니라 다른 하드웨어의 지원을 통해서 연속적이지 않은 공간에 프로그램이 위치하는 좀더 현실적인 메모리 관리 및 보호 기법에 대해 알아보겠습니다!
메모리 접근 명령 자체는 특권 명령이 아니지만, 기준 레지스터와 한계 레지스터 값을 세팅하는 연산은 특권 명령으로 규정해야 합니다. 만약 프로그램이 자신의 기준 레지스터와 한계 레지스터 값을 직접 변경할 수 있다면 의미가 없겠죠?? 따라서 운영체제가 값을 직접 세팅해주고 사용자 프로그램은 값을 변경할 수 없게 합니다
11. CPU 보호
일반적으로 CPU는 컴퓨터 시스템 내에 하나밖에 존재하지 않으며, CPU가 하나의 프로그램에 의해 독점되는 것을 막기 위해 운영체제는 타이머(timer)라는 하드웨어를 사용합니다.
타이머는 정해진 시간이 지나면, 인터럽트를 발생시켜 운영체제가 CPU의 제어권을 획득 할 수 있도록 하는 역할을 수행합니다. 즉 인터럽트를 발생시켜, 운영체제가 CPU 제어권을 얻게 해준다음 현재 실행 중인 프로세스를 중지하고 다음 프로세스를 스케쥴링하게 됩니다.
따라서, 만약 하나의 프로그램이 무한 루프에 빠지거나 CPU 자원을 과도하게 사용하려고 하면, 타이머 인터럽트가 이를 방지하고, CPU가 다른 프로세스로 전환되도록 합니다.
따라서 타이머는 CPU의 효율적 사용과 프로세스 간의 공정한 스케쥴링을 지원하는 중요한 하드웨어 장치인 것입니다!
12. 시스템 콜을 이용한 입출력 수행
사용자 프로그램이 디스크의 파일에 데이터를 읽고 쓰거나, 키보드나 마우스로 입력받고, 모니터에 출력하는 행위는 모두 입출력 명령이며, 이는 특권 명령이므로 사용자 프로그램이 직접 수행할 수 없습니다.
입출력 명령은 운영체제 코드에 구현되어 있으며, 사용자 프로그램은 운영체제에게 시스템 콜이라는 서비스 대행 요청을 통해 입출력을 수행하게 됩니다. 이러한 시스템콜은 일종의 소프트웨어 인터럽트로서, 사용자 프로그램이 시스템콜을 할경우, 트랩이 발생해 CPU에 대한 제어권이 운영체제로 넘어가게 됩니다.
이렇게 운영체제가 해당 시스템 콜을 처리하기 위한 루틴으로 가서 정의된 명령을 수행하고 나면, CPU에게 인터럽트를 발생시켜 입출력이 완료되었음을 알려주어 해당 프로그램이 다시 CPU를 할당받을 수 있도록 한다.
개발자 준비생이 대학 강의를 듣고 정리한 내용입니다
혹시 틀린 내용이 있다면 댓글 부탁드리겠습니다!! 곧바로 수정하겠습니다
'운영체제 > 반효경 교수님 - 운영체제 강의' 카테고리의 다른 글
운영체제 6강 - 메모리 관리 (1) | 2023.06.09 |
---|---|
운영체제 5강 - CPU 스케쥴링 (0) | 2023.06.08 |
운영체제 4강 - 프로세스 관리 (2) | 2023.06.07 |
운영체제 3강 - 프로그램 구조와 실행 (2) | 2023.06.07 |
운영체제 1강 - 운영체제 개요 (2) | 2023.05.30 |