관리 메뉴

클라이언트/ 서버/ 엔지니어 " 게임 개발자"를 향한 매일의 공부일지

입출력장치 2 - 다양한 입출력 방법 1 : 프로그램 입출력 및 인터럽트 기반 입출력 본문

알고리즘 및 자료 관리/컴퓨터 구조 & 운영체제

입출력장치 2 - 다양한 입출력 방법 1 : 프로그램 입출력 및 인터럽트 기반 입출력

huenuri 2024. 10. 16. 16:08

이제 오후 공부를 시작해 본다. 하나의 타임이 끝나고 새로운 시간을 열 때가 가장 시작하기 어려운 것 같다. 그래도 일단 시작하면 다시 집중해서 열심히 하게 된다. 

가장 보편적인 입출력 방법인 프로그램 입출력과 인터럽트 기반 입출력, DMA 입출력에 대해 알아보겠다.

 

 

 

입출력 작업을 수행하려면 CPU와 장치 컨트롤러가 정보를 주고받아야 한다. 그렇다면 장치 컨트롤러는 CPU와 어떻게 정보를 주고받을까? 여기에는 크게 세 가지 방법이 있다. 프로그램 입출력, 인터럽트 기반 입출력, DMA 입출력이다. 


 

 

 

 

프로그램 입출력

 

메모리에 저장된 정보를 하드 디스크에 백업하는 상황을 생각해보자. CPU는 대략 아래 과정으로 입출력 작업을 한다.

 

 

 

 

 

 

 

 

 

 


 

 

 

 

메모리 맵 입출력

 

가령 1024개의 주소를 표현할 수 있는 컴퓨터가 있을 때 1024개 전부 메모리 주소를 표현하는 데 사용하지 않는다. 512개는 메모리 주소를, 512개는 장치 컨트롤러의 레지스터를 표현하기 위해 사용하는 것이다.

 

 

주소 공간 일부를 아래와 같이 약속했다고 가정해보자.

CPU는 '517번지를 읽어 들여러'라는 명령어로 키보드 상태를 읽을 수 있다. 그리고 '518번지에 a를 써라'라는 명령어로 하드 디스크 컨트롤러의 데이터 레지스터로 데이터를 보낼 수 있다.

이때 중요한 점은 메모리 맵 입출력 방식에서 CPU는 메모리의 주소들이나 장치 컨트롤러의 레지스터들이나 모두 똑같이 메모리 주소를 대하듯 하면 된다는 점이다. 그래서 메모리에 접근하는 명령어와 입출력장치에 접근하는 명령어는 굳이 다를 필요가 없다.

 

CPU가 '517번지를 읽어라'라는 명령어를 실행했을 때 517번지가 메모리상의 주소를 가리킨다면 CPU는 메모리 517번지에 저장된 정보를 읽어 들일 것이고, 517번지가 프린터 컨트롤러의 상태 레지스터를 가리킨다면 CPU는 프린터의 상태를 확인할 수 있을 것이다.


 

 

 

고립형 입출력

 

가령 1024개의 주소 공간을 가진 컴퓨터가 있다고 가정해 보자. 위의 그림처럼 제어 버스에 '메모리 읽기/쓰기' 선 외에 '입출력장치 읽기/쓰기 선'이 따로 있다면 메모리에도 1024개의 주소 공간을 활용하고, 입출력장치도 1024개의 주소 공간을 활용할 수 있다.

CPU가 메모리 읽기/쓰기 선이 활성화되는 명령어를 실행할 때는 메모리에 접근하고, 입출력장치 읽기/쓰기 선이 활성화되는 명령어를 실행할 때는 장치 컨트롤러에 접근하기 때문이다.

 

 

 

고립형 입출력은 메모리에 접근하기 위한 주소 공간과 입출력 장치에 접근하기 위한 주소 공간을 분리하는 입출력 방식이다.

 


 

 

 

 

인터럽트 기반 입출력

 

이전 장에서 'CPU가 입출력장치에 처리할 내용을 명령하면 입출력장치가 명령어를 수행하는 동안 CPU는 다른 일을 할 수 있다'라고 했다. 또한 '입출력장치가 CPU에게 인터럽트 요청 신호를 보내면 CPU는 하던 일을 잠시 멈추고 해당 인터럽트를 처리하는 프로그램인 인터럽트 서비스 루틴을 실행한 뒤 다시 하던 일로 되돌아온다'라고 했다.

 

 

 

CPU는 장치 컨트롤러에 입출력 작업을 명령하고, 장치 컨트롤러가 입출력장치를 제어하며 입출력을 수행하는 동안 CPU는 다른 일을 할 수 있다. 장치 컨트롤러가 입출력 작업을 끝낸 뒤 CPU에게 인터럽트 요청 신호를 보내면 CPU는 하던 일을 잠시 백업하고 인터럽트 서비스 루틴을 실행한다.

이렇게 인터럽트를 기반으로 하는 입출력을 인터럽트 기반 입출력이라고 한다.


 

 

폴링

인터럽트와 자주 비교되는 개념 중 폴링(polling)이라는 개념이 있다. 폴링이란 입출력장치의 상태는 어떤지, 처리할 데이터가 있는지를 주기적으로 확인하는 방식이다.

폴링 방식은 인터럽트 방식보다 CPU의 부담이 더 크다. 인터럽트를 활용하면 CPU가 인터럽트 요청을 받을 때까지 온전히 다른 일에 집중할 수 있기 때문이다.

 

 

 

여러 입출력장치에서 인터럽트가 동시에 발생한 경우에는 인터럽트들을 어떻게 처리해야 할까?

 

 

 

 

다른 입출력장치에 의한 하드웨어 인터럽트를 받아들이지 않기 때문에 CPU는 이렇듯 순차적으로 하드웨어 인터럽트를 처리하게 된다.

 

 

 

CPU는 인터럽트 간에 우선순위를 고려하여 우선순위가 높은 인터럽트 순으로 여러 인터럽트를 처리할 수 있다.

 

 

 

예를 들어 현재 CPU가 인터럽트 A를 처리하는 도중에 또 다른 인터럽트 B가 발생했다고 가정해 보자. 만약 지금 처리 중인 인터럽트 A보다 B의 우선순위가 낮다면 CPU는 A를 모두 처리한 뒤 B를 처리한다. 하지만 인터럽트 A보다 B의 우선순위가 높다면 CPU는 인터럽트 A의 실행을 잠시 멈추고 인터럽트 B를 처리한 뒤 다시 A를 처리한다.

 

플래그 레지스터 속 인터럽트 비트가 활성화되어 있는 경우, 혹은 인터럽트 비트를 비활성화해도 무시할 수 없는 인터럽트인 NMI(Non-Maskable interrupt)가 발생한 경우 CPU는 이렇게 우선순위가 높은 인터럽트부터 처리한다.

 

 

 

많은 컴퓨터에서는 프로그래머블 인터럽트 컨트롤러(PIC)라는 하드웨어를 사용한다. PIC에는 위 그림처럼 여러 핀이 있는데, 각 핀에는 CPU에 하드웨어 인터럽트 요청을 보낼 수 있는 약속된 하드웨어가 연결되어 있다. 가령 첫 번째 핀은 타이머 인터럽트를 받아들이는 핀, 두 번째 핀은 키보드 인터럽트를 받아들이는 핀.. 이런 식으로 말이다.

PIC에 연결된 장치 컨트롤러들이 동시에 하드웨어 인터럽트 요청을 보내면 PIC는 이들의 우선순위를 판단하여 CPU에 가장 먼저 처리할 인터럽트를 알려준다.

 

 

PIC의 다중 인터럽트 처리 과정을 조금 더 정확히 알아보면 다음과 같다.

  1. PIC가 장치 컨트롤러에서 인터럽트 요청 신호(들)를 받아들인다
  2. PIC는 인터럽트 운선순위를 판단한 뒤 CPU에 처리해야 할 인터럽트 요청 신호를 보낸다.
  3. CPU는 PIC에 인터럽트 확인 신호를 보낸다.
  4. PIC는 데이터 버스를 통해 CPU에 인터럽트 벡터를 보낸다.
  5. CPU는 인터럽트 벡터를 통해 인터럽트 요청의 주체를 알게 되고, 해당 장치의 인터럽트 서비스 루틴을 실행한다.

일반적으로 더 많고 복잡한 장치들의 인터럽트를 관리하기 위해 아래와 같이 PIC를 두 개 이상 계층적으로 구성한다. 이렇게 PIC를 여려 개 사용하면 훨씬 더 많은 하드웨어 인터럽트를 관리할 수 있다.

 

 

PIC가 무시할 수 없는 인터럽트인 NMI까지 우선순위를 판별하지는 않는다. NMI는 우선순위가 가장 높아 우선순위 판별이 불필요하기 때문이다. PIC가 우선순위를 조정해 주는 인터럽트는 인터럽트 비트를 통해 막을 수 있는 하드웨어 인터럽트이다.

 

 

 

여러 가지 동시 다발적으로 발생하는 하드웨어 인터럽트를 직접 확인해 볼 수도 있다.

 


 

 

 

학습을 마치고

이 단원도 분량이 많아서 두 개로 나누어서 작성해 본다. DMA 입출력은 다음 포스트에서 다루어보려고 한다. 이 내용은 솔직히 많이 어렵고 강의를 듣고 책을 읽어도 무슨 말인지 잘 이해가 되지 않았다. 

그리고 오늘 오후부터는 공부하는 게 왜 이렇게 지치던지 정말 하기 싫은 마음이 80% 이상이었다. 그래도 다시 오후 수업에 나와서 공부하는 분위기 속에서 다시 집중하게 되었다.

 

오늘 운영체제까지 진도를 나가려고 했지만 하지 못할 가능성이 많을 것 같다. 솔직히 너무 어려운 컴퓨터 구조를 공부하느라 요즘 힘들었던 것 같다. 운영 체제는 머리를 식히면서 다른 공부를 하다가 다음 주쯤 공부해 볼 생각이다.