3. PnP(Plug-and-Play) 해결책

3.1. PnP 에 대한 소개

PnP(Plug-and-Play) 라는 용어는 다양한 의미를 가지고 있다. 넓은 의미에서는 단순히 디바이스를 꼽기만 하면 스스로 자신을 설정하는 자동 설정기능을 가리킨다. 본 HOWTO 에서 사용하는 PnP 의 의미는 PnP 버스 리소스를 설정하고 디바이스 드라이버에게 그 설정 내용을 알리는 것만을 의미한다. 좀 더 좁은 의미로는 하드웨어 디바이스상에 버스 리소스를 셋팅하는 것만을 가리킨다. 리눅스의 경우에 한해 말한다면, PnP 는 대개는 그저 바이오스 등등에 의해 디바이스에 세팅된 버스 리소스의 내용을 드라이버가 감지해내는 것을 뜻한다. PnP 는 그저 PnP 규격명세를 뜻할수도 있다. ISA 버스 리소스에 관한 PnP 규격명세는 상당히 길다. 표준 PCI 규격명세(이것을 PnP 라고 부르지는 않는다)는 PnP 에 해당하는 기능을 PCI 버스상에서도 구현하고 있다.

PnP 는 디바이스들을 해당 디바이스 드라이버들에 결부시키고 양측의 통신 채널을 특정한다. PnP 가 등장하기 전의 ISA 버스에서는 점퍼를 이용해 사전에 미리 버스 리소스들을 하드웨어 디바이스상에 셋팅시켰었다. 소프트웨어 드라이버들은 설정화일(또는 그와 유사한 것)에 의해 버스 리소스들을 지정받거나, 아니면 특정 디바이스가 있을법한 어드레스들을 조사해보는 방법을 통해 버스 리소스를 지정받았다. PCI 버스는 애초부터 PnP 와 유사한 기능을 내재하고 있었으나 처음에는 이를 PnP 라 부르지는 않았다. PCI 버스 명세서에서는 PnP 라는 용어를 사용하지는 않지만 오늘날 PnP 라고 부르는 기능을 하드웨어적으로 지원하고 있다.

3.2. PnP 의 동작원리에 대한 간략한 설명

여기서는 PnP 동작원리의 개요를 아주 간략하게 설명하겠다. PnP 설정 프로그램(리눅스에서는 BIOS 만이 이 작업을 수행한다)은 모든 PnP 디바이스들을 검색한 후, 각 디바이스에게 어떤 버스 리소스를 원하는지를 묻는다. 그 다음, 분배해줄 버스 리소스들(IRQ 등등)이 무엇무엇인지를 체크한다. 물론, 이때 PnP 아닌 디바이스(Legacy 디바이스라고 한다)들이 사용하기로 예약된 버스 리소스들만큼은 (BIOS 가 그 버스 리소스들을 알고있는 한) 분배해주지 않아야한다. 그다음, 일정 기준(이 기준은 PnP 명세에 규정된 것은 아니다)에 의해 충돌이 없는 한도내에서 가능한한 각 디바이스들에게 원했던 버스리소스를 배정한다. 그다음, 각각의 물리적 디바이스들에게 어떤 버스 리소스가 배정되었는지를 알려주고, 이에 디바이스들은 그 버스 리소스대로 자신을 셋팅한다. 이렇게 되면, 이제 디바이스 드라이버들은 적당한 방법을 통해 자신의 디바이스가 사용하는 버스 리소스를 알아낸 후, 그를 통해 자신이 제어하는 디바이스와 통신할 수 있게 되는 것이다.

예를 들어 어떤 카드가 인터럽트(IRQ 번호) 하나와 1 MB 의 공유메모리를 필요로한다고 치자. PnP 프로그램은 그 카드로부터 이 요구사항을 읽어본 후, 그 카드에 IRQ 5 와 어드레스 0xe9000000 부터의 1 MB 메모리 어드레스 공간을 할당해준다. 그런데 일이 항상 이렇게 순조로운 것만은 아니다. 예를 들어 그 카드가 자기는 반드시 특정한 IRQ 번호만 써야한다고 주장하거나(ISA 경우에만 이런 일이 생길수 있다), 그 1 MB 메모리는 반드시 어떤 특정한 메모리 어드레스 범위내일 것을 주장하는 수가 있다. 자세한 사항은 PCI 버스냐 ISA 버스냐에 따라 각각 다른데, ISA 경우가 더 복잡하다.

이 경우 PnP 설정 프로그램이 취할수 있는 간편한 방법이 몇가지 있다. 그 하나는 컴퓨터를 마지막으로 사용했을 때의 최종적인 버스 리소스 설정상태를 기록해 두었다가 그것을 그대로 재사용하는 것이다. 윈도우즈 9X 와 PnP BIOS 들은 이 방법을 사용하지만 표준 리눅스는 이 방법을 사용하지 않는다. 윈도우즈 9x는 이 설정상태 정보를 자신의 하드디스크상의 "레지스트리"에 기록해 두며, PnP BIOS 는 이 정보를 PC 내의 비휘발성 메모리(ESCD 라고 부른다. 5.3.2절 를 보라)에 기록해 둔다.

MS 윈도우즈는 (윈도우즈 95 부터) PnP OS 인 반면, 리눅스는 PnP OS 가 아니다. 하지만 리눅스에 있어서도 BIOS 가 버스 리소스 설정을 해주고 디바이스 드라이버들이 (리눅스 커널이 제공하는 프로그램을 써서) BIOS 가 설정한 그 내용을 알아내게되므로 PnP 가 다소 역할을 한다고 할수있다. 또한, 드라이버들은 그런 프로그램들을 이용해 지정된 버스 리소스 값을 변경시켜버릴수도 있다(하지만 이때 다른 디바이스용의 버스 리소스를 침해할지도 모른다). 어떤 디바이스 드라이버들은 자신이 최종 사용한 설정내용을 저장했다가 다음번 컴퓨터 사용시에 그를 사용하기도 한다.

디바이스 하드웨어가 자신의 지난번 설정을 기억하고 있다면야 다음 부팅때에 하드웨어의 리소스 설정을 두고 고민할 필요가 전혀 없겠지만, 하드웨어들은 보통 전원이 꺼짐과 동시에 자신의 설정상태를 잊어버리게 된다. 일부 디바이스들은 디폴트 설정을 가지고 있을수도 있다(하지만 그 디폴트 설정상태가 지난번 최종사용시의 설정상태와 같으란 법도 없다). 따라서 PnP 설정 프로그램은 PC 를 켤때마다 실행되어야 한다. 또한, 만일 새로운 디바이스를 추가한다면 그 디바이스 역시 설정될 필요가 있다. 이 새로운 디바이스에 버스 리소스를 할당하는 작업은 어쩌면 현재의 디바이스로부터 버스리소스 일부를 빼앗아 새 디바이스에 주고 그대신 원래의 디바이스에게는 다른 버스 리소스를 할당해주는 작업이 될 수도 있다. 현재로서는 리눅스는 이런 작업은 할수 없다.

3.3. PC 의 기동

PC 를 처음 켜면, BIOS 칩은 컴퓨터를 시작시키기 위해서 BIOS 프로그램을 실행한다(첫 단계는 하드웨어의 체크이다). 운영체제가 하드 디스크에 저장되어 있는 경우(보통은 그렇다)라면, BIOS는 먼저 그 하드 디스크에 대한 사항을 알아야만 한다. 하드 디스크가 PnP 기기라면, BIOS 는 이를 찾아내기 위해 PnP 방법을 사용할 수도 있다. 또한, 컴퓨터 시작시에 사용자가 BIOS 의 CMOS 내용을 수동으로 설정할 수 있도록 하거나 컴퓨터 기동시의 에러 메세지에 대응할 수 있도록 하기 위해서는, 스크린(비디오 카드)와 키보드도 필요하다. 따라서, BIOS 는 이들 디바이스들만큼은 언제나 PnP 설정시켜야 하는 것이다.

일단 BIOS가 하드 디스크, 비디오 카드, 키보드를 인식하면, 이제 부팅(하드 디스크로부터 메모리로 운영체제를 로드하는 것)개시 준비는 완료된 것이다. 만일 BIOS 설정메뉴에서 PnP 운영체제(PnP OS)를 사용하는것으로 설정했다면, 앞서 말한 바대로 BIOS 가 PC의 부팅을 개시한 후, 이제 운영체제가 나머지 PnP 설정작업을 마무리짓게 된다. BIOS 를 PnP OS 사용않음으로 설정했다면 운영체제의 부팅에 앞서 PnP-BIOS 자신이 디바이스들의 PnP 설정을 끝까지 마무리지으려 할 것이다(하지만 BIOS 로서는 드라이버들에게 그 설정내용을 직접 알려주지는 못한다). 이상이 리눅스 기동시에 일반적으로 발생하는 일들이다.

3.4. 버스

ISA 는 구형 IBM PC 에서 사용하던 옛날 버스인 반면, PCI 는 인텔이 제안한 새롭고 빠른 버스이다. PCI 버스는 현재 우리가 PnP 라고 부르는 기능을 실현할 수 있도록 설계되었다. ISA 버스에 비해 PCI 버스에서는 하드웨어 디바이스들에 PnP 버스 리소스들이 어떻게 할당되었는지를 알아내기가 용이하다. 설정상태를 알아보려면 lspciscanpci(X 윈도우용 명령어) 명령을 사용하거나, /proc/pci 또는 /proc/bus/pci 파일을 보면 된다. 부팅시에 화면에 표시되는 메시지(지나간 메시지를 보기위해서는 Shift-PageUp 을 사용한다)도 유용하다. 7.1절를 참조하라.

ISA 버스의 경우에는 PnP 를 구현하는데 있어 정말이지 어려운 문제가 있었다. 이러한 사태가 벌어진 이유는 ISA 버스를 설계할 당시에는 누구도 PnP를 고려하지 않았다는 점과, PnP 가 설정정보를 물리적 디바이스로 보내는데 사용할 수 있는 I/O 어드레스 영역이 거의 없다는 점 때문이다. 결국, ISA 버스상에서 PnP 를 구현하는 방법은 매우 복잡한 것이 되었다. 이 주제에 대한 책이 몇권 있다. 5.8절 를 참고하라. 무엇보다도, ISA 버스에서의 PnP 구현시에는 각각의 PnP 디바이스는 PnP 프로그램에 의해 일시적인 "핸들(handle)"을 할당받아야만 한다. 그래야 이를 통해 그 디바이스에 PnP 설정을 위한 어드레스를 줄수있게 된다. 이 "핸들" 들을 할당하는 것을 "Isolation" 이라고 한다. 보다 복잡한 세부내용은 9.6절을 참조하라.

결국 ISA 버스는 언젠가 사라지게 될것이다. 그렇게되면 하드웨어를 BIOS 가 어떻게 설정했는지를 간단히 알아낼수 있게되므로 PnP 는 훨씬 쉬워질 것이다. 하지만 그래도 디바이스 드라이버와 디바이스를 일치시키는 작업과 PC 의 시작 및 실행시에 새로 추가된 디바이스를 설정하는 작업만큼은 언제까지나 필요할 것이다. 이러한 작업들은 리눅스가 PnP 운영체제가 된다면 보다 간단히 완료될수 있을 것이다.

3.5. 리눅스가 PnP 를 구현하는 방법

리눅스는 지금껏 PnP 를 다루는데 있어 심각한 문제점을 가지고 있었고 아직도 문제를 안고는 있다. 하지만 더이상 과거처럼 치명적인 문제는 아니다. 리눅스는 아직도 진정한 PnP 운영체제는 아니며, 디바이스용 버스리소스들을 설정하기위해 디바이스 드라이버들과 PnP BIOS 에 주로 의존한다고 볼수있다. 하지만 커널은 드라이버들의 요청이 있을 경우 PnP 프로그램을 제공하는 형식으로 드라이버들을 도와준다. 많은 경우, 필요한 설정작업은 디바이스 드라이버가 전부 해낸다. 그밖의 경우에는, BIOS 가 설정작업을 담당한 다음, 디바이스 드라이버는 바이오스가 설정한 내용을 찾아보는 방법을 사용할수도 있다. 커널은 드라이버에게 디바이스의 존재여부 확인, 설정상태 확인, 설정상태 변경 등의 몇가지 함수들(프로그램 코드이다)을 제공할 수도 있다. 커널 2.2 에서는 PCI 버스에 대해서만 이 기능이 있었으나, 커널 2.4 부터는 ISA 버스와 PCI 버스 둘다에 대해서도 이 기능이 가능하다(단, 커널 컴파일시에 해당하는 PNP 옵션들이 선택되어야 가능하다). 그렇지만 커널이 이런 기능을 가지고 있다고해서 모든 드라이버들이 이 기능을 정확하게, 또 완전히 활용하고 있다는 뜻은 아니다.

게다가, 커널은 두 디바이스들이 동시에 같은 버스 리소스를 사용하는 것을 허용치 않음으로써 리소스의 충돌이 일어나지 않게 도와준다. 원래 이것은 IRQ 들 간의 충돌이나 DMA 들 간의 충돌만을 방지하기위한 기법이었으나 이제는 어드레스 리소스들 간의 충돌을 방지하기 위해서도 사용되고있다.

커널 2.4 이전에는 독자적인 프로그램인 isapnp 를 이용해 ISA 버스상의 PnP 디바이스를 설정을 하거나 그 정보를 얻어내는 수가 많았다. 아직도 isapnp 는 ISA 버스상의 PnP 구현이 불완전한 디바이스 드라이버에게는 필요하다. 리눅스를 진짜 PnP 운영체제로 만들려는 시도가 적어도 한번은 있었다. 를 참고하라. 하지만 이 시도가 커널에 포함된 적은 없었다.

커널이 디바이스 드라이버들에게 제공할수 있는 도움에는 어떤 것들이 있는가는 커널 문서를 보라. 이 문서는 (당신이 가지고 있다면) /usr/.../.../Documentation 디렉토리에 있다. 앞의 ... 기호중에는 "kernel" 이라는 단어가 포함되어있다. "locate" 명령으로 찾을수 있다. 이 문서 디렉토리내의 pci.txt ("How to Write Linux PCI Drivers") 파일과 /usr/include/linux/pci.h 파일을 보라. 이 파일들은 상당히 간결하기 때문에 당신이 드라이버에 대해 도사 수준이 아니고 C 프로그래밍도 모른다면 드라이버 작성법을 배우는 데는 사실상 도움이 되지 못할 것이다. 하지만 드라이버가 이용할 수 있는 PnP 용 함수들에 어떤 것들이 있는지 다소 알수 있을 것이다. ISA 버스에 대한 것은 isapnp.txt 문서와 커널 2.4 의 경우라면 /usr/include/linux/isapnp.h 도 도움이 될 것이다.

PC 가 기동될때 화면에 나타나는 메시지중에는 몇몇 리눅스 디바이스 드라이버들이 자신의 하드웨어 디바이스를 (그리고 BIOS 가 그에 할당시킨 버스리소스들도) 발견했다는 내용도 있을수 있다. 하지만 진정한 PnP 운영체제라면 다음의 상태들까지도 더 잘 처리할 수 있을 것이다.

"버스 리소스들의 부족" 문제는 다음 두가지 이유로 점차 문제거리가 안되어 가고있다 : 첫째, PCI 버스가 ISA 버스를 대체해가고 있다. PCI 에서는 IRQ 를 공유할 수 있기 때문에 비록 공유로 인해 효율이 떨어지기는 해도 IRQ 가 부족해지는 일은 없다. 또한, PCI 는 DMA 리소스를 사용하지 않는다(DMA 리소스 없이도 DMA 와 동등한 기능을 구현한다).

둘째, 점점 더 많은 물리적 디바이스들이 IO 어드레스 공간 대신 메인 메모리 어드레스 공간을 사용하는 쪽으로 가고있다. 32 비트 PC 들은 4GB 의 메모리 어드레스 공간을 쓸 수 있으므로, 당신 시스템이 4 GB 의 메인 메모리를 사용하지 않는 한, 이 메인 메모리 어드레스 버스 리소스의 상당 부분을 IO 디바이스로 사용할 수 있다. 이에 비해 IO 어드레스 공간은 겨우 64 KB 가 한계이다. 따라서 디바이스의 IO 용으로 쓸수 있는 메모리 공간은 (아직까지는?) 부족하지 않다.