12.2 Работа с драйверами Linux (Working with Linux Drivers)¶
В «старые добрые» времена Linux работа с аппаратным обеспечением была делом весьма непредсказуемым. Если вы устанавливали распространённое оборудование известного производителя, оно, как правило, работало. Однако при установке специализированного оборудования или устройств малоизвестных производителей шансы на то, что устройство заработает под Linux, были невелики.
Почему? Ключевой проблемой была поддержка драйверов. Linux в те времена не воспринимался всерьёз, поэтому большинство производителей оборудования не тратили время на разработку Linux-драйвера для своих устройств. Если вы не умели писать драйверы самостоятельно, приходилось надеяться на доброго разработчика где-то в мире, который потратит время на создание поддержки для вашего оборудования.
К счастью, в современной IT-среде всё кардинально изменилось. Linux больше не считается «игрушкой» для энтузиастов — он всё активнее утверждается как надёжная операционная система корпоративного уровня. Как следствие, поддержка оборудования в Linux существенно улучшилась. Она ещё не идеальна, но с каждым годом становится лучше.
Если вам предстоит заниматься администрированием Linux-систем, крайне важно понимать, как работают драйверы в этой операционной системе. Если вы пользовались Windows, вы, вероятно, уже знакомы с процессом загрузки драйвера для поддержки того или иного устройства. Однако принципы работы драйверов в Linux несколько отличаются.
Чтобы убедиться в вашей уверенной работе с драйверами Linux, в этой части главы мы рассмотрим следующие темы:
- Что такое драйвер?
- Как реализованы драйверы в Linux
- Управление модулями ядра
- Работа с устройствами горячей и холодной замены
Чтобы эффективно управлять драйверами в Linux, необходимо прежде всего понять, что такое драйвер. Начнём с определения и разберёмся, как драйверы работают в Linux.
Что такое драйвер? (What Exactly Is a Driver, Anyway?)¶
Ключевая идея, которую необходимо понять при обсуждении драйверов: центральный процессор на материнской плате «от природы» не умеет взаимодействовать с другими аппаратными устройствами системы. Он не знает, как передать видеоданные на видеокарту, как сохранить данные на жёстком диске и как отправить данные на звуковую карту.
Чтобы выполнять эти операции, процессор нуждается в инструкциях в виде программного обеспечения, указывающего, как взаимодействовать с этими устройствами. Для базовых устройств — системных часов и клавиатуры — базовые драйверы могут храниться в микросхеме на материнской плате. Однако программное обеспечение для более сложных устройств, таких как сетевые и звуковые карты, не может быть удобно размещено в микросхеме материнской платы: существует слишком много разновидностей и моделей подобных устройств, и хранить всё необходимое программное обеспечение для каждого из них в одной микросхеме совершенно нереально. Вместо этого программное обеспечение хранится на жёстком диске системы. В процессе загрузки операционная система загружает это программное обеспечение (называемое драйвером (driver)) с жёсткого диска в оперативную память. После этого процессор получает инструкции, необходимые для взаимодействия с соответствующим оборудованием.
В этой главе нас прежде всего интересуют драйверы, относящиеся к последней категории оборудования. Давайте разберём, как с ними работать.
Реализация драйверов в Linux (How Drivers Are Implemented Under Linux)¶
Существуют два способа реализации драйвера устройства для конкретного аппаратного компонента в системе Linux. Во-первых, драйвер может быть загружен как модуль ядра (kernel module). После того как ядро Linux загружено в память в ходе рассмотренного ранее процесса загрузки, оно может быть настроено на загрузку модулей ядра, позволяющих процессору и операционной системе работать с установленным в системе оборудованием.
Модули ядра имеют расширение .ko или .o и хранятся в каталоге /lib/modules/kernel_version/kernel/drivers вашей Linux-системы. Внутри этого каталога расположен ряд подкаталогов, в которых непосредственно хранятся модули ядра для аппаратных устройств. Они показаны на рис. 12-3.

Рис. 12-3. Подкаталоги модулей в /lib/modules.
Например, модули ядра для поддержки устройств хранения SATA хранятся в подкаталоге ata, как показано на рис. 12-4.

Рис. 12-4. Модули ядра ATA в подкаталоге ata.
Второй способ реализации поддержки оборудования в Linux — непосредственная компиляция необходимой поддержки драйвера непосредственно в само ядро. Для этого необходимо вручную перекомпилировать ядро из исходного кода и в процессе указать, поддержку какого оборудования вы хотите встроить в ядро. После этого модули ядра для соответствующих устройств больше не нужны, поскольку операционная система располагает всем необходимым программным обеспечением непосредственно в ядре.
Звучит как отличное решение, не правда ли? Так почему же не компилировать драйверы для всего оборудования системы прямо в ядро? Для некоторых драйверов это действительно хорошая стратегия. Однако как общее правило следует ограничивать количество драйверов, компилируемых непосредственно в ядро, только теми, которые необходимы для загрузки системы (драйверы клавиатуры, драйверы устройств хранения и т.п.). Остальные драйверы следует загружать в виде модулей ядра.
Существует несколько весомых причин действовать именно таким образом. Во-первых, каждый драйвер, скомпилированный в ядро, увеличивает его общий размер. Следует стремиться к тому, чтобы ядро было как можно более компактным. Во-вторых, настройка ядра — операция более сложная, требующая детальных знаний об оборудовании и назначении каждого параметра конфигурации.
Наконец, необходимо учитывать вопрос модульности. Если вы никогда не изменяете, не обновляете и не перенастраиваете свою компьютерную систему, то компиляция большего числа драйверов непосредственно в ядро может иметь смысл. Однако для тех, кто постоянно добавляет, удаляет и перенастраивает оборудование, это нерационально. Загружая модули ядра, можно очень быстро добавлять или удалять поддержку аппаратных устройств из командной строки. Если же драйверы скомпилированы в ядро, может получиться раздутое ядро с поддержкой оборудования, которого в системе уже нет. По существу, модули ядра обеспечивают значительно большую гибкость системы.
В этой главе мы сосредоточимся прежде всего на модулях ядра. Давайте разберём, как ими управлять.
Управление модулями ядра (Managing Kernel Modules)¶
Когда начинающие администраторы впервые сталкиваются с управлением модулями ядра, это кажется сложным. Однако, как и многие другие аспекты Linux, на практике всё оказывается достаточно просто. В этой части главы мы рассмотрим следующие темы:
- Просмотр установленного оборудования
- Управление модулями ядра с помощью команд оболочки
Начнём с просмотра оборудования, установленного в системе.
Просмотр установленного оборудования (Viewing Installed Hardware)¶
Одним из ключевых навыков системного администратора Linux является умение просматривать список оборудования, установленного в системе. Одним из лучших инструментов для этого служит каталог /proc. Напомним: каталог /proc фактически не существует в файловой системе. Это псевдофайловая система, которая динамически создаётся при каждом обращении к ней. Как видно на рис. 12-5, каталог /proc содержит подкаталог для каждого процесса, выполняющегося в системе.

Рис. 12-5. Использование каталога /proc
Помимо подкаталогов процессов, /proc содержит ряд других файлов. Ниже перечислены наиболее полезные из них:
cpuinfo— содержит сведения о процессоре, установленном в системе. Для просмотра содержимого этого файла можно использоватьcat,less,moreили любой текстовый редактор. Пример содержимого:
processor : 0
vendor_id : GenuineIntel
cpu family : 15
model : 6
model name : Intel(R) Pentium(R) D CPU 2.80GHz
stepping : 2
microcode : 0xf
cpu MHz : 2793.072
cache size : 2048 KB
physical id : 0
siblings : 2
core id : 0
cpu cores : 2
...
devices— содержит список устройств, установленных в системе.dma— содержит список назначений каналов DMA в системе.interrupts— содержит список назначений IRQ в системе. Пример:
CPU0 CPU1
0: 344 0 IO-APIC-edge timer
1: 31 301 IO-APIC-edge i8042
3: 1 0 IO-APIC-edge
4: 0 1 IO-APIC-edge
6: 7 0 IO-APIC-edge floppy
7: 0 0 IO-APIC-edge parport0
8: 1 0 IO-APIC-edge rtc0
9: 0 0 IO-APIC-fasteoi acpi
12: 1589 107 IO-APIC-edge i8042
14: 0 0 IO-APIC-edge ata_piix
15: 4111 1246 IO-APIC-edge ata_piix
16: 659 0 IO-APIC-fasteoi vmci, Ensoniq AudioPCI
...
iomem— содержит список назначений адресов портов ввода-вывода в системе.modules— содержит список всех модулей ядра, используемых системой в данный момент. Пример:
fuse 75897 3 - Live 0xffffffffa01cc000
p6t_LOG 5898 8 - Live 0xffffffffa020f000
xt_tcpudp 2859 10 - Live 0xffffffffa01f0000
xt_pkttype 1288 3 - Live 0xffffffffa01e3000
ipt_LOG 6067 8 - Live 0xffffffffa00f7000
xt_limit 2559 16 - Live 0xffffffffa00de000
vmsync 4288 0 - Live 0xffffffffa0082000
vmblock 13824 1 - Live 0xffffffffa0065000
...
version— содержит информацию о версии ядра Linux, работающего в системе./scsi/— содержит файлы со сведениями об SCSI-устройствах системы, если используется SCSI-адаптер./bus/devices— содержит информацию об USB-устройствах системы.
В дополнение к /proc каталог /sys/ также предоставляет информацию об оборудовании, установленном в системе. Файловая система организована в виде древовидной структуры, сгруппированной по шинам данных, устройствам и связанным с ними драйверам. На верхнем уровне каталога /sys содержится множество подкаталогов, в том числе:
/sys/block— содержит файл символьной ссылки для каждого блочного устройства в системе/sys/bus— содержит каталог для каждой шины данных в системе, включая шины PCI, SCSI и USB
У каждого каталога шины есть два подкаталога — devices и drivers, как показано здесь:
Подкаталог devices содержит записи для каждого устройства на данной шине, тогда как подкаталог drivers содержит подкаталоги для каждого драйвера, загруженного для устройства на этой шине:
/sys/class— содержит все доступные классы устройств/sys/devices— содержит запись для каждого обнаруженного устройства/sys/module— содержит подкаталоги для каждого модуля, загруженного в ядро, как показано ниже:
openSUSE:/sys/module # ls
8250 ip6t_LOG nf_conntrack_ipv6 snd_seq
ac ip6t_REJECT nf_conntrack_netbios_ns snd_seq_device
ac97_bus ip6table_filter nf_defrag_ipv4 snd_seq_midi
acpi ip6table_mangle parport snd_seq_midi_event
apparmor ip6table_raw parport_pc snd_timer
ata_generic ip_tables pci_hotplug soundcore
ata_piix ipt_LOG pcie_aspm spurious
...
В большинстве дистрибутивов Linux имеется также набор инструментов командной строки для просмотра информации об оборудовании системы. Некоторые из этих инструментов получают данные непосредственно из каталога /proc. Вы можете использовать следующие команды:
hdparm /dev/устройство— отображает информацию о жёстком диске. Вместоустройствоукажитеsda,sdbи т.д. Пример:
ws1:/proc # hdparm /dev/sdb
/dev/sdb:
HDIO_DRIVE_CMD(identify) failed: Invalid argument
readonly = 0 (off)
readahead = 256 (on)
geometry = 1044/255/63, sectors = 16777216, start = 0
sg_scan— сканирует шину SCSI и выводит список всех устройств, подключённых к SCSI-контроллеру.sginfo –l— также выводит список всех подключённых SCSI-устройств.lsusb— отображает информацию об USB-устройствах, подключённых к системе Linux.hwinfo— отображает исчерпывающий обзор аппаратного обеспечения системы. Утилита зондирует аппаратуру и формирует системный обзорный отчёт. Фрагмент образца отчёта:
HD 0 type : none
HD 1 type : none
HD type 48 data: 53760/6/212 C/H/S, precomp 10, lz 0
HD type 49 data: 273/128/201 C/H/S, precomp 0, lz 7168
DOS base memory: 640 kB
Extended memory: 64512 kB (configured), 64512 kB (tested)
Gfx adapter : EGA, VGA, ... (with BIOS)
FPU : installed
...
Если не указывать параметры, утилита может сформировать довольно длинный отчёт. Однако его можно ограничить одной подсистемой устройств с помощью параметра --device. Список поддерживаемых устройств смотрите в man-странице hwinfo.
lspci— выводит список всех PCI-устройств, установленных в системе. Параметр–kпозволяет увидеть модули ядра, связанные с каждым устройством.
Получив эти сведения, вы готовы перейти к управлению модулями ядра из командной строки.
Управление модулями ядра с помощью команд оболочки (Using Shell Commands to Manage Kernel Modules)¶
Как и в большинстве других операционных систем, модули ядра Linux можно вручную просматривать, загружать и выгружать. Хотя для этих задач существуют графические утилиты, мы сосредоточимся на выполнении этих операций из командной строки.
Для просмотра всех загруженных в данный момент модулей ядра используется команда lsmod. Она считывает данные из файла /proc/modules и отформатированно выводит их на экран. Для использования команды достаточно ввести lsmod в командной строке, как показано в примере:
Module Size Used by
lp 10913 0
parport_pc 37547 1
af_packet 23229 0
joydev 11942 0
st 41564 0
fuse 75897 3
...
Для получения подробных сведений о конкретном загруженном модуле используется команда modinfo. Сначала с помощью lsmod можно найти имя нужного модуля, а затем выполнить modinfo имя_модуля в командной строке. В приведённом выше примере одним из модулей, отображаемых lsmod, является joydev — модуль ядра джойстика системы. Для получения подробной информации об этом модуле введите modinfo joydev в командной строке:
openSUSE:~ # modinfo joydev
filename: /lib/modules/2.6.34.7-0.7-desktop/kernel/drivers/input/joydev.ko
license: GPL
description: Joystick device interfaces
author: Vojtech Pavlik vojtech@ucw.cz
srcversion: B57DA6AAEE9B8102A061E91
alias: input:b*v*p*e*-e*1,*k*2C0,*r*a*m*l*s*f*w*
alias: input:b*v*p*e*-e*1,*k*130,*r*a*m*l*s*f*w*
alias: input:b*v*p*e*-e*1,*k*120,*r*a*m*l*s*f*w*
alias: input:b*v*p*e*-e*3,*k*r*a*6,*m*l*s*f*w*
alias: input:b*v*p*e*-e*3,*k*r*a*8,*m*l*s*f*w*
alias: input:b*v*p*e*-e*3,*k*r*a*0,*m*l*s*f*w*
depends:
vermagic: 2.6.34.7-0.7-desktop SMP preempt mod_unload modversions
Перед загрузкой модуля ядра необходимо выполнить команду depmod в командной строке. Эта команда строит файл modules.dep, хранящийся в /lib/modules/kernel_version/:
openSUSE:/lib/modules/modules/3.11.10-21-desktop # ls
kernel modules.builtin.bin modules.order systemtap
modules.alias modules.dep modules.softdep vdso
modules.alias.bin modules.dep.bin modules.symbols weak-updates
modules.builtin modules.devname modules.symbols.bin
В этом файле depmod перечисляет зависимости между модулями. Это позволяет другим утилитам управления модулями ядра гарантировать загрузку зависимых модулей при загрузке любого модуля.
После создания файла modules.dep можно приступать к загрузке модулей ядра. Для этого используется одна из двух команд. Первая — insmod. Синтаксис: insmod имя_файла_модуля. Имя файла модуля — это, как правило, модуль ядра в одном из подкаталогов /lib/modules/kernel_version/kernel/. Например, чтобы загрузить драйвер стандартного параллельного порта PC, нужно выполнить insmod /lib/modules/version/kernel/drivers/parport/parport_pc.ko в командной строке.
Помимо insmod, можно также использовать команду modprobe. Большинство администраторов предпочитают modprobe. Ключевое отличие состоит в том, что insmod не учитывает зависимости модулей, выявленные командой depmod.
Синтаксис использования modprobe: modprobe имя_модуля. Как и в случае с insmod, загружаемый с помощью modprobe модуль находится в подкаталоге /lib/modules/kernel_version/kernel/. Например, каталог /lib/modules/kernel_version/kernel/drivers/net/ethernet содержит модули ядра для различных сетевых карт:
ws1:/lib/modules/modules/3.11.10-21-desktop/kernel/drivers/net/ethernet # ls
3c59x.ko chelsio hamachi.ko myri10ge qlcnic sunhme.ko
8139cp.ko cnic.ko hamradio natsemi.ko qlge tehuti.ko
8139too.ko cxgb3 hp100.ko ne2k-pci.ko r6040.ko tg3.ko
...
Чтобы загрузить модуль ядра для сетевой карты 3c590, нужно ввести modprobe 3c590 в командной строке. Вероятно, у вас возникнет вопрос: будет ли модуль постоянно загружаться после перезагрузки системы? Ответ: нет (если только устройство не определяется автоматически при загрузке). Однако modprobe выполняется автоматически при каждой загрузке ядра. Он читает содержимое файла /etc/modprobe.conf, чтобы определить, какие модули ядра следует загружать при запуске.
Совет
Если файл /etc/modprobe.conf не существует, modprobe будет использовать файлы из каталога /etc/modprobe.d/ для определения модулей ядра, загружаемых при загрузке.
Файл modprobe.conf использует следующие директивы:
install имя_модуля— указываетmodprobeзагрузить указанный модуль. Директиву также можно использовать для выполнения любой допустимой команды оболочки, что обеспечивает высокую гибкость при загрузке модулей.alias псевдоним имя_модуля— задаёт модулю ядра псевдоним, который можно использовать для обращения к нему из командной строки.options имя_модуля параметры— передаётmodprobeсписок параметров (например,irq=иio=), которые должны применяться при загрузке конкретного модуля.
Обратите внимание: в начале файла имеется предупреждение о том, что не следует изменять /etc/modprobe.conf напрямую. Вместо этого, если вам нужно вручную указать, что конкретный модуль ядра должен загружаться при запуске системы, следует вносить изменения в файл /etc/modprobe.conf.local, используя описанные выше директивы.
Примечание
В качестве альтернативы можно добавить команду modprobe имя_модуля в файл rc.local или boot.local, чтобы обеспечить загрузку модуля при запуске системы. Однако на практике это требуется редко. В большинстве дистрибутивов при загрузке системы выполняется процедура обнаружения оборудования, которая сканирует новые устройства и автоматически загружает соответствующие модули ядра.
Для выгрузки загруженного модуля ядра используется команда rmmod имя_модуля. Следует учитывать, что эта команда не сработает, если устройство, обслуживаемое модулем, используется в данный момент — в таком случае модуль не будет удалён. Как и insmod, команда rmmod не учитывает зависимости модулей и не выполняет с ними никаких действий. Если требуется удалить модуль с учётом зависимостей, следует использовать modprobe. Синтаксис удаления модуля с помощью modprobe: modprobe –r имя_модуля.
Давайте закрепим навыки работы с модулями ядра в следующем упражнении.
Упражнение 12-1. Работа с модулями ядра
В этом упражнении вы будете практиковаться в просмотре информации о модулях ядра. Упражнение можно выполнить на виртуальной машине, прилагаемой к книге. Запустите снимок состояния 12-1 для правильно настроенного окружения.
Видео
Посмотрите видео к упражнению 12-1: в нём показано, как выполнить это задание.
Выполните следующие шаги:
- Загрузите систему Linux и войдите в систему как пользователь
studentс паролемstudent. - Откройте сеанс терминала.
- Переключитесь на учётную запись суперпользователя root, введя
su –и парольstudent. - Просмотрите состояние модулей ядра системы, введя
lsmod | lessв командной строке. - Пролистайте список модулей ядра. Когда закончите, нажмите
q. - Просмотрите информацию о модуле ядра
parport, введяmodinfo parportв командной строке. - Создайте список зависимостей модулей, введя
depmodв командной строке. - С помощью утилиты
lessилиmoreпросмотрите созданный файл зависимостейmodules.depв каталоге/lib/modules/kernel_version/. - Введите
lsmod | grep joydevв командной строке. В строке вывода должно стоять0, что означает: модуль загружен, но оборудование не используется. - Выгрузите модуль
joydev, введяrmmod joydevв командной строке. - Убедитесь, что модуль ядра
joydevбыл выгружен, введяlsmod | grep joydevещё раз. Вывод должен быть пустым, что означает: модуль не загружен. - Перезагрузите модуль джойстика, введя
modprobe joydevв командной строке. - Введите
lsmod | grep joydevещё раз. Снова должно появиться0, что означает: модуль загружен, но оборудование не используется:
Работа с устройствами горячей и холодной замены (Working with Hot-Plug and Cold-Plug Devices)¶
При работе с аппаратным обеспечением и модулями ядра в Linux необходимо помнить, что оборудование ПК делится на две категории:
-
Устройства холодной замены (cold-plug devices) — устройства, которые можно физически подключать к системе или отключать от неё только при выключенном питании. К внутренним устройствам холодной замены относятся процессор, оперативная память, платы расширения и устройства хранения. К внешним устройствам холодной замены — параллельные принтеры и внешние SCSI-устройства.
Ключевое свойство устройств холодной замены: если подключить их при работающей системе, они, скорее всего, не будут обнаружены и распознаны. Если же подключить их при выключенной системе, а затем попытаться отключить при работающей, вероятно, будет повреждён компонент (или сама система) вследствие электрического удара. По умолчанию непосредственно взаимодействовать с аппаратным обеспечением системы могут только процессы, выполняющиеся в пространстве ядра.
Примечание
Процессы пространства ядра (kernel space) являются частью самого ядра. Процессы пространства пользователя (user space) — это программы, запускаемые конечным пользователем.
Для предоставления процессам пространства пользователя доступа к оборудованию служит каталог
/dev. Как было описано ранее, оборудование в Linux-системе доступно через файл устройства в/dev. Именно так осуществляется доступ к устройствам холодной замены (например, к жёстким дискам). -
Устройства горячей замены (hot-plug devices) — устройства, предназначенные для подключения и отключения «на лету», во время работы системы. Программное обеспечение Linux определяет изменения в составе системы при подключении и отключении таких устройств. Система распознаёт устройство при его подключении и загружает соответствующие модули. Примерами устройств горячей замены являются USB- и FireWire-устройства.
Проблема обращения к устройствам горячей замены заключается в следующем. Устройства холодной замены доступны через файлы устройств в
/dev. Можно ли таким же образом обращаться к устройствам горячей замены? Да, но для этого требуется дополнительная поддержка. Один из вариантов — заранее создать в/devфайл устройства для каждого возможного устройства, которое когда-либо может быть подключено к системе. Это позволит приложениям обращаться к устройствам горячей замены через соответствующие файлы устройств при их подключении.Такой подход работает, но весьма неудобен. Более рациональный вариант — динамически создавать файл устройства в
/devпри подключении устройства горячей замены и удалять его при отключении. Для этого операционная система должна распознать новое оборудование, создать соответствующие файлы устройств в/devи уведомить выполняющиеся процессы о появлении нового оборудования.Для этого используется несколько компонентов Linux. Первый — sysfs. Этот компонент предоставляет виртуальную файловую систему
/sys, рассмотренную ранее в этой главе. Его задача — экспортировать информацию об аппаратных устройствах системы (как горячей, так и холодной замены), чтобы приложения и утилиты, работающие в системе, могли получить к ним доступ.Второй компонент — уровень аппаратных абстракций (Hardware Abstraction Layer, HAL) — демон
hald. Этот демон автоматически запускается при старте системы. Его задача — предоставлять запущенным приложениям информацию об оборудовании (как горячей, так и холодной замены), доступном в системе.Следующий компонент — шина рабочего стола (Desktop Bus)
dbus. Она также работает в Linux в режиме демона и выполняет несколько ключевых функций. В контексте данной темы важно помнить, что именно демонdbusуведомляет запущенные процессы при подключении или отключении устройства горячей замены.Наконец, в Linux используется udev. Демон
udevdсоздаёт виртуальную файловую систему, монтируемую по адресу/dev. Он взаимодействует с ядром Linux через интерфейс uevent. Когда устройство горячей замены подключается или отключается, ядро отправляет сообщение uevent, которое перехватываетсяudevd. На основании правил, определённых в файлах каталога/etc/udev/rules.d,udevdвыполняет следующие действия:- Инициализирует устройство.
- Создаёт соответствующий файл устройства в каталоге
/dev. - Если новое устройство является сетевым интерфейсом — настраивает его с помощью утилиты
ifup. - Если новое устройство является устройством хранения — монтирует его, используя информацию из
/etc/fstab. - Уведомляет запущенные процессы о появлении нового устройства.