View

키보드로 입력받는 방법을 소개할 것이고, 그 방법의 핵심인 키보드 디바이스 드라이버를 이야기할 것이다.

 

컴퓨터 구조 시간에 배웠던 내용이다. CPU가 키보드 및 프린터와 상호 작용할 때, 각각의 제어기가 존재하고, 제어기에는 상태 레지스터와 데이터 레지스터가 존재한다는 것이었다. 그리고, CPU에서 해당 제어기로부터 데이터를 주고 받음으로써 키보드 제어기와 프린터 제어기의 레지스터의 값을 확인/변경하는 것이었다.

여기서 중요한 것은 CPU는 키보드와 프린터랑 직접 데이터를 주고 받는 것이 아니라, 제어기라는 중간 매체가 존재하여, 이를 통해, 데이터를 전달한다는 것이다.

 

여기서 키보드 제어기와 프린터 제어기를 I/O 장치 제어기로 한 번에 이야기할 수 있겠다. I/O 장치 제어기는 CPU로부터 명령을 받아서, 해당 I/O 장치를 제어하고 데이터를 이동함으로써, 명령을 수행하는 전자회로 장치이다.

상태 레지스터는 I/O 장치의 현재 상태를 나타내는 비트들을 저장하고 있는 레지스터이다. 키보드 장치의 준비 상태 비트(RDY), 키보드로부터의 데이터 전송확인 비트(ACK) 등이 이곳에 담긴다. 키보드로부터의 입력을 가져올 때 가장 중요한 것은 Output Buffer State와 Input Buffer State로, 각각 출력 버퍼에 키보드가 보낸 데이터가 남아있는지, 입력 버퍼에 프로세서(CPU)가 쓴 데이터가 남아있는지 여부를 판단한다.

데이터 레지스터는 CPU와 I/O 장치 간에 전달되는 데이터를 일시적으로 저장하는 레지스터이다. CPU가 키보드 컨트롤러에 데이터를 보내는 일은 여러 가지가 있지만, 그중 하나는 키보드 컨트롤러와 키보드를 초기화(활성화)할 때, 입력 버퍼로 데이터(커맨드)를 보낸다. 여기서 중요한 것은 키보드 컨트롤러를 활성화하였더라도 키보드가 아직 활성화된 것이 아니기에, 키보드 또한 활성화시켜주어야 한다는 것이다.

 

키보드를 누를 때, 그리고 뗄 때 동시에 인식된다.

즉, 두 경우 모두 키보드에서 CPU로 어떠한 데이터를 보낸다는 것이다.

 

64비트 멀티코어 OS 원리와 구조 中

이전에 보여준 사진보다 훨씬 더 자세하게 나온 모습이다. 키보드 컨트롤러에는 상태 레지스터와 데이터 레지스터(입력 버퍼와 출력 버퍼) 말고도 컨트롤 레지스터가 추가로 존재한다. 그리고 화살표에 쓰인 것은 포트를 의미하는 것으로, 이렇게 입출력 전용 주소를 할당하여 해당 포트 주소에 접근하는 것을 포트 맵 I/O 방식이라고 한다. 이와는 달리, 물리 메모리 주소에 I/O 용도의 공간을 할당하는 방식도 있는데, 이는 메모리 맵 I/O 방식이라고 부른다. 언젠가 자세히 볼 날이 있기를 기원한다 ㅋㅋ..

 

참고로, PS/2 케이블은 위 사진의 케이블을 의미한다. 내가 어렸을 적에 마우스와 키보드가 이런 케이블을 사용했던 것으로 기억한다.

 

키보드가 '딸', '깍'할 때 전달되는 어떠한 데이터는 바로 '스캔 코드'이다. Down Code는 키가 눌렸을 때, Up Code는 키가 떼어졌을 때 각각 전달되는 데이터를 의미한다. 그리고 위는 스캔 코드 테이블인데 보면 일종의 규칙이 존재한다. 먼저, 일반적인 키들은 Down Code에 0x80을 더하면(최상위 비트를 세팅하면), Up Code가 된다. 그리고, 특별한 키들은 0xE0과 0xE1로 시작하며, 0xE0이 입력되었다면 그 이후의 Down Code를 기준으로 어떠한 키인지 판별하면 될 것이다. 마지막으로, 0xE1이 입력되었다면 이는 Pause가 입력된 것이므로, 나머지 2개의 스캔 코드는 무시해도 된다.

 

그렇다면, 스캔 코드가 우리가 인식할 수 있는 아스키 코드로 치환되는 과정은 어떻게 될까?

먼저, 상태 레지스터의 0번째 비트를 확인하여, 키보드가 보낸 스캔 코드가 키보드 컨트롤러의 출력 버퍼에 있는지 확인한다. 만약 존재한다면, 해당 스캔 코드를 CPU로 가져온다. 이후에, Shift와 Caps Lock, 그리고 Num Lock 여부를 확인하여 스캔 코드를 적절한 아스키 코드로 치환한다.

 

스캔 코드를 쉽게 아스키 코드로 바꿀 수 있도록, 스캔 코드를 입력으로 넣으면 아스키 코드를 반환하는 테이블을 만들었다. 첫째 칸에 있는 키는 일반적인 키이고, 둘째 칸에 있는 키는 조합 키이다. 만약 Shift가 눌린 상황이라면, 일반적인 키가 아니라, 조합 키로 반환하여야 할 것이다.

 

그리고, 위는 실제 CPU에서 수행하고 있는 작업이다. 반복적으로 상태 레지스터를 통해, 스캔 코드가 전달되었는지를 확인하고, 만약 존재한다면 스캔 코드를 아스키 코드로 전환하여 출력하는 것이다. 그런데, 이는 CPU 낭비가 너무 심하다. 키보드가 입력을 처리하는 시간보다 키보드의 입력을 기다리는 시간이 훨씬 더 길기 때문이다. CPU의 속도가 키보드 장치의 속도보다 훨씬 더 빠르기 때문이다.

그래서, 무작정 CPU가 이렇게 반복적으로 돌면서, 키보드의 입력을 기다리는 것은 좋지 않다. 이를 해결하기 위한, 하나의 방법으로는 키보드 입력이 수행되었을 때, 인터럽트(외부 장치에서 발생하는 이벤트)를 CPU에 전달하여, 해당 작업을 수행하도록 만드는 것이 좋을 것이다.

그래야 CPU가 다른 작업을 수행하다가, 키보드의 입력이 발생하였을 때, 이를 핸들링하고 다시 본래의 작업을 수행할 수 있을 것이기 때문이다. 이러한 방식으로 구현하였을 때, CPU의 유휴 시간이 사라지고, 단위 시간 내에 더 많이 CPU를 이용할 수 있게 된다.

 

추후 계획은 콘솔 쉘 구현인데.. 얼른 해야 겠다 ㅠㅠ. 지금은 QEMU 구버전 빌드가 안 돼서, 저자의 블로그에 올려져 있는 윈도우용 구버전 QEMU를, 우분투 리눅스 WINE을 통해 실행하고자 한다.

만약 이게 실패한다면, 그냥 호스트와 게스트 VM 사이에 공유 디렉토리 만들어두고, VS CODE로 코딩하는 방법도 있겠다. 적어놓고 보니 이것도 나쁘지 않은 것 같다..!

 

좋은 책 감사합니다!

ლ(╹◡╹ლ)

'OS' 카테고리의 다른 글

매삼햌 - 운영 모드와 메모리 관리 기법 (2022.03.19.)  (0) 2022.05.05
Share Link
reply
«   2025/07   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31