본문 바로가기
학부공부/OS_운영체제

09. 운영체제와 컴퓨터 구조(2)

by sonpang 2021. 11. 8.
반응형

안녕하세요. 이번 글은

2021.11.08 - [학부공부/운영체제] - 08. 운영체제와 컴퓨터 구조(1)

 

08. 운영체제와 컴퓨터 구조(1)

안녕하세요. 이번 글은 컴퓨터 구조에서 배웠던 내용 중 운영체제 과목을 이해하는 데 도움이 되는 토픽들을 정리해보는 시간을 가지겠습니다. 다룰 Content부터 소개하겠습니다. Contetns Bus Bottlene

ku320121.tistory.com

포스팅에 이어 컴퓨터 구조 과목에서 배웠던 내용 중 운영체제 과목을 이해하는 데 도움이 되는 토픽들을 마저 정리해보는 시간을 가지겠습니다.

 

지난 포스팅에서는 인터럽트와 트랩에 대해서 소개하였습니다. Divieded by zero와 같은 경우 더 이상 프로그램이 진행 할 수 없습니다. 또는 invalid pointer에 접근하는 경우도 마찬가지겠죠. 즉, 프로그램이 terminate되어야 하는데 trap은 이런 동기적인 이벤트에 대해 트랩 핸들러가 처리한다고 설명하였습니다. 오늘은 I/O에 대해서 이야기 할 계획인데요.

2021.11.01 - [학부공부/운영체제] - 03. 운영체제 구조(1)

 

03. 운영체제 구조(1)

안녕하세요. 이번 글은 운영체제 구조에 관한 글입니다. 이번 글부터는 Content부터 소개를 좀 하고 들어가겠습니다. 지난번 작성한  2021.10.24 - [학부공부/운영체제] - 02. 운영체제 역사 02. 운영체

ku320121.tistory.com

포스팅에서 HAL에 대해서 언급한적 있습니다. HAL같은 것을 두어 임의의 하드웨어 디바이스가 커널과 접점을 만드는데요. 디바이스 드라이버는 인터럽트가 어떻게 발생하는 지 등이 디바이스 specific하기 때문에 하드웨어 제작 회사에서 제공합니다.(HAL은 HAL에 디바이스를 붙일 때 디바이스 드라이버 버그를 테스트하는 용도로도 사용하죠.)

 

 

 

09.01. I/O device model

디바이스의 종류와 기능이 다양하기 때문에 I/O device를 보델링하기는 상당히 까다롭습니다. 그에 대한 답으로 하드웨어 장치를 제어하는 controller가 있는데요. Controller는 대부분 4종류의 register를 가집니다.

 

Control register

Status resgister

Input resgister

Output register

 

register들은 메인 메모리의 영역에 매핑을 하고 그 주소를 통해 CPU가 접근하게 됩니다. I/O에 쓴다고 한다면 control register에 쓰라고 명령하고 Output register에 답을 넣어주겠죠. 커널이 control register에 명령어를 주고 명령에 따라 controller가 수행하는 것이라고 생각하면 됩니다. 결국

 

I/O = CPU가 레지스터에 읽고 쓰는 동작

 

으로 모델링 될 수 있음을 의미합니다.

 

 

09.02. Polling_I/O 처리 기법

Polling은 loop안에서 특정 이벤트의 도착 여부를 계속 확인하는 방법입니다. 인터럽트는 이벤트가 발생하면 알려주는 개념이고 폴링은 발생여부를 지속적으로 확인한다는 점에서 반대되는 개념이라고 할 수 있습니다. 확인한다는 동작 자체가 결국 CPU Cycle을 사용한다는 것이므로 장치가 매우 빠른 경우 한하여 폴링이 이벤트 처리 기법으로 적당하다고 할 수 있습니다. 만약 이벤트 도착 시간의 간격이 길 경우 폴링은 CPU time을 낭비한다고 볼 수 있죠. 그렇다면 장치가 빠른 경우 폴링이 인터럽트에 비해 CPU측면에서 나쁘지 않다는 이유는 무엇일까요?

앞서 소개해드린 지난 포스팅을 참고하시기 바랍니다. 인터럽트는 state를 저장해야 합니다. 소프트웨어적으로 하나씩 저장할 수도 있고 하드웨어적으로 한번에 저장할 수도(하드웨어적으로 state를 한번에 저장하는 instruction이 있으면 좋을 것입니다.) 있겠지만 무튼 오버헤드는 발생합니다. DoS 공격도 결국 이러한 오버헤드를 공략한 것이라고 볼 수 있습니다. 폴링은 그런 오버헤드가 없습니다.

 

CPU가 I/O를 위한 데이터 이동을 담당하는데요. 폴링 뒤에 PIO(programmed I/O)가 실행됩니다. 

 

프로그램 입출력(Programmed Input/Output, 줄여서 PIO)은 네트워크 어댑터나 ATA 기억 장치와 같은 주변 기기와 중앙 처리 장치 사이에서 데이터를 주고받는 방식이다. 이 PIO의 단점을 극복하기 위해 인터럽트, DMA등이 고안되었다.

 

 

09.03. DMA(Direct Memory Access)_I/O 처리 기법

폴링을 사용할 경우 모든 동작은 CPU에 의해 진행됩니다. 결국 CPU clock을 사용하는 것이죠.(PIO역시 데이터 복사 등을 해야하기 때문에 CPU clock을 사용한다고 볼 수 있습니다.) 그렇다면 느린 장치에 적합한 기법이 있어야겠죠? 그것이 바로 DMA입니다. DMA는 CPU를 장치의 상태 확인 및 데이터 이동에 사용하지 않게 사용하는 별도의 장치라고 생각하시면 됩니다. 그리고 DMA라는 별도의 장치를 사용하기 때문에 I/O를 호출한 프로세스는 sleep하게 되죠. 

 

정확하게는 DMA가 방식을 의미하기 때문에 구동장치를 표현하기 위해 DMA engine이라고 표현하는 특수목적 프로세스가 있습니다. CPU가 DMA에게 control register를 통해 I/O를 요청하면 DMA는 CPU 대신 I/O장치와 메모리 사이 데이터 전송을 수행하고 CPU는 I/O가 진행되는 동안 다른 일을 수행(멀티 프로그래밍이나 time sharing에 의해서 수행하겠죠.)하는데요. DMA가 동작을 마치면 CPU에 인터럽트를 발생시킵니다. 이때 인터럽트가 발생하면 스케쥴러가 어떻게 할지 결정하게 됩니다.

 

(이것은 매우매우 중요한 내용입니다. 우리는 항상 복잡한 시스템을 모델링할때 이렇게 각 모듈이 할 일을 정확히 정해놓죠. 나중에 동기화 이슈에서도 다루겠지만 스케쥴링은 인터럽트, DMA 이런 이슈와는 독립적으로 동작할 수 있어야 합니다. 하나의 component가 다른 component의 결정을 하지 않게 하는 것은 아주 중요하죠. 즉 스케쥴링 결정은 스케쥴러가 하는 것입니다. 한 문단을 통으로 굵음 처리하는 것은 이번이 처음이네요. 그만큼 중요한 사고방식입니다.)

 

 

DMA는 Bus stealing할 수 있다는 것이 중요한 특징 중 하나입니다. Bus에는 여러 디바이스가 물려있지만 데이터를 동시에 실진 못합니다. 그래서 메모리는 bus transaction을 initiate하지 못합니다. 요청이 들어오면 그냥 데이터를 토해내는(?) 역할만 할 뿐이죠. 원래 CPU만이 bus transaction을 할 수 있었는데 이젠 DMA도 할 수 있게 된 것입니다. 그럼 당연히 우선순위가 있겠죠. 짐작하셨다시피 CPU의 우선순위가 높습니다. 

[그림 1] DMA read

참고자료에 아주 좋은 그림이 있어 가져왔습니다. Step 1은 Control register에 넘겨주는 것입니다. Step 1, 2에서 해당하는 프로세스는 sleep할 것입니다. Step 4에서 읽어오는 Byte 단위는 PCI Bus의 size에 따라 달라집니다. Step 5에서는 읽어온 값을 메모리로 올려보냅니다. 

 

 

09.04. Polling vs DMA

그렇다면 최종적으로 폴링과 DMA를 비교할 시간입니다. 폴링은 빠른 장치에 적합하다 하였고 DMA는 느린 장치에 필요하다 하였습니다. DMA는 CPU가 동작하는 사이에 I/O를 가능하게 한다는 점은 장점이지만 추가적인 하드웨어(cost)가 필요하다는 것은 단점이죠. 그래서 적당한 parallelism이 필요합니다. 예를 들면 휴대폰의 카메라 픽셀을 읽어 올 때 DMA가 필요할까요?

 

옛날 같으면 DMA를 사용할 필요가 없었을 것입니다. 하지만 지금은 카톡, 게임, 음악 등 많은 기능을 동시에 수행해야하죠. CPU가 할 일이 산더미처럼 쌓여 있습니다. 그래서 DMA있어 I/O를 대신 처리해주면 아주 좋을 것입니다.(DMA가 추구하는 것은 비용을 더 지불하면서 parallelism을 지원하는 것이죠)

 

 

09.05. I/O instruction_I/O device access

I/O를 하기위해 I/O장치의 레지스터를 사용한다고 하였는데 이때 사용하는 2가지 방식에 대해 알아보겠습니다. CPU는 I/O instruction을 통해 레지스터의 값을 읽고 씀으로써 장치와 통신할 수 있습니다. 물론 커널모드에서 실행되죠.

 

 

09.06. Memory Mapped I/O_I/O device access

또 다른 방법으로는 레지스터들을 메모리 공간에 매핑하여 사용하는 방법이 있습니다. 레지스터들을 주소 공간의 일부로 접근하는 것이죠. 이렇게 한다면 컴퓨터 구조 과목에서도 배웠던 MOV, LOAD, STORE와 같은 아주 일반적인 명령어를 사용하여 작업을 수행할 수 있습니다. 

 

 

09.06. I/O instruction vs Memory Mapped I/O

I/O 장치를 접근하는 2가지 방법에 대해 알아보았습니다. CPU마다 다르고 둘 다 지원하는 경우도 있는데요. ARM, MIPS는 Memory Mapped I/O, Intel 계열은 I/O instruction이 사용됩니다.(ARM 어셈블러를 작성해보신 분들이 계시다면 실제로 메모리 주소를 pointer로 잡아서 값에 접근하는지 댓글 남겨주시면 감사하겠습니다.)

 

Memory Mapped I/O와 같은 경우는 메모리 영역에 매핑하기 때문에 그만큼 사용할 수 있는 메모리 용량은 감소한다는 것이 단점입니다. 하지만 입출력을 구현할 때 기존 instruction을 사용할 수 있어 CPU 내부 로직이 단순하다는 것이 큰 장점입니다. 이는 저렴하고 빠르고 단순한 CPU를 만들기에 유리하죠.(RISC : Reduced Instruction Set Computer에 적합한 방법일 것입니다.) 추가적으로 고려해야 할 이슈가 있긴합니다. 레지스터가 단순한 값을 저장하는 것이 아니라 입출력 장치를 제어하는 역할을 하기 때문에 compiler의 최적화가 이루어지면 안되는 영역이라는 것을 고려해야 하죠.

 

I/O instruction는 별도의 주소 영역을 사용하기 때문에 메모리 용량은 감소하지 않습니다. 하지만 CPU가 메모리와 I/O를 구분하기 때문에 I/O에 접근하기 위한 별도의 명령어가 필요합니다. 위에서 Memory Mapped I/O가 고려할 이슈로 Addressing에 대해서 이야기 하였는데요. 이러한 Addressing 능력이 부족하거나 제한된 CPU에 사용할 수 있는 방법입니다. 어셈블리어 수준에서도 code를 볼 때 입출력 수행 routine을 알아보기 쉽죠.

 

 

이번 포스팅에선 컴퓨터 구조 과목에서 배웠던 I/O와 관련한 내용을 마지막으로 정리하는 시간을 가져보았습니다. 더 자세한 내용은 컴퓨터 구조 내용을 포스팅 할 기회가 생기면 소개하겠습니다. 긴 글 읽어주셔서 감사하고 댓글 많이 남겨주시면 더욱 감사하겠습니다.(잘못된 내용 지적이나 질문은 언제나 대환영입니다.) 항상 댓글 요청(?)으로 글을 마감하는 것 같네요...

 

  

반응형

댓글