Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология Образование Политология Производство Психология Стандартизация Технологии |
Добавление вашего кода в ядро
В этой главе: ? 10.1 Обход исходников ? 10.2 Написание кода ? 10.3 Сборка и отладка ? Резюме ? Упражнения Глава 10 • Добавление вашего кода в ядро
этой главе можно выделить две главные части: «Обход исходников» и «Написание кода». «Обход исходников» посвящен обзору драйвера устройства /dev/random, который является общим для всех систем Linux, и демонстрирует, как с ним связано ядро. Во время обзора мы вспомним некоторые особенности внутренней работы ядра, рассмотренные ранее, и осветим их с практической точки зрения. «Написание кода» - это руководство по написанию драйвера и затрагивает распространенные ситуации, с которыми сталкивается разработчик драйверов. После этих разделов мы перейдем к описанию того, как вы можете отлаживать драйвер устройства с помощью системы /ргос. Может быть, это и есть третья | сторона монеты? Обход исходников Этот раздел включает представление концепции системных вызовов и драйверов (также известных как модули) в Linux. Системные вызовы используются пользовательскими программами для общения с операционной системой для запроса служб. Добавление системного вызова - это один из способов создания новой службы ядра. Гл. 3, «Процессы: принципиальная модель выполнения», описывает внутреннюю реализацию системного вызова. Эта глава описывает практические аспекты встраивания вашего системного вызова в ядро Linux. Драйвер устройства представляет собой интерфейс, используемый ядром Linux для того, чтобы разрешать программисту управлять системным вводом-выводом устройств. Эта глава подробно разъясняет все тонкости. В этом разделе мы проследим работу драйвера с его представления в файловой системе и вплоть до контролирующего его кода ядра. В следующем разделе мы покажем, как использовать то, что мы изучили в первой части разработки функционального символьного устройства. Заключительная часть гл. 10 описывает, как писать системные вызовы и собирать ядро. Мы начнем с рассмотрения файловой системы и покажем, как эти файлы связаны с ядром. Познакомимся с файловой системой Устройства в Linux доступны через /dev. Например, -1 /dev/random выдает следующее: crw-rw-rw- 1 root root 1, 8 Oct 2 08: 08 /dev/random Первая «с» означает, что устройство является символьным; «Ь» означает блочное устройство. После владельца и колонок группы идут два числа, разделенные запятыми (в данном случае 1, 8). Первое число - это старший номер драйвера, а второе число - младший номер. Когда драйвер устройства регистрируется в ядре, он регистрирует старший номер. Когда данное устройство открывается, ядро использует старший номер устройства 10.1 Обход исходников для нахождения драйвера, зарегистрированного для этого старшего номера1. Младший номер передается через ядро в сам драйвер устройства, так как один драйвер может управлять несколькими устройствами. Например, /dev/urandom имеет старший номер 1 и младший номер 9. Это значит, что драйвер устройства зарегистрирован со старшим номером 1, обрабатывающим как /dev/random, так и /dev/urandom. Для генерации случайного числа мы просто выполняем чтение из /dev/random. Следующим способом можно считать 4 байта случайных данных2: lkp@lkp: ~$ head -c4 /dev/urandom | od -x 0000000 823а ЗЬе5 Если вы повторите эту команду, вы увидите что 4 байта [823а ЗЬе5] продолжают изменяться. Для демонстрации того, как ядро Linux использует драйверы устройств, мы проследим по шагам, что делает ядро, когда пользователь получает доступ к /dev/ random. Мы знаем, что файл устройства /dev/random имеет старший номер 1; мы можем определить, какой драйвер контролирует этот узел через /proc/devices: lkp@lkp: ~$ less /proc/devices Character devices: 1 mem Давайте рассмотрим драйвер устройства mem и поищем вхождение «random»; 653 static int memory_open(struct inode * inode, struct file * filp) 654 { 655 switch (iminor(inode)) { 656 case 1:
676 case 8: 677 filp-> f_op 678 break; 679 case 9: 680 filp-> f_op 681 break; 1 mknod создает файлы блочных и символьных устройств. 2 head -с4 берет первые 4 байта, a od -x форматирует их в шестнадцатеричный вид. Глава 10 • Добавление вашего кода в ядро Строки 655-681 Конструкция switch инициализирует поддержку драйверов на основе младшего номера устройства, с которым мы работаем. Точнее говоря, устанавливаются f ilp и fops. Возникает вопрос, что такое f ilp и что такое fop? FilpsnFops f ilp - это просто указатель на файловую структуру, a fop - это указатель на структуру f i 1 е_орerations. Ядро использует структуру f ile_operations для определения того, какую функцию при работе с данным файлом вызывать. Здесь выбирается раздел структур, используемых в драйвере устройства random. 556 struct file { 557 struct list_head f_list; 558 struct dentry *f_dentry; 559 struct vfsmount *f_vfsmnt; 560 struct file_operations *f__op; 561 atomic_t f_count; 562 unsigned int f_flags; 581 struct address_space *f_mapping; 582 }; 863 struct file_operations { 864 struct module *owner; 865 loff_t (*llseek) (struct file *, loff_t, int); 866 ssize_t (*read) (struct file *, char __ user *, size_t, loff_t *); 867 ssize_t (*aio_read) (struct kiocb *, char user *, size_t, loff_t); 868 ssize_t (*write) (struct file *, const char ___ user *, size__t, loff_t *); 869 ssize_t (*aio_write)(struct kiocb *, const char __ user *, size_t, loff_t); 870 int (*readdir) (struct file *, void *, filldir__t); 871 unsigned int (*poll) (struct file *, struct poll_table__struct *); 872 int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); 888 }; Драйвер устройства random определяет, какую операцию производить следующим образом: функции, реализованные в драйвере, должны соответствовать прототипам, перечисленным в структуре f ile_operations: Обход исходников 1824 struct file_operations random_fops = { 1825.read = random_read/ 1826.write = random_write/ 1827.poll = random_poll, 1828.ioctl = random_ioctl, 1829 }; 1831 struct file_operations urandom_fops = { 1832.read = urandom_read/ 1833.write = random_write/ 1834.ioctl = random_ioctl/ 1835 }; Строки 1824-1829 Устройство random реализует операции read, write, poll и ioctl. Строки 1831-1835 Устройство urandom реализует операции read, write и ioctl. Операция poll позволяет программисту выполнять проверку перед выполнением операции для проверки блокировки. Существует соглашение, что /dev/random блокируется, если затребовано больше байтов, чем находится в пуле энтропии1; /dev/ urandom не блокируется, но может вернуть не полностью случайные данные, если пул энтропии слишком мал. (Более подробную информацию см. в man pages, особенно в man 4 random.) По мере углубления в код обратите внимание, что, когда с /dev/random выполняются операции чтения, ядро передает управление в функцию random_read () (см. строку 1825); random_rand() определен следующим образом: drivers/char/random.с 1588 static ssize_t 1589 random_read(struct file * file, char _ user * buf, size_t nbytes, loff_t *ppos) У этой функции следующие параметры: • file. Указывает на структуру устройства. • buf. Указывает на область пользовательской памяти, где сохраняется результат. L В драйвере устройства random энтропия означает системные данные, которые невозможно предсказать. Обычно, она собирается из клавиатурного времени, перемещений мыши и другого нерегулярного ввода. Глава 10 • Добавление вашего кода в ядро • nbytes. Размер требуемых данных. • ppos. Указывает на позицию в файле, к которой получает доступ пользователь. Получается интересный результат: если драйвер выполняется в пространстве ядра, но буфер находится в пользовательском пространстве, как мы можем безопасно получить доступ к данным в buf? Следующий подраздел объясняет процесс перемещения данных между пользовательской памятью и памятью ядра. Популярное:
|
Последнее изменение этой страницы: 2016-03-25; Просмотров: 773; Нарушение авторского права страницы