7.3. 장치 및 모듈 핸들링 개요

6장에서 우리는, eudev가 빌드될 때 Udev 패키지를 설치했다. 이들의 작동 방식에 대한 자세한 내용을 살펴보기 전에, 과거의 장치 핸들링 방법의 역사에 대해 간단히 짚고 넘어가고자 한다.

일반적인 리눅스 시스템은 전통적으로 정적인 장치 생성 방법을 사용해서, 해당 하드웨어 장치가 실제로 존재하는지 여부에 관계없이 /dev 아래에 매우 많은 장치 노드(때로는 말 그대로 수천 개의 노드)가 만들어졌다. 이는 일반적으로 MAKEDEV 스크립트를 통해 이뤄졌는데, 이 스크립트는 세상에 존재할 수 있는 모든 장치에 대한 관련 주/부 장치 번호와 함께 mknod 프로그램에 대한 여러 호출들을 담고 있었다.

Udev를 사용하면 커널에 의해 탐지된 장치만 해당 장치 노드가 생성된다. 이런 장치 노드는 시스템이 부팅될 때마다 생성되기 때문에 devtmpfs 파일 시스템(완전히 시스템 메모리에 상주하는 가상 파일 시스템)에 저장된다. 장치 노드는 공간이 많이 필요하지 않기 때문에 사용되는 메모리는 무시할 수 있다.

7.3.1. 역사

2000년 2월, devfs라는 새로운 파일 시스템이 2.3.46 커널에 병합되어 2.4 안정 버전 커널에서 사용할 수 있게 되었다. 동적으로 장치를 만드는 이 방법은 비록 커널 소스에 존재는 했으나 핵심 커널 개발자들로부터 결코 지지를 받지 못했다.

devfs에서 채택된 접근 방식의 주요 문제는 장치 인식, 생성 및 명명 처리 방식이었다. 장치 노드 명명 문제인 후자가 아마도 가장 중요한 사안이었을 것이다. 장치 이름을 설정하는 것이 가능하다면, 장치 명명 정책은 특정 개발자가 아닌 시스템 관리자의 몫이어야 한다는 것이 보편적인 생각이다. 더구나 devfs 파일 시스템은 그 설계 구조상 커널에 대한 실질적인 수정 없이는 고칠 수 없는 교착 상태에 시달렸다. 이는 유지보수가 부실해지자 장기간 폐기된 것으로 표시되었고, 결국 2006년 6월 커널에서 제거되었다.

나중에 2.6 안정 버전으로 배포된 2.5 개발 버전 커널 트리가 개발되면서 sysfs라는 새로운 가상 파일 시스템이 생겼다. sysfs의 역할은 시스템 하드웨어 구성을 유저스페이스 프로세스(userspace processes)에서 볼 수 있도록 하는 것이다. 이 유저스페이스에서 볼 수 있도록 하는 표현 방식으로 인해, devfs에 대응하는 유저스페이스용 대체품 개발 가능성이 훨씬 더 높아졌다.

7.3.2. Udev 구현

7.3.2.1. Sysfs

sysfs 파일 시스템은 위에서 간략히 언급했다. 그렇다면 sysfs는 시스템에 어떤 장치가 존재하는지, 또 그 장치들에 어떤 장치 번호를 사용해야 하는지를 어떻게 알 수 있을까? 커널 내부로 컴파일된 드라이버는 커널에 의해 감지되는 대로 장치를 sysfs(내부적으론 devtmpfs)에 직접 등록한다. 모듈로 컴파일된 드라이버는 모듈이 로드될 때 등록된다. sysfs 파일 시스템이 마운트되고나면(/sys에), 드라이버가 sysfs에 등록한 데이터는 유저스페이스 프로세스와 udevd에서 사용 가능하여 장치 노드 수정을 포함한 처리에 쓰인다.

7.3.2.2. 장치 노드 생성

장치 파일은 커널에 의해 devtmpfs 파일 시스템으로 생성된다. 장치 노드를 등록하려는 모든 드라이버는 devtmpfs를 통해 (드라이버 코어에 의해) 작업을 수행한다. devtmpfs 인스턴스가 /dev에 마운트되면, 장치 노드가 고정된 이름과 권한, 소유자를 갖고 초기화되어 생성된다.

그리고나서, 커널은 uevent를 udevd로 보낸다. /etc/udev/rules.d, /lib/udev/rules.d, /run/udev/rules.d 디렉토리에 있는 파일들에 지정된 규칙에 따라, udevd는 장치 노드에 대한 추가 심볼릭 링크를 만들거나, 장치 노드 권한, 소유자 또는 그룹을 변경하거나, 해당 개체에 대한 내부 udevd 데이터베이스 항목(이름)을 수정한다.

이 세 디렉토리 안의 규칙들은 번호가 메겨지고 디렉토리들은 모두 병합된다. 만약 udevd가 생성 중인 장치에 대한 규칙을 찾지 못하면, devtmpfs가 처음 사용된 장치의 권한과 소유권을 남긴다.

7.3.2.3. 모듈 로딩

모듈로 컴파일된 장치 드라이버에는 별칭(alias)이 내장되어 있을 수 있다. 별칭은 modinfo 프로그램의 출력을 통해 확인할 수 있으며 보통은 모듈이 지원하는 장치의 버스 고유 식별자와 관련이 있다. 예를 들어, snd-fm801 드라이버는 벤더 ID가 0x1319이고 장치 ID가 0x0801인 PCI 장치를 지원하며, pci:v00001319d00000801sv*sd*bc04sc01i*라는 별칭을 갖고 있다. 대부분의 장치에서, 버스 드라이버는 sysfs를 통해 장치를 제어할 드라이버의 별칭을 내보낸다. 다시 말해, /sys/bus/pci/devices/0000:00:0d.0/modalias 파일은 문자열 pci:v00001319d00000801sv00001319sd00001319bc04sc01i00을 담고 있다. Udev와 함께 제공되는 기본 규칙은 udevdMODALIAS uevent 환경 변수의 내용(sysfs의 modalias 파일 내용과 같음)으로 /sbin/modprobe를 호출하여, 와일드카드 확장 후 별칭이 이 문자열과 일치하는 모든 모듈을 로드한다.

앞서 제시한 snd-fm801 예시에서는, 구식(그리고 원치 않는) forte 드라이버가 사용 가능하다면, 추가적으로 로드된다는 것을 의미한다. 원치 않는 드라이버 로드를 방지하려면 아래를 참고하라.

커널 자체도 네트워크 프로토콜, 파일 시스템 및 NLS 지원을 위한 모듈을 필요에 따라 로드할 수 있다.

7.3.2.4. 핫플러그/동적 장치 처리

USB MP3플레이어와 같은 장치를 컴퓨터에 연결하면, 커널은 장치가 연결된 것을 인식하고 uevent를 생성한다. 이 uevent는 앞서 설명한 바와 같이 udevd에 의해 처리된다.

7.3.3. 모듈 로딩 및 장치 생성 문제

장치 노드를 자동으로 생성할 때는 몇 가지 문제가 발생할 수 있다.

7.3.3.1. 커널 모듈이 자동으로 로드되지 않음

Udev는 모듈이 버스 고유 별칭을 갖고 버스 드라이버가 필요한 별칭을 sysfs로 적절하게 내보내는 경우에만 모듈을 로드할 것이다. 그렇지 않은 경우에는 다른 방법으로 모듈을 로드해야 한다. Linux-5.5.3에서는, Udev가 제대로 작성된 INPUT, IDE, PCI, USB, SCSI, SERIO, FireWire 장치용 드라이버들을 로드하는 것으로 알려져 있다.

필요한 장치 드라이버가 Udev에 필요한 사항을 갖고있는지 확인하려면, modinfo에 모듈 이름을 인수로 전달해 실행하라. 이제 /sys/bus 아래에서 장치 디렉토리를 찾고 modalias 파일이 있는지 확인하라.

modalias 파일이 sysfs에 존재하면, 드라이버는 장치를 지원하고 직접 통신할 수 있지만, 별칭이 없다면 드라이버의 버그이다. Udev의 도움 없이 드라이버를 로드하고 나중에 문제가 수정되길 기대하라.

/sys/bus 아래의 관련 디렉토리에 modalias 파일이 없다면, 이는 커널 개발자들이 아직 이 버스 유형에 modalias 지원을 추가하지 않았음을 의미한다. Linux-5.5.3에서는, ISA 버스가 이에 해당한다. 이 문제는 이후 커널 버전에서 수정되기를 기대하라.

Udev는 snd-pcm-oss와 같은 wrapper 드라이버나 loop와 같은 비하드웨어 드라이버를 로드하기 위한 것이 전혀 아니다.

7.3.3.2. 커널 모듈이 자동으로 로드되지 않으며, Udev도 커널 모듈을 로드하지 않음

wrapper 모듈이 일부 다른 모듈에서 제공하는 기능만 향상시킨다면(가령, snd-pcm-oss는 OSS 응용 프로그램에서 사운드 카드를 사용할 수 있게 하여 snd-pcm의 기능을 향상시킴), Udev가 래핑된 모듈을 로드한 후 래퍼를 로드하도록 modprobe를 구성하라. 이렇게 하려면 해당 /etc/modprobe.d/<filename>.conf 파일에 softdep 줄을 추가하라. 예를 들면:

softdep snd-pcm post: snd-pcm-oss

softdep 명령은 pre: 구문이나 pre:post:의 혼용도 허용한다는 점을 참고하라. softdep 문법 및 기능에 대한 자세한 내용은 modprobe.d(5) 매뉴얼 페이지를 참고하라.

문제의 모듈이 래퍼가 아니며 그 자체로 유용할 경우, 그 모듈을 시스템 부팅 중 로드하도록 modules 부트스크립트를 구성하라. 이렇게 하려면 모듈 이름을 /etc/sysconfig/modules 파일에 별도의 행으로 추가하라. 이것은 래퍼 모듈에도 적용되지만, 그것은 차선책이다.

7.3.3.3. Udev가 일부 원치 않는 모듈을 로드함

모듈을 빌드하지 않도록 하거나, 아래의 forte 모듈 예시처럼 /etc/modprobe.d/blacklist.conf 파일에 모듈을 블랙리스트로 추가하라:

blacklist forte

블랙리스트된 모듈도 modprobe 명령을 명시적으로 사용하여 수동으로 로드할 수 있다.

7.3.3.4. Udev가 장치를 잘못 생성하거나, 잘못된 심볼릭 링크를 생성함

이런 현상은 일반적으로 규칙이 예기치 않게 장치와 일치할 때 발생한다. 예를 들어, 형편없이 작성된 규칙은 SCSI 디스크(의도한 장치)와 해당 벤더의 SCSI 일반 장치(잘못된) 양쪽에 일치할 수 있다. udevadm info 명령의 도움을 받아 위반되는 규칙을 찾고 보다 구체적으로 수정하라.

7.3.3.5. Udev 규칙을 신뢰할 수 없음

이것은 앞서 설명한 문제의 또다른 증상일 수 있다. 그렇지 않고 규칙이 sysfs 속성을 사용한다면, 이후 커널에서 수정될 수 있는 커널 타이밍 문제일 수 있다. 당장은 사용된 sysfs 속성을 기다리는 규칙을 만들어 /etc/udev/rules.d/10-wait_for_sysfs.rules 파일에 추가(이 파일이 존재하지 않으면 생성하라)하면 된다. 이렇게 해서 도움이 되었다면 LFS 개발 리스트에 알려주기 바란다.

7.3.3.6. Udev가 장치를 생성하지 않음

아래 문단은 드라이버가 커널에 정적으로 구축되었거나 이미 모듈로 로드되어 있으며, Udev가 잘못 명명된 장치를 생성하지 않는 것을 이미 확인했다고 가정한다.

커널 드라이버가 데이터를 sysfs로 내보내지 않으면 Udev에는 장치 노드를 생성하는 데 필요한 정보가 없다. 이는 커널 트리 외부의 타사 드라이버에서 가장 흔하게 발생한다. /lib/udev/devices에 적절한 주/부 번호로 정적 장치 노드를 생성하라(커널 문서 내의 devices.txt 파일이나 타사 드라이버 공급 업체가 제공한 문서를 참고). 정적 장치 노드는 udev에 의해 /dev에 복사될 것이다.

7.3.3.7. 재부팅 후 장치 이름 지정 순서가 임의로 변경됨

이는 Udev가 설계상 uevents와 모듈 로드를 병렬로 처리하기 때문에, 순서를 예측할 수 없기 때문이다. 이것은 결코 수정되지 않을 것이다. 장치 이름들이 안정적일 것으로 커널에 기대해선 안 된다. 대신, Udev가 설치한 다양한 *_id 유틸리티의 출력이나 일련 번호와 같이, 장치의 안정적인 속성을 기반으로 안정적인 이름을 가진 심볼릭 링크를 만드는 규칙을 직접 생성하라. 예시는 7.4절. “장치 관리” 7.5절. “일반 네트워크 구성”를 참고하라.

7.3.4. 유용한 읽을거리

다음 사이트들에서 추가적으로 유용한 문서들을 읽을 수 있다: