본문 바로가기

CS/컴퓨터구조

[컴퓨터구조] I/O

현재 글은 컴퓨터 구조와 아키텍처 서적 및 대학에서 들은 강의를 기반으로 한다. 


External Devices

 외부 기기는 외부 환경과 컴퓨터 사이에서 데이터를 전달할 때 사용되며, I/O 모듈의 링크를 통해 컴퓨터와 연결된다. 링크는 control, status, data을 외부 기기와 I/O 모듈 사이에서 교환하는 데 사용된다.

  • Control: 디바이스가 수행할 기능. INPUT, READ, OUTPUT, WRITE 등
  • Status: 디바이스의 상태를 나타내는 정보. READY, NOT-READY 등
  • Data: I/O 모듈과 교환하는 비트 모음

이러한 외부 기기의 종류는 크게 3가지로 나눌 수 있다.

  • Human Readable: 컴퓨터를 사용하는 유저와 소통하는데 사용.
    • 프린터, 모니터 등
  • Machine Readable: 장비(equipment)와 소통하는데 사용. 
    • 마그네틱 디스크, 테이프, 센서 등 
  • Communication: 원격의 기기와 소통하는데 사용. 원격의 기기는 프린터, 센서 컴퓨터 등 모두 가능하다.

External Device의 간략한 구조

 외부 기기는 컴퓨터와의 커뮤니케이션을 통해 데이터를 주고받는다. 이때 외부 기기와 컴퓨터의 데이터 구조가 다르기 때문에 이를 처리하기 위한 장치들이 필요하다. transducer은 외부 기기와 컴퓨터가 받아들일 수 있는 형태로 데이터를 변환하는 기기로, 기계적인 신호를 전자적인 신호로 바꾸는 등의 역할을 수행한다. 예를 들어 카메라로 사진을 찍으면 카메라 내의 센서들이 자신이 받은 자극을 픽셀 형태로 저장한다. 이때 센서가 받은 "빛"이라는 물리적인 자극 자체는 컴퓨터에 저장될 수 없으며, 이를 전자적 신호로 바꿔야만 외부로 표현할 수 있다. 변환 작업을 위한 공간으로 버퍼가 필요하며, 장치의 상태나 동작을 위해 Control Logic도 존재하는 모습이 보인다.


I/O 모듈

일반적인 I/O 모듈의 구조

 I/O 모듈은 버스와 외부 디바이스(peripheral devices)들을 연결한다. 외부 기기는 CPU 및 메모리와의 속도 차이, 다루는 데이터의 구조 등이 달라 직접 통신할 수 없으므로 I/O 모듈은 이들을 연결하는 인터페이스 역할을 한다.

I/O 모듈의 기능

  • Control and Timing: 컴퓨터 내부 리소스와 외부 기기 사이의 flow, traffic을 조정한다.
  • Processor Communication: Command Decoding, Data, Status Reporting, Address Recognition 등 수행.
    • Command Decoding: 프로세서가 I/O 모듈에게 전달하는 READ, SECTOR 등의 명령 해석
    • Data: 데이터 버스 통해서 I/O 모듈과 데이터 교환
    • Status Reporting: 외부 기기의 속도가 CPU 등에 비해 상당히 느리므로 이들의 상태를 프로세서에게 알려줌.
    • Address Recognition: 외부 I/O 디바이스에 할당된 고유한 주소를 식별
  • Device Communication: Command, Status Information, Data
  • Data Buffering: I/O 기기와 CPU 사이의 속도 차이에 따른 문제를 해결하기 위함
  • Error Detection: 에러를 감지하고 이를 알림

I/O 모듈의 간략한 구조


I/O Operation 방법

  • Programmed I/O
  • Interrupt-driven I/O
  • Direct Memory Access(DMA)

Programmed I/O

 데이터를 전송할 때  프로세서가 직접 개입하는 방식이다. 외부 기기는 인터럽트를 걸지 않으므로 데이터 전송 과정이 완료되었는지 알기 위해서는 프로세서가 직접 검사해야 한다. 프로세서가 I/O 작업을 특정 외부 기기에 맡길 때 해당 작업의 종료 여부를 계속 검사해야 하므로 I/O가 끝나기 전까지는 다른 일을 할 수 없는 상태가 된다.

 프로세서가 I/O 작업을 만나면 적절한 I/O 모듈에게 신호를 보낸다. 해당 모듈은 요청된 작업을 수행하고 I/O 상태 레지스터를 적절하게 설정한다. 이때 I/O 모듈은 프로세서에게 자신의 작업이 종료되었음을 알리지 않으므로 프로세서가 작업이 완료되었는지 알기 위해서는 모듈의 작업이 끝났는지 여부를 주기적으로 검사할 수 밖에 없다. 이 동작은 busy waiting이라고 부른다.

 Programmed I/O 환경에서 프로세서는 다음 명령어를 가진다.

  • Control: 주변 장치를 활성화하고 어떤 작업을 할지 알려줌.
  • Test: I/O 모듈 및 주변 장치의 다양한 상태 조건을 테스트.(파워, 종료, 에러 등 정보)
  • Read/Write

Programmed I/O 동작

Memory Mapped I/O vs Isolated I/O

 각 디바이스에는 고유한 주소가 부여 된다. 이때 I/O 명령에는 해당 명령을 실행할 디바이스의 주소도 포함되므로 디바이스들은 해당 명령이 자신의 것인지 알기 위해 들어오는 주소를 해석해야 하는 문제가 있을 수 있다.

  • Memory Mapped I/O
    • 디바이스와 메모리가 주소 공간을 공유하는 방식
    • I/O 디바이스의 주소가 메인 메모리 상에 배치되므로 메모리에 대한 명령어를 공유하여 사용할 수 있다.
    • I/O 디바이스를 위한 특별한 커맨드가 필요하지 않다.
    • 대량의 메모리 데이터를 접근할 때 유용하다.
  • Isolated I/O
    • 각 디바이스마다 고유의 메모리 공간을 가지는 방식.
    • 메인 메모리 공간을 디바이스에게 할당할 필요가 없다.
    • I/O 디바이스를 위한 별도의 명령어가 요구된다.
    • 작은 데이터에 접근할 때는 사용할 수 있다.

Memory-mapped 방식과 Isolated 방식의 차이

 위 그림에서는 Memory-mapped I/O와 Isolated I/O에 대한 예시를 보여주고 있다. Memory-mapped I/O에서는 키보드에 대한 input 및 control register이 516 및 517번 주소에 할당되어 있으며, Load, Store 같은 메모리에서 일반적으로 사용하는 instruction을 통해 키보드 작업을 진행할 수 있다. 반면 Isolated I/O 방식에서는 Load I/O, Test I/O 같이 I/O 모듈을 위한 별개의 명령을 요구하며 메모리 공간 역시 따로 분리되어 있다.


Interrupt-driven  I/O

 프로세서는 I/O 명령을 내리고 다른 업무를 수행하다가 명령을 받은 기기가 I/O을 수행할 수 있는 상태(READY)가 되면 I/O 작업을 수행하는 방식이다. I/O을 요청하고 계속 해당 모듈의 진행 상태를 검사해야 하는 Programmed I/O 와는 달리, d인터럽트 기반 I/O에서는 모듈이 자신의 작업이 끝났음을 인터럽트를 통해 통지하므로 프로세서는 다른 일을 수행하다가 모듈의 통지(인터럽트)를 받고 기존 작업에 복귀할 수 있게 된다.

인터럽트 동작 과정

  1. 디바이스가 프로세서에게 interrupt signal 신호를 보낸다.
  2. 프로세서는 현재 진행중인 instruction을 끝내고 해당 인터럽트에 대응한다.
  3. 프로세서는 인터럽트를 test 한 후 인터럽트를 보낸 기기에게 ack 신호를 보낸다.
    기기는 ack 신호를 받고 interrupt signal을 제거할 수 있다.
  4. 프로세서는 Interrupt control을 수행하기 전에 프로세서의 상태를 저장하는 PSW(Program Status Word) 레지스터의 내용과 다음 실행될 instruction 위치를 담은 PC(Program Counter) 레지스터 정보를 저장한다.
  5. 프로세서는 인터럽트 한 프로세스를 위해 해당 프로세스의 PC 값을 저장한다.
  6. 현재 실행 중이었던 프로세스의 상태를 저장한다.
  7. 인터럽트 관련 프로세스 작업을 수행한다.
  8. 프로세스가 끝나면 기존 프로세스의 상태를 레지스터에 복구한다.
  9. PSW 및 PC 정보를 복구한다.

인터럽트의 디자인 이슈

인터럽트에서는 두가지 이슈가 발생할 수 있다.

  • 많은 I/O 모듈이 있을 때, 프로세서는 어떻게 인터럽트를 일으킨 모듈을 식별할 수 있나?
  • 다수의 인터럽트가 동시에 발생했다면, 어떤 것을 먼저 선택해야 하나?

이 문제는 다음 4가지 방식으로 해결한다.

  • Multiple interrupt line
    • 프로세서와 I/O 모듈 사이에 여러 개의 interrupt line을 둔다.
    • 단, 인터럽트가 매우 많은 경우 어차피 중복이 발생하므로 각 라인에 다른 기법을 추가로 적용한다.
  • Software Poll
    • 인터럽트가 발생했을 때 소프트웨어 수준에서 해당 인터럽트가 어디서 발생했는지 검사한다.
    • 소프트웨어 기반이라 속도가 느린 단점이 있다.
  • Daisy Chaining(vector based)
    • 하드웨어적으로 Polling을 구현한 것. 하드웨어 수준에서 인터럽트 발신지를 검사할 수 있다.
    • I/O 모듈의 주소 등 고유 정보를 담고 있는 vector이라는 정보를 이용하며, 이에 기반하여 위치를 찾는다.
  • 벡터 기반 버스 중재
    • 버스 컨트롤을 얻어야 interrupt request line 가능.
    • interrupt request 감지하면 프로세서가 interrupt acknowledge line으로 응답.
    • 요청한 모듈은 데이터 라인에 자신의 벡터를 올려 놓음.

인터럽트 컨트롤러 구조 예시

 위 그림에서는 82C59A 칩을 이용하여 64개의 기기에 대한 인터럽트 컨트롤러를 구성한 모습을 보여준다. 각 칩은 내부적인 우선순위로 8개 중 하나를 선택하고, 나온 8개의 디바이스 중에서도 우선순위에 따라 하나가 선택된다고 한다.


DMA

DMA 동작 과정

Programmed I/O 및 Interrupt-driven I/O 모두 데이터 전송에 프로세서가 참여한다. 따라서 다음과 같은 문제가 있었다.

  • I/O 전송률이 프로세서가 I/O 장치를 테스트 및 제어할 수 있는 수준으로 제한된다.
  • 프로세서가 I/O 전송을 관리하기 때문에 수많은 instruction이 I/O 전송에 사용된다.

 프로세서는 데이터 전송 과정을 관리하며 단순한 통로 역할을 수행한다. 이때 부가 장치들은 프로세서에 비해 속도가 상당히 느리기 때문에 I/O 작업이 많아질수록 프로세서 본연의 성능을 뽑아내기 어렵다. 따라서 I/O 작업을 다른 기기를 관리하게 만들면 프로세서는 I/O을 제외한 연산 작업에 집중할 수 있으므로 전체적인 성능이 좋아진다.

 DMA는 프로세서가 I/O 작업에 직접적으로 개입하지 않는 방식이다. I/O 작업은 별도의 DMA 모듈이 주도하며, DMA 모듈과 메모리 사이에서 직접 데이터 교환이 발생한다. CPU는 I/O을 원할 때 DMA controller에게 다음과 같은 정보를 보내며 DMA 동작을 요청한다.

  • READ/WRITE 중 원하는 컨트롤 정보
  • 디바이스 주소
  • 데이터 메모리 블록의 시작 주소(physical address 제공 필요).
    가상 메모리를 다루는 도중에 Scatter/Gather 동작이 필요할 수 있음.
  • 전송할 데이터의 크기

 요청된 작업은 DMA 모듈에게 이관되며, CPU는 바로 다른 작업을 수행할 수 있다. DMA 모듈은 CPU가 버스를 사용하지 않을 때 버스 사이클을 훔쳐(cycle stealing) 작업을 수행할 수 있으며, 작업이 끝난 후 CPU에게 완료 사실을 통지한다. 즉, CPU는 제어 신호만 주고받으며 실제 데이터 전송은 DMA 모듈이 맡는다.

Instruction cycle 중 DMA와 Interrupt Breakpoint

 이때 DMA의 버스 사용 요청은 I/O 작업이라 프로세스의 실행 과정과 상관없으므로, 인터럽트와는 달리 프로세서 및 프로세스의 현재 상태를 저장할 필요가 전혀 없다(Context Switching이 필요한 상황이 아니다). 따라서 DMA 모듈의 요청이 발생하면 프로세서는 현재 상태 그대로 버스 사용권을 넘겨줄 수 있다.

DMA와 버스 구성 방식

 버스의 구조는 구현에 따라 다양할 수 있다. DMA에 따른 I/O 버스가 있어서 확장성이 좋을 수도 있고, 기존 버스에 I/O 장치나 DMA가 함께 있을 수도 있다.

8237 칩. System Bus을 공유해서 사용하는 방식

  • Fly-By DMA Controller: 데이터가 DMA 칩에 저장되지 않고, I/O 포트에서 메모리로 직접 전송되는 방식으로,  포트에서 포트 또는 메모리에서 메모리로 데이터를 전송하지는 못한다.

Direct Cache Access

 현재 데이터 전송 속도가 매우 빠름에도 메모리 수준에서 해당 속도를 따라가지 못하는 경우가 많다. 따라서 CPU 칩 외부의 캐시(L3 등 last level cache)에 대해서도 직접 I/O을 수행하여 더 빠르게 접근하자는 것이 DCA이다. 

인텔 CPU Xeon 시리즈의 칩 아키텍처

 인텔의 Xeon 칩 시리즈는 bidirectional ring을 이용하여 shared cache(L3), 각 코어 등을 연결한다. ring에는 데이터를 실어 담을 수 있는데, 각 시간 간격(time slot)이 있어서 특정 시간대에 데이터를 담으면 특정 코어 또는 캐시에서 해당 내용을 가져가는 방식으로 동작할 수 있다. 이렇듯 일정한 단위로 ring이 나뉘기 때문에 일종의 파이프라인처럼 데이터를 실어 나를 수 있다. ring은 여러 개가 있기 때문에 목적지에 가까운 ring에 데이터를 실어 옮길 수 있다. 코어의 수가 더 많아지는 경우 ring의 개수를 늘려 속도를 높일 수 있다고 한다.

 현재 컴퓨터에서 네트워크 통신은 빼놓을 수 없다. 이때 컴퓨터의 내용이 네트워크 상에 실리기 위해서는 일정한 형태의 데이터 단위로 포장될 필요가 있다. 계층이 낮아질 때마다 추가되는 헤더 정보들은 I/O 컨트롤러 또는 NIC(Network Interface Controller)에 의해 작업이 이루어진다. 이때 네트워크를 통해 들어오거나 나오는 트래픽에 대한 작업에 코어, 메모리 및 캐시 모두 참여하게 되므로 성능이 좋지 않다. 

  1. 애플리케이션이 네트워크를 통해 전송하고 싶은 데이터를 메모리 상 application-assigned buffer에 올린다.
  2. 코어는 해당 내용을 시스템 버퍼에 저장한 후 필수적인 TCP/IP 헤더를 붙여 시스템 메모리에 둔다.
  3. 헤더가 붙은 데이터는 DMA에 의해 NIC로 이동한다.

위 과정에는 메모리 및 캐시가 모두 참여한다고 한다. 이때 두 가지 요소에 의해 성능이 저하될 수 있다.

  1. 코어는 system, application buffer의 정보를 복사하기 위해 클럭 사이클을 낭비한다.
  2. 메모리의 속도가 CPU를 넘을 수는 없으므로 코어는 메모리의 R/W 시간만큼 대기하느라 시간을 낭비한다.

네트워크 I/O에 대해 캐시를 효율적으로 사용할 수 있도록 여러 방법이 연구되었다고 한다.

  1. 들어오는 네트워크 트래픽이 시스템 메모리에 도착하자마자 prefetch hint를 보내서 코어가 데이터 패킷을 빠르게 가져옴으로써 cache miss 및 연관된 코어 사이클 낭비를 막는다.
  2. 패킷 및 패킷 디스크립터 정보는 코어에 의해 단 한 번만 읽히므로 시스템 버퍼를 다시 접근하지 않기 위해 들어오는 패킷을 코어가 읽어 데이터를 뽑아낸 후 페이로드를 application buffer에 바로 보낸다. 

DDIO(Direct Data I/O)

DDIO을 설명하는 그림들

 네트워크 프로세싱 과정에 메모리를 거치지 않고 직접 캐시에 접근하는 방식. DMA I/O에서 CPU가 데이터를 연산하려면 DMA -> 메모리 -> 캐시로 데이터를 옮겨와야 하는데, 현재 네트워크 속도가 매우 빠르기 때문에 메모리에서 가져오는 지연 조차도 시간 낭비로 작용하며, 메모리에 저장한 패킷을 다시 사용할 일이 거의 없으므로 메모리에 저장하는 것도 큰 의미가 없다. Intel에서는 이러한 의미 없는 속도 지연을 더 줄이기 위해 I/O 컨트롤러가 메모리가 아닌 Last Level Cache로 바로 데이터를 보낼 수 있게 구현했다고 한다.


I/O 기능의 발전

  1. CPU가 주변 기기를 직접 제어
  2. 컨트롤러나 I/O 모듈이 추가하여 장치 제어. CPU는 Programmed I/O 수행
  3. 인터럽트가 추가되어 CPU가 I/O 작업 시작을 기다리지 않음. I/O 자체는 CPU가 관여
  4. DMA가 추가되어 CPU는 I/O 작업에 대한 신호만 주고받음. I/O에 관여하지 않음.
  5. I/O 모듈이 I/O에 맞춘 특별한 명령어 체계를 가진 프로세서 기능을 수행
  6. I/O 모듈 내에 자체적인 메모리를 가지는 등 일종의 프로세서가 됨.

I/O 채널 특성

 I/O 채널은 DMA 콘셉트의 확장으로, 각각의 채널이 독립적인 I/O 명령을 수행할 수 있다. I/O 채널 자체가 일종의 특수 목적 프로세서로, CPU 대신 관련된 명령을 처리하므로 CPU는 I/O 채널에게 메모리 상에 저장된 I/O 명령을 수행하라고 지시함으로써 I/O 작업을 진행할 수 있다.

두가지 타입의 채널

  • Selector Channel: 다수의 디바이스 중 하나를 선택하여 데이터 전송을 수행한다.
  • Multiplexer Channel: 다수의 장치에 대한 I/O을 동시에 처리한다. A1 B1 C1, A2 B2 C2, A3 B3 C3... 와 같이  데이터 스트림을 일정 단위로 나눠 계속 전송하는 방법.

External Interconnection

 과거에는 버스 형태의 연결 구조가 일반적이었으나, 버스 기반 아키텍처에는 필연적으로 중재 과정 등이 필요하여 속도가 느린 단점이 있었다. 따라서 최근의 연결 구조는 대부분 Point to Point 형식을 가져 각 기기들 사이를 직접 연결한다.

 I/O 모듈 사이의 연결 구조는 아래 둘 중 하나에 속한다.

  • Point-to-Point: I/O 인터페이스와 외부 기기 사이의 연결이 1대 1로 이루어진다.
  • Multiport: 다양한 외부 기기들에 대한 연결을 지원하는데 사용된다.

USB

다양한 USB 규격(좌) 및 USB-C 타입(우)

 개인용 컴퓨터에서 주변 장치들의 연결을 표준화하기 위해 등장하였으며, 데이터 전송 및 충전 기능을 가진다. 기존에 있던 다양한 (직렬 및 병렬) 포트를 대체했으며, 사용되는 범위도 매우 다양하다. 현재 개인용 컴퓨터 수준에서는 기존에 있던 대부분의 포트를 대체할 수 있다. 

https://terms.naver.com/entry.naver?docId=3571649&cid=59088&categoryId=59096 

 

USB

1990년대 이전 까지만 하더라도 컴퓨터와 주변기기를 연결할 때 사용하는 인터페이스(포트나 케이블)의 종류가 매우 다양했고 연결해서 사용하는 방법도 각각이었다. 때문에 컴퓨터에 대한 전문

terms.naver.com

FireWire Serial Bus

 과거에 많이 사용하던 SCSI(small computer system interface)을 대체하기 위해 등장한 인터페이스로, 다양한 용도로 사용될 수 있다고 한다. 슈퍼컴퓨터나 메인 프레임 시스템을 위한 크고 비용이 많이 드는 I/O 채널 기술을 피하고, 높은 I/O 속도를 위해 개발되었으며, IEEE 1394 표준으로 등록되어 있다.

  •  Daisy chain을 통해 하나의 포트에 63 기기까지 연결 가능
  • hot plugging이 가능
  • SCSI와는 달리 configuration이 자동

이긴 한데, 현재 USB에 밀려 개인용 컴퓨터 수준에서는 사용하는 기기가 보기 힘들다. 

Fire Wire 규격

USB vs FireWire

USB

  • 버스 마스터(보통 컴퓨터)가 필요하다. 버스 마스터는 USB slave의 Point to Point 연결을 관리한다.
  • 구조가 간단해서 상대적으로 저렴하다.

FireWire

  • Peer-to-Peer network을 형성, 버스에 다양한 기기들이 연결될 수 있다.
  • 데이터 버스가 2개라 Full Duplex 데이터 송신을 지원한다.

SCSI

Small Computer System Interface의 약자로, 컴퓨터와 주변기기 연결에 사용되는 인터페이스 표준이다. SCSI 컨트롤러의 경우 CPU와 독립적으로 동작하므로 USB와는 달리 CPU 점유율이 낮다.

 과거에는 일종의 표준으로 사용되었기 때문에 현재도 서버와 같은 영역에서는 SCSI 및 SCSI의 후속 격의 인터페이스를 주로 사용하나, 개인용 컴퓨터와 같은 영역에서는 USB에게 완전히 밀렸다.

 Parallel Transmission + shared bus의 특징을 지녔었다.

  서버 하드디스크에 SCSI가 많이 사용되었으나, 점점 SATA 기반으로 대체되었다. 현재 SCSI의 후속 인터페이스인 SAS기반의 하드디스크가 가장 빠르나, 생산량과 용량이 SATA에 비해 밀린다. SATA의 큰 용량 및 SAS의 신뢰성 높은 인터페이스라는 장점을 누리기 위해 NL-SAS라는 일종의 어댑터 인터페이스도 생겼다.

Modern SCSI

  • SAS(Serial Attached SCSI): 연결 방식을 Serial한 point to point 링크로 변환한 인터페이스. 
    병렬 인터페이스에서는 고속으로 데이터를 멀리 보내면 클럭이 왜곡되는 현상(clock skew issue)이 있어서 이를 해결하면서도 속도를 높이기 위해 직렬 연결 방식으로 바꿨다.
    높은 데이터 전송율, 긴 전송거리 등 다양한 영역에서의 장점이 있다고 한다. 
  • FCP(SCSI-over-Fibre Channel Protocol): 광채널로 SCSI 데이터 보내는 방식. 현재 막 사용되지는 않음.
  • iSCSI: IP(Internet Protocol) 기반 데이터 스토리지 연결 표준. 
    과거에 SCSI을 서버에서 많이 사용했는데, 이때 사용했던 SCSI 명령 등을 적용해서 원격의 서버 장치를 관리할 수 있는 네트워킹 표준이다. SCSI 커맨드가 서버에 사용되다 보니 일종의 표준이 된 셈이다.