Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология Образование Политология Производство Психология Стандартизация Технологии |
Дополнение кода для системного вызова⇐ ПредыдущаяСтр 55 из 55
Мы можем изменить Makefile в /kernel для включения файла в нашу функцию, но проще будет включить код функции в уже существующий файл в дереве исходников. Файл /kernel/sys. с содержит функции ядра для системных вызовов, а файл arch/ i386/kernel/sys_i3 86.с содержит системные вызовы х86 с нестандартной последовательностью вызова. Мы добавляем туда исходный код для нашего системного вызова, написанный на С. Этот код запускается в режиме ядра и выполняет всю работу. Все остальное в этой процедуре помогает нам получить эту функцию. Она обрабатывается через обработчик исключения х86: Написание кода kernel/sys.с 1: ..
/* где-то после последней функции*/ /* простая функция для демонстрации системного вызова. */ /* получение в номер, вывод, возвращение number+1 */ asmlinkage long sys_ourcall(long num) { printk(" Inside our syscall num =%d \n", num); return(num+1); } Когда обработчик исключения выполняет int 0x80, он индексируется внутри таблицы системного вызова. Файл /arch/i3 8 б /kernel /entry. S содержит функции обработчики прерывания нижнего уровня и таблицу системного вызова sys_ call_table. Таблица - это реализация в ассемблерном коде массива С с элементами длиной 4 байта. Каждый элемент или вхождение в этой таблице инициализируется для адресации функции. По соглашению мы должны приготовить имя нашей функции в sys__. Из-за того, что позиция в таблице определяется номером системного вызова, мы должны добавить имя нашей функции в конец списка. См. следующий код для изменения таблицы: arch/i38б/kernel/entry.S : .data 608: ENTRY(sys_call_table) .long sys_restart_syscall /* 0 - old " setupО" системный вызов используется для перезапуска */ .long sys_tgkill /* 270 */ .long sys_utimes.1ong sys_fadvi s e б 4_6 4 .long sys_ni_syscall /* sys_vserver */ 884: nr_syscalls=(.-sys_call_table)/4 Файл include/asiri/unistd.h связывает системные вызовы с их номерами позиции в sys_call_table. Также в этом файле находятся макросы для помощи пользовательским программам (написанным на С) загружать параметры в регистры. Здесь мы изменяем unistd.h и вставляем наш системный вызов: Глава 10• Добавление вашего кода в ядро include/asm/unistd.h
/* * Этот файл содержит номера системных вызовов. tdefine _____ NR_restart_syscall 0 #define _____ NR_exit 1 tdefine ______ NR_fork 2 #define _____ NR_utimes 271 #define _____ NR_fadvise64_64 272 #define _____ NR_vserver 273 #define _____ NR_ourcall 274 /* #define NR_syscalls 274 это старое значение перед нашим Наконец, мы хотим создать пользовательскую программу для проверки нового системного вызова. Как говорилось ранее в этом разделе, существует набор макросов, помогающих программисту ядра загружать параметры из кода С в регистры х86. В /usr/ include/asm/unistd.h существует 7 макросов: _sy sea llx (type, name, ..), гдех - номер параметра. Каждый макрос предназначен для загрузки определенного количества параметров от 0 до 5, a syscall б (...) позволяет загрузить указатель на большее число параметров. Следующая демонстрационная программа получает один параметр. Для этого примера (в строке 5) мы используем макрос _syscall (type, name, typel, namel) из /unistd.h, преобразующийся в вызов int 0x80 с правильными параметрами: mytest.с
#include < stdio.h> tinclude < stdlib.h> #include Vusr/include/asm/unistd.h" _syscall(long, ourcall, long, num); main() { printf(*our syscall --> num in=5, num out = %d\n", ourcall(5)); } 10.3 Сборка и отладка Сборка и отладка Добавление вашего кода в ядро обычно требует нескольких циклов программирования и отладки. В этом разделе мы опишем, как можно отладить написанный вами код ядра и как собрать связанные с отладкой инструменты. Отладка драйвера устройства В предыдущих разделах мы использовали файловую систему /proс для получения информации об ядре. Мы можем сделать доступной информацию о нашем драйвере устройства для пользователя через /ргос, что является отличным способом отладки части вашего драйвера устройства. Каждый узел в файловой системе /ргос связан с функцией ядра при чтении и записи. В ядре 2.6 большинство записей в части ядра, включая устройства, выполняется через sysf s вместо /ргос. Операции модифицируют специальные атрибуты объектов ядра во время выполнения ядра; /ргос остается полезным инструментом для операций только чтения, требующих большего количества данных, чем пара атрибут-значение, а этот раздел работает только с чтением из вхождений /ргос. Первый шаг позволяет получить доступ к чтению с вашего устройства с помощью создания вхождения в файловой системе /ргос с помощью create_proc_read_entry (): include/linux/proc_fs.h 146 static inline struct proc_dir_entry *create_proc_read_entry(const 147 mode_t mode, struct proc_dir_entry *base, 148 read_proc_t *read_proc, void * data) *name - это вхождение узла, появляющегося в /ргос, mode 0 позволяет файлу быть читаемым отовсюду. Если вы создаете несколько различных файлов ргос для одного драйвера устройства, сначала стоит создать директорию ргос с помощью proc_mkdir () и затем помещать каждый файл в нее; *base - это путь директории внутри /ргос для размещения файлов; значение NULL помещает файл прямо в /ргос. Функция *read_proc вызывается при чтении файла, а указатель *data передается обратное *read_proc: include/linux/proc_fs.h 44 typedef int (read_proc_t)(char *page, char **start, off_t off, 45 int count, int *eof, void *data); Это прототип для функций, которые будут осуществлять чтение через файловую систему /ргос; *раде - это указатель на буфер, куда функции записывают свои данные во Глава 10• Добавление вашего кода в ядро время чтения файла /ргос. Функция должна начать с записи по байту of f в *page и записи остальных count байт. Так как обычно чтение возвращает только небольшое количество информации, многие реализации игнорируют как off, так и count. Дополнительно **start обычно игнорируется и редко используется в ядре. Если вы реализуете функцию чтения, возвращающую определенное количество данных, то **start, off и count можно использовать для управления чтением небольших порций за определенное время. Когда чтение закончено, функция должна записывать 1 в * eof. Наконец, *data является параметром, передаваемым в функцию чтения, определенную в create^ proc__read_entry (). Резюме Эта глава описывает драйвер устройства, модули и системные вызовы. Мы описали несколько способов, которыми Linux использует драйверы устройств. Точнее говоря, мы рассмотрели следующие темы: в Мы описали дерево /dev в файловой системе Linux и объяснили, как определить, какое устройство контролируется и с помощью какого драйвера устройства. • Мы объяснили, как драйверы устройств используют структуры файлов и структуры файловых операций для обработки ввода-вывода файловой системы. • Мы описали разницу между памятью пользовательского уровня и памятью пространства ядра и то, как драйвер устройства может копировать данные из одной области в другую. • Мы рассмотрели конструкцию очереди ожидания в ядре и показали, как она используется, когда драйверу устройства нужно подождать освобождения определенного ресурса. • Мы исследовали теорию, стоящую за очередью ожидания и прерываниями, являющимися методами, используемыми ядром Linux для очистки прерываний и обработки драйверов устройств, когда процессор нужно передать другому процессу. • Мы представили вашему вниманию системные вызовы Linux и описали их базовые функции. • Мы описали разницу между блочными и символьными устройствами и новую модель устройств, представленную в Linux 2.6. Также сюда включен короткий обзор sysfs. В первой части гл. 10 об этих темах говорилось на абстрактном уровне, и мы проследили их практическое применение на примере специального драйвера устройства /dev/ random. Вторая часть гл. 10 предоставляет более конкретные примеры и код для непосредственного создания драйвера устройства. Упражнения Точнее говоря, мы рассмотрели детали следующих концепций: • Мы показали, как мы конструируем узлы в /dev, которые нужно связать с драйверами устройств, и как создаются динамические модули. • Мы описали новые методы в Linux 2.6 для экспорта символов из модуля драйвера устройства. • Мы продемонстрировали, как драйвер устройства предоставляет функцию IOCTL, позволяющую устройству работать с Linux через файловую систему. • Мы объяснили, как возникают прерывания и помещение в пул, а также разницу между циклическими блокировками на архитектурах х86 и РРС. • Мы объяснили, как добавить простой системный вызов в ядро Linux. Гл. 10 представляет мощную основу для разработки драйверов устройств в 2.6 и объединяет в единое целое идеи и концепции, представленные ранее в этой книге. Упражнения 1. См. создание ядра и пользовательского кода в гл. 3, «Процессы: принципиальная модель выполнения». Перекомпилируйте ядро и скомпилируйте my test. с. Запустите my test. с и рассмотрите вывод. 2. Добавьте другой параметр в ourcall. 3. Создайте системный вызов из ourcall. 4. Объясните сходства и различия между системными вызовами и драйверами устройств. 5. Почему мы не можем использовать memcpy для копирования данных между пользовательским пространством и пространством ядра? 6. В чем разница между функциями верхней половины и нижней половины? 7. В чем разница между тасклетами и work_queue? 8. Когда устройство обрабатывает больше чем просто чтение и запись, как с ним общается Linux? 9. Чему равно численное значение снятой циклической блокировки на архитектуре х86? НаРРС? 10. Одним предложением объясните разницу между блочными и символьными устройствами. Популярное:
|
Последнее изменение этой страницы: 2016-03-25; Просмотров: 973; Нарушение авторского права страницы