- 외부 IO 장치는 이들의 응답 시간을 결정하는 물리적 이동 속도는 Computer의 CPU나 주 Memory의 속보도다 몇 배는 느리다.
- IO 데이터 전송 속도의 병목은 주로 외부 장치의 속도이므로, 컴퓨터가 장치가 처리할 수 있는 것보다 데이터를 빨리 전송하지 않도록 주의해야 한다.
- 모든 IO 장치는 CPU에 대해서 비동기적으로 동작한다.
- 다시 말해서 입력 장치에 데이터가 있거나 출력 장치에 데이터가 필요함을 알리는 Event의 발생 시기는 CPU에 독립적이다.
- 따라서 대부분의 IO 프로그래밍은 안정된 데이터 전송을 위해서 CPU와 IO 장치 간의 상당량의 Handshaking이 필요하다.
- IO 소프트웨어는 IO 장치의 요구사항과 제약사항을 세심하게 고려해서 설계해야 한다.
- 설계 방식은 Polling waiting loop, Interrupt driven IO, DMA(Direct Memory Access) 방식이다.
1. 인텔 IO 명령어
- 많은 IO 포트 번호가 0 ~ 255 범위에 들기 때문에 1 Byte 안에 넣을 수 있다.
- 하지만 IO 포트 주소 버스의 폭은 12 bit 이기 때문에 포트 번호는 4095까지 될 수 있다.
- 포트 번호가 1 Byte에 들어가지 않는 경우에는 포트를 접근하기 전에 포트 번호를 DX Register에 넣어둬야 한다.
- IN, OUT, INSB, OUTSB
2. 동기화, 전송 속도, 대기 시간
- 입력 데이터는 IO 장치에 있지만 장치가 출력 데이터를 받을 준비가 되어 있는 지를 가리키는 Event 발생은 컴퓨터의 제어 밖에 있다.
- IO 장치과 데이터를 주고 받으려면 장치의 상태를 조사하고 준비될 때까지 기다림으로써 CPU를 IO 장치에 동기화해야 한다.
- 전송 속드는 단순히 CPU와 외부 장치 사이에 1초 동안 전송된 Byte 수다.
- 인터럽트 구동 IO는 인터럽트가 발생할 때마다 두 Byte 이상 전송하지 않는다면, 기계의 상태를 저장하고 복원하는 Load 때문에 심각하게 성능이 저하된다.
- 대기 시간(latency)은 장치가 준비된 시점부터 첫 데이터 Byte가 전송될 때까지의 지연 시간이다. --> 대기 시간은 응답 시간과 같다.
3. 폴링 대기 루프
- 각각의 데이터 바이트를 전송하기 전에 장치의 상태를 S/W 로 테스트 한다.
- 성능을 제한하는 주된 병목은 메모리 대역폭이다.
- Serial_Input은 명령어를 가져오고 Stack에서 Return Address 4 Byte를 가져오는 것을 제외하면 메모리 접근을 하지 않는다.
- 게다가 IO 전송을 두 번 수행해야 한다.
추가 정리 필요
4. 인터럽트 구동 I/O
- 예문
- 집에서 소득세를 계산하고 있을 때 전화가 오면(인터럽트), 세금 계산을 멈추고 전화를 받는다(인터럽트를 받아들인다.)
- 전화는 친구가 자신의 자동차 수리를 위해서 자동차 수리공의 이름과 전화번호를 묻는 것이었다.
- 자동차 수리공의 이름과 전화번호를 준다(인터럽트 요청을 즉시 처리한다.)
- 인터럽트를 이용하려면 소프트웨어가 그 이점을 활용할 수 있도록 구성하기 위해서 먼저 CPU가 Interrupt를 어떻게 처리하는지를 이해해야 한다.
4.1 하드웨어 응답
- IO 장치는 전송할 데이터가 생기면 CPU에 인터럽트 요청을 보낸다.
- 인터럽트가 허용되어 있으면(IF Flag가 1로 Setting), CPU는 현재 명령어의 실행을 마치고 EFlags와 Return Address(CS와 EIP)를 Stack에 넣은 다음, 이후의 Interrupt를 금지하고(Disable) 해당 ISR로 점프한다.
- ISR에 진입하면 인터럽트를 다시 허용해서 우선순위가 더 높은 인터럽트가 현재의 ISR을 선점할 수 있도록 한다.
- CPU Register의 현재값을 모두 보존하고 IO 데이터를 전송한 다음, 낮은 우선순위의 인터럽트를 다시 허용하고 CPU 레지스터의 원래 값을 복원한다.
- 마지막으로 IRET 명령어는 EIP, CS, EFlags Register를 POP 하고 Interrupt 된 Code에게 제어를 돌려준다.
- ISR은 모든 CPU Register의 내용을 보존/복원해서 Interrupt 된 Code가 약간의 시간 지연 외에는 아무 일도 없었던 것처럼, 떠낫떤 지점에서 계속할 수 있어야 한다.
아래는 H/W Interrupt 처리 순서이다.
1. H/W Interrupt 요청 발생
- CPU는 현재 명령어를 마친 뒤 인터럽트 대응 절차를 시작한다.
2. Interrupt 대응 절차
- CPU는 EFlags와 복귀주소(CS와 EIP)를 PUSH하고, Interrupt를 금지하고, 인터럽트를 요청한 장치에서 인터럽트 종류 코드를 읽고, 제어를 해당하는 ISR로 넘긴다.
3. Interrupt Service Routine
- 우선 순위가 더 높은 Interrupt를 다시 허용
- CPU Register 보존
- 데이터 전송(그리고 인터럽트 요청 삭제)
- 우선순위가 더 낮은 Interrupt를 다시 허용
- CPU Register 복원
- EIP, CS, EFlags 를 POP 하고 Interrupt 된 Code로 복귀
- CPU가 Interrupt 요청을 받아들이면, 요청한 장치는 Interrupt 종류 코드 1 Byte를 데이터 버스에 올려 놓는다.
- CPU가 이 Byte를 IDT에 대한 Index로 사용해서 호출할 ISR의 Entry Address를 얻는다.
- Interrupt 종류 Code는 IDTR Register에 있는 IDT 주소와 AND 연산을 통해서 Interrupt Descriptor Table에 있는 ISR의 Physical Address를 알아낸다.
- Interrupt Descriptor Table의 ISR은 Main Memory에 존재한다.
추가 정리 필요
- 인터럽트는 진행 중인 명령어를 방해할 수는 없으므로, 두 명령어 실행 사이에만 인식된다.
- 따라서 인터럽트 요청부터 하드웨어 응답 완료까지의 최대 대기 시간은 가장 느린 명령어의 실행 시간 + 하드웨어 응답에 필요한 메모리 전송 시간이다.
-