'운영체제와 정보기술의 원리' 스터디를 진행하며 정리한 내용이다.
1. 컴퓨터 시스템의 구조
- 컴퓨터 내부장치인 CPU, 메모리와 컴퓨터 외부장치인 디스크, 키보드, 마우스, 모니터, 네트워크 장치 등으로 구성된다.
-
컴퓨터는 외부장치에서 내부장치로 데이터를 읽어와 각종 연산을 수행한 후, 그 결과를 외부장치로 다시 내보내는 방식으로 업무를 처리함.
- 컴퓨터 내부로 데이터가 들어오는 것을 입력(input), 컴퓨터 외부장치로 데이터가 나가는 것을 출력(output)이라고 함.
- 컴퓨터 외부장치를 입출력 장치라고 부른다.
- 메모리 및 입출력장치 등의 각 하드웨어 장치에는 컨트롤러라는 것이 붙어 있음.
- 운영체제 중 항상 메모리에 올라가 있는 부분은 전체 운영체제 중 핵심적인 부분에 한정되며, 이 부분을 커널(kernel)이라고 부른다.
2. CPU 연산과 I/O 연산
- 입출력 장치들의 I/O 연산은 입출력 컨트롤러가 담당하고, 컴퓨터 내에서 수행되는 연산은 메인 CPU가 담당한다. 이때 입출력 장치와 메인 CPU는 동시 수행이 가능하다.
-
각 장치마다 장치 컨트롤러는 장치로부터 들어오고 나가는 데이터를 임시로 저장하기 위한 작은 메모리를 가지고 있다. 이를 로컬버퍼(local buffer)라고 부른다.
- 디스크나 키보드 등에서 데이터를 읽어오는 경우, 우선 로컬버퍼에 데이터가 임시로 저장된 후 메모리에 전달됨.
- 로컬버퍼로 읽어오는 일은 컨트롤러가 담당한다.
-
로컬버퍼로 읽어오는 작업이 끝났는지를 메인 CPU가 지속적으로 체크하는 것이 아니라 장치에 있는 컨트롤러가 인터럽트를 발생시켜 CPU에 보고하게 된다.
- CPU 옆에는 인터럽트 라인(interrupt line)이 있어서, 자신의 작업을 하던 중간에 인터럽트 라인에 신호가 들어오면 하던 일을 멈추고 인터럽트와 관련된 일을 먼저 처리함.
- CPU는 명령 하나를 수행할 때마다 인터럽트가 발생했는지 확인한다. 발생했으면 다음 명령을 수행하기 전에 인터럽트를 처리하게 되고, 그렇지 않으면 다음 명령을 계속 수행하게 되는 것.
- 인터럽트는 키보드 입력 혹은 요청된 디스크 입출력 작업의 완료 등 CPU에 알려줄 필요가 있는 이벤트가 일어난 경우 컨트롤러가 발생시키는 것.
3. 인터럽트의 일반적 기능
- 운영체제 커널에는 인터럽트가 들어왔을 때 해야 할 일이 미리 다 프로그래밍되어 그 코드가 보관돼 있다.
- 운영체제 커널 내에 있는 인터럽트 처리루틴은 다양한 인터럽트에 대해 각각 처리해야 할 업무들을 정의하고 있음.
-
하드웨어 인터럽트와 소프트웨어 인터럽트가 있음. 인터럽트 라인에 신호를 보내서 인터럽트가 발생했음을 알려주는 방식은 둘 다 동일.
- 하드웨어 인터럽트는 컨트롤러 등 하드웨어 장치가 CPU의 인터럽트 라인을 세팅함.
- 소프트웨어 인터럽트는 소프트웨어가 그 일을 수행함.
-
운영체제는 인터럽트 벡터(interrupt vector)를 가지고 있음.
- 인터럽트 벡터란 인터럽트 종류마다 번호를 정해서, 번호에 따라 처리해야 할 코드가 위치한 부분을 가리키고 있는 자료구조를 말함.
- 실제 처리해야 할 코드는 인터럽트 처리루틴(interrupt service routine) 또는 인터럽트 핸들러(interrupt handler)라고 불리는 다른 곳에 정의 됨.
- 인터럽트 처리를 완료하고 나면 원래 수행하던 작업으로 돌아가 일을 계속해서 수행하게 되는데 운영체제는 이 돌아갈 위치에 대한 정보를 저장하기 위한 장소를 별도로 가지고 있다.
- 통상적으로 인터럽트라고 하면 하드웨어 인터럽트를 의미하고, 소프트웨어 인터럽트는 트랩(trap)이라는 용어로 주로 불림.
-
소프트웨어 인터럽트의 예로는 예외상황(exception)과 시스템 콜(system call)이 있음.
- 예외상황은 사용자 프로그램이 0으로 나누는 연산 등 비정상적인 작업을 시도하거나, 자신의 메모리 영역 바깥에 접근하려는 시도 등 권한이 없는 작업을 시도할 때 이에 대한 처리를 위해 발생시키는 인터럽트를 말함.
- 시스템 콜은 사용자 프로그램이 운영체제 내부에 정의된 코드를 실행하고 싶을 때 운영체제에 서비스를 요청하는 방법이라고 볼 수 있음.
- 둘 다 사용자 프로세스로부터 CPU의 제어권이 운영체제에 이양되어 처리된다.
4. 인터럽트 핸들링
- 인터럽트 핸들링이란 인터럽트가 발생한 경우에 처리해야 할 일의 절차를 의미한다.
-
프로그램이 실행되고 있을 때 인터럽트가 발생하면 프로그램의 현재 상태를 먼저 저장한다.
- 현재 상태란 현재 CPU에서 실행 중인 명령의 메모리 주소를 포함해 몇 가지 부가적인 정보들.
- 원래 CPU에서 명령이 실행될 때 임시기억장치인 레지스터(register)에 데이터를 읽거나 쓰면서 작업하는데, 인터럽트가 발생하면 기존 레지스터값들이 지워지게 되므로 이러한 상태를 저장해두어야 하는 것.
-
운영체제는 현재 시스템 내에서 실행되는 프로그램들을 관리하기 위해 프로세스 제어블록(Process Control Block: PCB)이라는 자료구조를 둔다.
- PCB는 각각의 프로그램마다 하나씩 존재하며 해당 프로그램의 어느 부분이 실행 중이었는지를 저장하고 있음. 구체적으로는 실행 중이던 코드의 메모리 주소, 레지스터값, 하드웨어 상태 등이 저장.
- 인터럽트가 발생하면 프로그램의 실행 상태를 PCB에 저장한 후 CPU의 제어권이 인터럽트 처리루틴으로 넘어가게 되며, 인터럽트 처리가 끝나면 저장된 상태를 PCB로부터 CPU 상에 복원해 인터럽트 당하기 직전의 위치부터 실행이 이어지게 되는 것
-
오늘날의 컴퓨터에서 운영체제는 인터럽트가 발생할 때에만 실행된다.
- 운영체제가 직접 CPU를 점유하는 경우는 인터럽트에 의하지 않고는 발생하지 않는다.
- 그럼에도 불구하고 운영체제는 자원을 체계적이고 효율적으로 관리할 수 있다.
5. 입출력 구조
- 입출력(I/O)이란 컴퓨터 시스템이 컴퓨터 외부의 입출력 장치들과 데이터를 주고받는 것을 말한다.
-
동기식 입출력(synchronous I/O)
- 어떤 프로그램이 입출력 요청을 했을 때 입출력 작업이 완료된 후에야 그 프로그램이 후속 작업을 수행할 수 있는 방식을 말한다.
- 동기식 입출력에서 CPU는 입출력 연산이 끝날 때까지 인터럽트를 기다리며 자원을 낭비하게 된다.
- 따라서 일반적으로 프로그램이 입출력을 수행 중인 경우 CPU를 다른 프로그램에게 이양해 CPU가 계속 쉬지 않고 일할 수 있도록 관리한다.
- 운영체제는 프로그램을 몇 가지 상태로 나누고 입출력 중인 프로그램의 경우 봉쇄 상태(blocked state)로 전환시킨다. 봉쇄 상태의 프로그램에게는 CPU를 할당하지 않고, CPU 할당 시 곧바로 명령을 수행할 수 있는 프로그램에만 CPU를 할당한다.
-
다수의 입출력 연산이 동시에 요청되거나 처리될 수 있다.
- 입출력 요청의 동기화를 위해 장치별로 큐(queue)를 두어 요청한 순서대로 처리할 수 있도록 한다.
- 요청들을 모으고 처리 순서를 바꾸어 입출력의 효율성을 높일 수도 있는데, 이러한 경우 동기화를 보장하기 위한 별도의 방안이 마련되어야 한다.
-
장치마다 큐헤더가 존재하고 컨트롤러는 이 큐 순서에 따라 매 시점 하나씩 자신에게 주어진 입출력 작업을 처리하게 된다.
- CPU의 수행 속도에 비해 컨트롤러의 수행 속도나 장치 자체의 작업 수행 능력은 매우 떨어진다.
- 입출력이 완료될 때까지 입출력과 관련 없는 프로그램을 수행하도록 하고, 요청된 입출력 연산이 완료되면 CPU에게 입출력이 완료되었음을 알려주는 방식으로 진행.
-
비동기식 입출력
- 입출력 연산을 요청한 후에 연산이 끝나기를 기다리는 것이 아니라 CPU의 제어권을 입출력 연산을 호출한 그 프로그램에게 곧바로 다시 부여하는 방식.
- 비동기식 입출력은 데이터와 관련 없이 수행할 수 있는 작업을 먼저 수행하고, 읽어오는 데이터가 반드시 있어야 수행할 수 있는 일들은 입출력이 완료된 후에 수행함.
- 쓰기 작업이 완료되기 전에도 다음 명령을 수행할 수 있으므로 비동기식 입출력이 사용될 수 있음
- 사용자가 I/O 요청을 하면 동기식 입출력에서는 먼저 운영체제의 커널로 CPU의 제어권이 넘어와서 입출력 처리와 관련된 커널의 코드가 수행됨. 이때 입출력을 호출한 프로세스의 상태를 봉쇄 상태로 바꾸어 입출력이 완료될 때까지 CPU를 할당받지 못하도록 함. 입출력이 완료되면 I/O 컨트롤러가 CPU에게 인터럽트를 발생시켜 입출력이 완료되었음을 알려줌. 프로세스의 봉쇄 상태를 해제시켜 CPU를 할당받을 수 있는 권한이 다시 생기게 됨
- 비동기식 입출력에서는 CPU의 제어권이 입출력을 요청한 프로세스에게 곧바로 다시 주어지며, 입출력 연산이 완료되는 것과 무관하게 처리 가능한 작업부터 처리함. 비동기식 입출력에서도 입출력 연산이 완료되면 동기식과 마찬가지로 인터럽트를 통해 CPU에게 알려줌. 그 시점부터 읽어온 데이터를 필요로 하는 명령을 수행할 수 있게 됨.
6. DMA
- 원칙적으로 메모리는 CPU에 의해서만 접근할 수 있는 장치.
- 따라서 CPU 외의 장치가 메모리의 데이터에 접근하기 위해서는 CPU에게 인터럽트를 발생시켜 CPU가 이를 대행하는 식으로만 가능함.
- 이 비효율성을 극복하기 위해 CPU 이외에 메모리 접근이 가능한 장치를 하나 더 두는 경우가 많은데, 이와 같은 장치를 DMA(Direct Memory Access)라고 부른다.
- DMA를 사용하면 로컬버퍼에서 메모리로 읽어오는 작업을 CPU가 담당하는 것이 아니라 DMA가 대행함으로써 CPU는 원래 하던 작업을 멈추고 인터럽트를 처리할 필요가 없어지는 것.
- DMA는 바이트(byte) 단위가 아니라 블록(block)이라는 큰 단위로 정보를 메모리로 읽어온 후에 CPU에게 인터럽트를 발생시켜서 해당 작업의 완료를 알려준다.
7. 저장장치의 구조
-
주기억장치
- 보통 메모리라고 부르며 전원이 나가면 저장되었던 내용이 모두 사라져버리는 휘발성(volatile)의 RAM을 매체로 사용하는 경우가 대부분
-
보조기억장치
- 전원이 나가도 저장된 내용을 기억할 수 있는 비휘발성(nonvolatile)의 마그네틱 디스크를 주로 사용함.
- 플래시 메모리, CD, 마그네틱 테이프 등이 사용됨.
-
용도는 크게 두 가지로 구분됨.
-
파일 시스템용
- 전원이 나가도 유지해야 할 정보가 있으면 그것을 파일 형태로 보조기억장치에 저장함.
-
메모리의 연장 공간인 스왑 영역(swap area)용
- 운영체제는 프로그램 수행에 당장 필요한 부분만 메모리에 올려놓고 그렇지 않은 부분은 스왑 영역에 내려놓게됨.
- 디스크에 내려놓는 일을 스왑 아웃(swap out)시킨다고 말하며, 스왑 아웃된 부분이 필요할 때에는 다시 메모리 영역에 올리게 된다.
- 스왑 영역으로는 하드디스크가 가장 널리 사용
-
8. 저장장치의 계층 구조
- 컴퓨터 시스템을 구성하는 저장장치는 빠른 저장장치부터 느린 저장장치까지 단계적인 계층 구조로 이루어진다.
- 빠른 저장장치는 단위 공간당 가격이 높기 때문에 적은 용량을 사용한다. 따라서 당장 필요한 정보는 빠른 저장장치에 넣어두어 수행 속도를 높인다.
- 느린 저장장치는 가격이 저렴해 대용량을 사용하는 반면 접근 속도가 느리다. 당장 필요하지 않은 정보는 상대적으로 느린 저장장치에 보관하게 된다.
- 레지스터, 캐시 메모리, 메인 메모리 등은 휘발성 저장장치로 이 부분에 저장되는 정보는 전원이 나가면 그 내용이 사라짐. 그 밑에 저장장치 계층은 전원이 나가도 지워지지 않는 비휘발성 저장장치.
-
상위 저장장치 계층으로 갈수록 접근 속도가 월등히 빠르지만 용량은 상대적으로 적다.
- 당장 필요한 정보만을 선별적으로 저장하면 하위에 있는 큰 용량의 저장장치를 가지고 있는 것과 비슷한 성능 효과를 낼 수 있다.
- 예로 캐시 메모리가 있다. 상대적으로 용량이 적은 빠른 저장장치를 이용해 느린 저장장치의 성능을 향상시키는 총체적 기법인 캐싱 기법을 사용한다. 반복되는 코드를 빠른 저장장치에 올려놓으면 적은 저장공간만으로도 전체 시스템의 평균적인 성능을 향상시킬 수 있다.
9. 하드웨어의 보안
- 다중 프로그래밍 환경에서는 각 프로그램이 다른 프로그램의 실행을 방해하거나 프로그램 간의 충돌을 일으키는 문제를 막기 위해 하드웨어에 대한 각종 보안 기법이 필요하다.
-
커널모드
- 중요한 정보에 접근해 위험한 상황을 초래할 수 있는 연산은 커널모드에서 실행되도록 하여 일반 사용자 프로그램이 직접 위험한 명령을 수행할 수 없도록 함.
- 운영체제가 CPU의 제어권을 가지고 운영체제 코드를 실행하는 모드로서, 이 모드에서는 모든 종류의 명령을 다 실행할 수 있다.
-
사용자모드
- 일반적인 연산만 사용자모드에서 사용자 프로그램이 수행하도록 통제하여 보안성을 확보함.
- 일반 사용자 프로그램이 실행되며 제한적인 명령만 수행함.
-
사용자 프로그램이 프로그램 내 중요한 연산을 수행해버리면 제어가 아무런 소용이 없게 되므로 이를 막기 위해 하드웨어적 지원으로 CPU 내부에 모드비트(mode bit)를 두어 사용자 프로그램을 감시하게 됨.
- 모드비트가 0으로 세팅되어 있으면 커널모드로서 모든 명령을 수행할 수 있고, 모드비트가 1로 세팅되어 있으면 사용자모드로서 제한된 명령만을 수행할 수 있음.
- 시스템 보안과 관련된 명령들을 특권명령이라 지칭하는데, 특권명령은 모드비트가 0, 즉 커널모드에서 운영체제에 의해서만 수행할 수 있다.
-
모든 입출력 명령은 특권명령으로 규정해서 사용자 프로그램이 직접 입출력을 하는 것을 차단한다.
- 사용자 프로그램이 입출력을 하고 싶으면 시스템 콜로 운영체제에 요청해야 한다.
- 그러면 인터럽트 하드웨어에 의해 모드비트가 0으로 세팅되어 운영체제가 입출력을 수행할 수 있게 된다.
10. 메모리 보안
- 여러 프로그램이 메모리에 동시에 올라가서 실행되기 때문에 하나의 프로그램이 다른 프로그램이나 운영체제가 위치한 메모리 영역을 침범할 수 있기에 메모리 보안도 필요하다.
-
특히, 인터럽트 벡터와 인터럽트 처리루틴이 있는 곳은 각별한 보안이 필요하다.
- 운영체제만 수행할 수 있는 특권명령을 보안성이 침해되는 이상한 명령어로 변형할 수 있기 때문.
-
2개의 레지스터를 사용해서 프로그램이 접근하려는 메모리 부분이 합법적인지 체크함으로써 메모리를 보호할 수 있다.
-
기준 레지스터(base register)
- 어떤 프로그램이 수행되는 동안 그 프로그램이 합법적으로 접근할 수 있는 메모리상의 가장 작은 주소를 보관하고 있다.
-
한계 레지스터(limit register)
- 프로그램이 기준 레지스터값부터 접근할 수 있는 메모리의 범위를 보관하고 있음.
- 어떤 프로그램이 실제 메모리에 올라가 있는 부분의 시작 주소와 그 프로그램의 길이를 각각 기준 레지스터와 한계 레지스터에 보관해 메모리 접근 연산이 있을 때마다 하드웨어적으로 현재 접근하려는 위치가 합법적 범위에 있는지 체크하게 됨.
- 사용자 프로그램은 기준 레지스터에 있는 주소부터 기준 레지스터 + 한계 레지스터값 사이의 주소 영역에만 접근할 수 있음.
-
-
메모리 접근 연산은 사용자 프로그램이 CPU를 가지고 있는 동안 수행할 수 있는 연산이므로 특권명령은 아니지만, 사용자 프로그램이 메모리에 접근하기 전에 하드웨어적으로 그 접근이 합법적인지를 체크하여 메모리를 보호하게 됨.
- 운영체제만이 수행할 수 있는 입출력 연산과 메모리 접근 연산의 차이점.
- 사용자모드인 경우, 기준 레지스터와 한계 레지스터를 사용해서 메모리를 보호하게 되고, 커널모드에서는 메모리에 무제한으로 접근하는 것이 가능함.
11. CPU 보호
- 특정 프로그램이 CPU를 독점되는 것을 막기 위해 운영체제는 타이머(timer)라는 하드웨어를 사용함.
- 타이머는 정해진 시간이 지나면 인터럽트를 발생시켜 운영체제가 CPU의 제어권을 획득할 수 있도록 하는 역할을 수행함.
-
타이머는 일정한 시간 단위로 세팅될 수 있으며 매 클럭 틱(clock tick) 때마다 1씩 감소하여 0이 되는 순간 인터럽트를 발생하게 됨.
- 세팅하는 명령을 로드 타이머(load timer)라고 하며 특권명령에 속함.
- 타이머는 시분할 시스템에서 현재 시간을 계산하기 위해서도 사용됨.
12. 시스템 콜을 이용한 입출력 수행
- 사용자 프로그램이 디스크 파일에 데이터를 쓰거나 읽어오는 행위, 키보드 입력하고 결과 출력하는 행위 등은 모두 특권명령인 입출력 명령에 해당하므로 사용자 프로그램이 직접 수행할 수 없음.
-
사용자 프로그램은 시스템 콜이라는 서비스 대행 요청을 하여 입출력을 수행함.
- 시스템 콜은 일종의 소프트웨어적인 인터럽트
- 시스템 콜을 하면 트랩이 발생해 CPU 제어권이 운영체제로 넘어감.
- 운영체제는 해당 시스템 콜을 처리하기 위한 루틴으로 가서 정의된 명령을 수행함.
- 예를 들어 디스크 컨트롤러에게 입출력 요청을 수행하도록 명령하고, 추후에 디스트 컨트롤러가 입출력 수행을 마치면 CPU에게 인터럽트를 발생시켜 입출력이 완료되었음을 알려줌으로써 해당 프로그램이 다시 CPU를 할당받을 수 있도록 함.