위의 그림을 바탕으로 자세하게 설명할 수 있다.
-cpu의 작업 공간이 메모리이기 때문에 cpu는 매 클럭 사이클마다 메모리에서 기계어를 하나씩 읽어서 실행을 하게 된다.
-맨오른쪽에 4가지 세로로 그림이 나열되어 있다. 이들은 IO 디바이스이며 input을 담당하는 키보드, output을 담당하는 모니터, 프린터가 있다. 맨 위에 있는 것은 하드디스크인데 이는 보조 기억 장치로 볼 수 있고 IO device라고 볼 수도 있다. 왜냐하면 디스크에 있는 메모리를 읽어서 메모리로 읽어들이기도 하고(input device), 동시에 처리 결과를 디스크에 있는 파일 시스템에 저장을 하는 역할(output device)을 하기 때문이다.
-device controller는 각각의 IO디바이스들은 그 디바이스를 전담하는 작은 CPU 격인 device controller가 있다. (각 디바이스마다 컨트롤러가 있다.)
- ex) 디스크의 내부를 디스크 컨트롤러가 한다.
-로컬 버퍼 = main CPU에 작업 공간인 메모리가 존재하듯 device 컨트롤러도 그들의 작업 공간이 필요하다.
-CPU와 IO device는 처리하는 속도 차이가 많이 난다. (백만배정도 느리다)
- CPU안에는 메모리보다 더 빠르면서 정보를 저장할 수 있는 작은 공간들이 있는데 이를 “레지스터”라고 한다.
Mode bit
- mode bit = CPU에서 실행되는 것이 운영체제인지 아니면 사용자 프로그램인지를 구분하는 것이다.
- 사용자 프로그램의 잘못된 수행으로 다른 프로그램 및 운영체제에 피해가 가지 않도록 하 기 위한 보호 장치 필요(mode bit을 통해 한정된 instruction 사용 가능), 보안이 목적
- Mode bit을 통해 하드웨어적으로 두 가지 모드의 operation 지원
1 = 사용자 모드 : 사용자 프로그램 수행
-사용자 프로그램이 CPU를 가지고 있을 때는 제한된 instruction만 CPU에서 실행할 수 있게 되어 있다.
(보안상 문제로) → IO instruction을 하지 못하게 하기 위해서 mode bit으로 구분하는 것이라고 할 수 있다.
-만약에 프로그램이 실행하다가 IO 접근하거나 다른 프로그램의 메모리 공간, 혹은 운영체제의 메모리 공간에 접근하려는 시도가 있으면 mode bit을 보고 해당 instruction실행이 되지 않게 구현한 것이다.
0 = 모니터 모드 ( = 커널 모드, 시스템 모드): OS 코드 수행 (운영체제가 CPU에서 수행)
-CPU에서 실행중일 때는 무슨 일이든지 다 할 수 있게 정의되어 있다.
운영체제가 CPU를 가지고 있기 때문에 메모리 접근 뿐만 아니라 IO 디바이스에 접근하는 일도 가능하다. (모든 instruction 가능)
-IO 디바이스를 접근하는 instruction은 운영체제만 실행할 수 있다고 했는데 ⇒ 운영체제가 CPU를 사용자 프로그램에게 넘겨줄 때 mode bit을 바꾸어서 넘겨준다. 그리고 interrupt가 걸리면 mode bit을 1로 바꾸어서 운영체제에게 CPU가 넘어간다.
- 보안을 해칠 수 있는 중요한 기계어는 모니터 모드에서만 수행 가능한 "특권명령"으로 규정 ↔ 사용자 모드일 때 수행가능한 것은 “일반 명령”으로 규정한다.
- Interrupt나 Exception 발생시 하드웨어가 mode bit을 0으로 바꿈
- 사용자 프로그램에게 CPU를 넘기기 전에 mode bit을 1로 셋팅
interrupt line
= CPU는 메모리에서 instruction하나씩 읽어와서 수행하는 것이다.
만약 키보드에서 입력이 들어왔다든지 또는 디스크에서 어떤 것을 읽어와야 한다든지 또는 디스크에 어떠한 것을 읽어오라고 요청했는데 이것을 다 끝냈다든지에 대해서 CPU가 아는 방법이다.
즉, 명령 실행 완료 여부를 아는 방법이다.
디스크는 요청한 데이터를 읽어오면서 자신의 local buffer에다가 저장을 할 것이다. 하지만 CPU가 scanf를 통해 어떠한 값을 받아오라고 시킨 것은 그 값을 통해 어떠한 수행을 할 것이라는 말이다. 그래서 IO 처리가 다 되면 알려달라고 하고 그때까지 CPU는 이와 관련된 작업들이 아닌 상관없는 instruction들을 실행한다.
- CPU는 메모리에서 instruction하나씩 읽어와서 수행하는 것인데 경우에는 scanf 같은 것을 통해 입력을 받을 수도 있고 디스크에서 파일을 읽어와서 다음 instruction을 실행해야 하는 경우도 있다. (항상 메모리에 있는 변수로 작업을 하는 것이 아니라 키보드로 읽어오고 화면에 출력하고 디스크에서 어떤 것을 읽어오거나 처리한 결과를 디스크에 쓰거나하는 것으로 프로그램이 구조화된다 즉, IO device와 연관된 instruction들이 있는에 CPU는 IO device에 직접 접근하지 않고 메모리에 접근하는 instruction만 실행하게 되어 있다. 그러다가 디스크에서 어떤 것을 읽어오라는 요청은 device controller에게 디스크에서 어떤 것을 읽어오라고 일을 시킨다.
*사용자 프로그램은 본인이 직접 IO장치에 접근할 수 없다. IO 장치에 접근하는 모든 instruction들은 운영체제를 통해서만 할 수 있도록 막아놨다. (보안등의 이유로) 그래서 IO작업이 필요한 경우 프로그램이 운영체제에게 CPU를 넘겨주는 것이다. 그리고 운영체제가 해당하는 작업을 IO controller에게 시키고 나면 이러한 작업들이 오래 걸리므로 방금 IO작업 해달라고 요청한 프로그램에게 CPU를 넘기는 것이 아니라 다른 프로그램에게 CPU를 넘긴다.
더 구체적으로 과정을 살펴보자,
- → 그럼 IO요청한 프로그램은 언제까지 기다리느냐? - 입력 받은 내용이 버퍼에 들어가면 device controller가 CPU에게 interrupt를 건다. → 그럼 다른 프로그램을 실행하는 것을 멈추고 CPU는 운영체제에게 넘어간다. 그러면 운영체제가 왜 interrupt가 들어왔는지 살펴보고 이것이 아까 IO작업을 했던 프로그램에서 키보드 입력이 들어왔다는 것을 알 수 있다. 그러면 입력된 키보드 값을 아까 키보드 입력 요청한 프로그램에 메모리 공간에다가 카피를 해주고 해당 프로그램에게 CPU를 주는 것이 아니라 아까 다른 프로그램이 사용했다가 interrupt 당해서 중지한 부분에 다시 CPU를 준다(보통은) 그 프로그램에 timer가 아직 남아있다면 CPU를 더 쓰게 한다. 그리고 시간이 끝나면 IO요청한 프로그램에게 CPU를 주어서 IO를 가지고 작업할 수 있게 한다. (라운드 로빈 방식이라고 할 수 있다. )
- ⇒ 즉, io작업이 있을 때 프로그램이 자진해서 IO를 해달라고 하고 CPU는 운영체제에게 넘겨진다.
- CPU는 이러는 도중에 IO 결과를 기다리지 않고 다음 instruction을 실행한다.
타이머(timer)
1. timer의 필요성
- 만약에 어떠한 프로그램이 IO작업을 하지 않고 for 혹은 while문을 통해 무한루프에 빠지게 되는 프로그램이라면 프로그램이 종료되지도 않고 IO를 사용하지 않으므로 device controller에게 넘어가지도 않고 CPU를 계속 사용하게 될 것이다. 그렇게 된다면 CPU의 time sharing을 구현할 수 없다. → 그래서 컴퓨터 안에는 timer라는 하드웨어를 두고 있다. (↔만약에 무한 루프를 돌지 않고 프로그램이 종료가 되었다면 자동적으로 CPU를 자동반납을 한다)컴퓨터를 딱 처음 켰을 때는 운영체제가 CPU를 가지고 있다가 여러 사용자 프로그램이 실행하기 시작하면 그 프로그램에게 CPU를 넘겨준다. 그런데 그냥 넘겨주는 것이 아니라 타이머에다가 어떤 값을 세팅한 다음에 사용자 프로그램에게 CPU를 넘겨주게 된다.
- 그럼 사용자 프로그램은 독점적으로 CPU 계속 쓸 수 있는 것이 아니라 할당된 시간(1초 보다 안되는 시간을 세팅)에서면 사용가능하다. 시간이 다 되면 타이머가 CPU에게 세팅한 시간이 끝났다고 interrupt를 알려준다.
2. CPU instruction 확인 과정과 구체적인 timer 작동 방식
- CPU는 매번 instruction을 하나씩 실행하다가 하나의 instruction이 끝나고 나면 interrupt line을 체크한다. (CPU는 계속 instruction만 실행하는 것이 아니라 “instruction 실행 → interrupt line확인 → instruction 실행 → interrupt line 확인을 반복”한다.
- 만약에 timer가 interrupt를 걸었으면 CPU는 하던 일을 잠시 멈추고 CPU의 제어권이 사용자 프로그램으로부터 운영체제에게 “자동으로” 넘어가게 되어있다. 운영 체제가 사용자 프로그램에게 CPU를 자유롭게 줄 수 있지만 한 번 준 것을 뺏을 수는 없다. 넘겨 받으면 사용자 프로그램이 그것을 마음껏 쓰는 것이므로 운영체제가 잘 짜여졌다고 하더라도 뺏을 방법이 없다.
- 그러므로 추가적인 하드웨어 “timer”을 두어 interrupt를 걸어주면 CPU제어권이 자동으로 사용자 프로그램으로부터 운영체제로 넘어오도록 만든 것이다.
- 이렇게 운영체제가 CPU를 넘겨받으면 다음 프로그램에게 timer에다가 값을 세팅하고 CPU를 넘겨준다.
3. timer의 목적
= 특정 프로그램이 CPU를 독점하는 것을 막기 위해서 운영체제가 사용자 프로그램에게 CPU를 넘겨줄 때는 timer에다가 정해진 시간을 할당한 다음에 넘겨주게 된다. (time sharing을 구현하기 위해)
정해진 시간이 흐른 뒤(세팅된 시간이 0이 되면) 운영체제에게 제어권이 넘어가도록 인터럽트를 발생시킨다.
4. timer의 구체적인 특징
✓ 타이머는 매 클럭 틱 때마다 1씩 감소한다.
✓ 타이머 값이 0이 되면 타이머 인터럽트 발생시킨다.
✓ CPU를 특정 프로그램이 독점하는 것으로부터 보호한다.
타이머는 time sharing을 구현하기 위해 널리 이용됨
• 타이머는 현재 시간을 계산하기 위해서도 사용한다.
Device Controller
I/O device controller
1. 해당 I/O 장치유형을 관리하는 일종의 작은 CPU
2. 제어 정보를 위해 control register, status register를 가짐
CPU가 IO device controller에게 일을 시킬 때 이 레지스터를 통해 “무슨 일을 해라”라고 지시하기 위한 것을 의미(명령을 전달할 때 사용)
3. local buffer를 가짐 (일종의 data register) → 실제 데이터를 저장하는 공간
IO device에서 실행하여 얻은 데이터를 CPU쪽으로 넘겨주거나 CPU에서 보낸 것을 화면에 출력하기 위해서는 이를 버퍼에 저장하고 그 다음 출력하게 되는 것이다. (즉, 데이터는 버퍼에 담는 것이고 ‘화면에 출력하라’라는 지시는 제어 레지스터를 통해 한다는 것이다)
• I/O는 실제 device와 local buffer 사이에서 일어난다고 볼 수 있다.
• Device controller는 I/O가 끝났을 경우 interrupt로 CPU에 그 사실을 알린다.
device driver (장치구동기) : OS 코드 중 각 장치별 처리루틴 ⇒ 즉, software
각 디바이스를 처리(접근)하기 위해서 디바이스의 인터페이스가 있는데 거기에 맞게 이것을 접근할 수 있게 해주는 소프트웨어 모듈이다.
하드웨어(디바이스)를 붙이면 이에 접근하기 위한 장치 구동기를 메인 메모리 안에 설치를 해야된다.
운영체제에서 제일 아랫 단까지 가서 디바이스(하드웨어)에 접근하게 되면 디바이스 드라이버 단을 접근하게 되는 것이다.
device controller (장치제어기) : 각 장치를 통제하는 일종의 작은 CPU ⇒ 즉, hardware이다.
Memory Controller
메모리도 하나의 디바이스이므로 컨트롤러가 있다. (다른 디바이스와 비슷하게 붙어있다)
메모리는 원칙적으로 CPU만 접근할 수 있게 한 것이다. 그리고 IO 디바이스들은 자신들의 로컬 버퍼가 존재하기 때문에 버퍼에다가 데이터를 받아서 일을 하고 버퍼에 쌓이게 되면 CPU가 거기에 있는 내용을 읽어서 메인메모리에 접근한다.
⇒ CPU는 메인메모리에 접근할 수 있고 로컬 버퍼에도 접근할 수 있다. ↔ 이와 달리, device controller는 자신의 로컬 버퍼에만 접근 가능하다.
하지만 이렇게 하다보니 CPU가 interrupt를 많이 당한다. CPU가 작업을 하는데 키보드에 입력이 들어와서 interrupt가 걸리고 다른 장치들에게 interrupt를 계속 당하면 CPU는 그것을 처리하느라 효율적이지 않게 동작을 한다. 이를 해결하기 위해 “DMA controller(direct memory access)”를 둔다.
DMA controller
Direct memory controller = 직접 메모리에 접근할 수 있는 컨트롤러이다.
1. 목적
= CPU가 다른 장치들에게 interrupt를 계속 당하면 CPU는 그것을 처리하느라 효율적이지 않게 동작을 한다.
원래는 메인 메모리에 접근할 수 있는 장치는 CPU뿐이었는데 DMA를 두게 되면
⇒ 메모리를 CPU가 접근할 수 있게 되고 DMA도 접근할 수 있게 되어있다.
그래서 CPU와 DMA가 특정 메모리 영역을 동시에 접근할 때 문제가 생길 수 있어서 memory controller는 그것을 중지하는 역할을 담당한다. (누가 먼저 조율할지 교통정리 역할)
2. 결과 =
CPU는 계속 자기 일을 하고 있고 중간 중간에 IO device의 수행으로 인해 local 버퍼에 들어오는 내용의 작업이 끝났으면 DMA가 직접 로컬 버퍼에 있는 내용을 메인 메모리에 복사한다.
-> 이후 CPU에게 interrupt를 한 번만 걸어서 “해당 내용이 모두 메인 메모리에 올라왔다”라고 보고를 한다.
-> 이렇게 되면 CPU가 일하는 도중에 interrupt 당하는 빈도가 줄어들어서 효율적으로 사용할 수 있다.
- 디스크 드라이브 컨트롤러, 그래픽 카드, 네트워크 카드, 사운드 카드를 포함한 많은 하드웨어 시스템이 DMA를 사용한다. (상대적으로 빠른 디스크이기 때문)
[ 참고 ]
CPU의 숙명은 본인이 이번에 수행해야 하는 instruction의 메모리 주소를 레지스터 중에서 PC(program counter)라는 레지스터가 다음 번 실행해야 하는 instruction 주소를 가지고 있다. 그래서 매번 그 instruction만 실행하는 것이 CPU의 운명이다. 그 instruction 중에서 IO 장치에 접근해야되는 상황이 오면 device driver를 통해서 디바이스에게 읽기 혹은 쓰기의 명령을 한다. (디바이스 드라이버가 디바이스에서 직접 디스크의 헤드를 움직여서 읽고 쓰고 하는 코드는 아니다 이런 일은 디스크 컨트롤러가 지시를 받아서 하는 것이다) 디스크 안에 farm ware라고 하는 디바이스를 움직이게 하는 instruction 코드들이 따로 있다. 그리고 디바이스 드라이버는 CPU가 IO 디바이스를 실행하는 필요한 코드를 담고 있는 것이다.
그래서 CPU는 직접 일을 할 수는 없고 메뉴얼이 있다. 메뉴얼에는 메모리 몇번지의 일을 하라는 것이 적혀있고 그 일을 하는 것이다. 이 instruction을 다 수행하게 되면 다음 메모리 공간에 있는 instruction을 실행하게 된다. (스스로 일하는 것이 아니다.) 그리고 CPU에 대한 전체적인 통제를 운영체제가 하는 것이다.
입출력(IO)의 수행
모든 입출력 명령은 모두 특권 명령으로 커널모드에서만 수행가능하다.
(그래서 사용자 프로그램이 직접 IO를 하지 못하고 운영체제를 통해서만 IO장치에 접근할 수 있다)
✓사용자 프로그램은 어떻게 I/O를 하는가?
시스템콜(system call)
- 사용자 프로그램이 운영체제의 서비스를 받기 위해 커널 함수를 호출하는 것(부탁 방법)
- 사용자 프로그램은 운영체제에게 I/O 요청(사용자 프로그램이 운영체제의 커널 함수를 호출하는 것)
⇒ 함수 호출 방식으로 요청하지만 본인이 직접 인터럽트를 걸어서 CPU가 OS에게 넘어가게 하는 트랩을 이용해서 인터럽트를 건다.
* 문제점 = 여기서 사용자가 만든 프로그램 중에 그 안에서 main 함수 호출하거나 조건문이 있으면 이 안에서 메모리 주소를 바꾸면 된다. 하지만 자신의 프로그램을 실행하다가 IO요청을 하기 위해 운영체제에게 요청한다면(시스템 콜) 그냥 메모리 주소를 바꾸어서 되는 것이 아니다 조금 더 복잡하다. 사용자 프로그램 실행 중이기 때문에 mode bit이 1이다. 그래서 사용자 프로그램 내에서만 점프가 가능한데 IO를 하기 위해서 운영체제에 해당하는 주소로 넘어가야 한다. 그러면 mode bit이 1일 때에는 허용이 되지 않는다.
* 해결 방법 = 운영체제의 함수를 호출하기 위해서 디바이스 컨트롤러가 CPU에게 interrupt를 걸 수 있다고 했는데 이런 식으로 시스템 콜을 한다. 즉, 프로그램이 직접 interrupt line을 세팅하는 instruction을 실행한다. (timer, io controller가 interrupt를 요청하는 것이 아니라 프로그램이 운영체제에 요청하기 위해서 소프트웨어적으로 직접 interrupt를 걸 수 있다. CPU는 다음 instruction을 실행하는 것이 아니고 interrupt가 들어왔기 때문에 mode bit이 0이고 CPU 제어권이 운영체제에게 넘어간다. 그러면 디스크에서 어떤 것을 읽어오라고 한 것을 요청하기 위해서 시스템콜을 했기 때문에 운영체제가 CPU를 가지고 있으므로 디스크 컨트롤러에게 읽어오라고 요청할 수 있는 것이다.
- 즉, interrupt는 하드디스크의 정보 교신을 위해 사용 + 사용자 프로그램이 돌아가다가 내가 직접 처리 못하고 운영체제에게 대신 해달라고 요청을 해야하는 상황에서도 interrupt를 발생시킬 수 있는데 이를 소프트웨어 interrupt라고 부른다.
[구체적인 동작 과정]
✓ trap을 사용하여 인터럽트 벡터의 특정 위치로 이동
✓ 제어권이 인터럽트 벡터가 가리키는 인터럽트 서비스 루틴으로 이동
✓ 올바른 I/O 요청인지 확인 후 I/O 수행(아무거나 요청을 들어주어선 안된다, IO 접근 권한이 있는지 확인) → IO컨트롤러가 요청을 실행 → CPU는 다른 프로그램으로 넘김 → IO가 끝나면 하드웨어 인터럽트가 걸릴 것임
✓ I/O 완료 시 제어권을 시스템콜 다음 instruction으로 옮김
총 2번의 인터럽트가 발생한다.
(첫 번째 인터럽트) 프로그램이 IO를 수행하기 위해 시스템 콜을 함(트랩) = 요청
(두 번째 인터럽트) IO에게 시킨 일이 다 끝나면 하드웨어 인터럽트가 걸림
즉, IO를 하기 위해서 필요한 인터럽트는 소프트웨어 인터럽트 + 하드웨어 인터럽트이다.
인터럽트
인터럽트 = 인터럽트 당한 시점의 레지스터와 program counter를 save 한 후 CPU의 제어를 인터럽트 처리 루틴에 넘긴다
Interrupt (넓은 의미)
✓ Interrupt (하드웨어 인터럽트): 하드웨어가 발생시킨 인터럽트(일반적인 인터럽트) IO장치 작업
✓ Trap (소프트웨어 인터럽트) : 소프트웨어가 발생시키는 인터럽트
(1) Exception: 프로그램이 오류를 범한 경우
(2) System call: 프로그램이 커널 함수를 호출하는 경우
[인터럽트 관련 용어]
인터럽트는 타이머가 거는 인터럽트, 키보드 컨트롤러가 거는 인터럽트 등 다양한 인터럽트가 있는데 이때마다 다른 작업을 해야 한다.
✓ 인터럽트 벡터
• 해당 인터럽트 처리루틴의 주소를 가지고 있음
인터럽트 종류가 다양한데 각 인터럽트마다 어디에 있는 함수를 실행해야 할 것인지 표시를 해주는 것이다. 함수의 주소들을 정의한다.
✓ 인터럽트 처리 루틴 (=Interrupt Service Routine, 인터럽트 핸들러)
• 해당 인터럽트를 처리하는 커널 함수
인터럽트 종류마다 해야할 것들을 운영체제의 코드에 다 정의가 되어 있는데 각 인터럽트 종류마다 처리해야 하는 코드를 “인터럽트 처리 루틴” 이라고 부른다.
“현대의 운영체제는 인터럽트에 의해 구동된다” 운영체제는 CPU를 사용할 일이 없고 인터럽트가 들어올 때만 CPU가 운영체제에게 넘어가는 것이지 그렇지 않으면 운영체제는 사용자 프로그램이 사용하고 있는 것이다.
*이 글은 이화여자대학교 반효경 교수님의 운영체제 강의를 참고하여 작성했습니다.
'CS > 컴퓨터 시스템' 카테고리의 다른 글
Chap 7. 프로세스_1 (0) | 2022.06.24 |
---|---|
Chap 6. 프로그램 실행 (0) | 2022.05.21 |
Chap 4. 운영체제란? (0) | 2022.05.18 |
Chap 3 - 3. static 라이브러리와 shared 라이브러리 (0) | 2022.05.14 |
Chap 3 - 2. 부분 컴파일 (0) | 2022.05.13 |