Динамические таймеры
Динамические таймеры создаются и уничтожаются динамически. Количество динамических таймеров, активных в данный момент, ничем не ограничено.
Динамический таймер хранится в следующей структуре типа timer iist:struct timer_list {struct list_head entry;
unsigned long expires; spinlock_t lock; unsigned long magic; void (function)(unsigned long); unsigned long data; tvec_base_t base;}
Поле function содержит адрес функции, которую надо выполнить, когда время таймера истечет. Поле data содержит параметр, передаваемый этой функции. Благодаря полю data существует возможность определить одну функцию общего назначения, которая будет реагировать на тайм-ауты нескольких драйверов устройств. Поле data будет хранить идентификатор устройства или иную информацию, с помощью которой функция сможет отличить устройство.
Поле expires определяет момент истечения времени таймера. Время в нем выражено количеством тиков, прошедших с момента запуска системы. Все таймеры, у которых значение expires меньше или равно значению jiffies, считаются отработавшими или истекшими.
Поле entry используется для занесения программного таймера в один из двунаправленных циклических списков, в которых таймеры сгруппированы по значению их полей expires. Алгоритм, работающий с этими списками
Чтобы создать и активизировать программный таймер, ядро должно:
1. Создать, если необходимо, новый объект типа timer iist, например, t. Это можно сделать несколькими способами:
• создав статическую глобальную переменную в коде;
• определив локальную переменную внутри функции; в этом случае объект будет храниться в стеке режима ядра;
• включив объект в состав динамически выделяемого дескриптора.
2. Инициализировать объект с помощью функции init timer (&t). Она запишет в поле t.base значение null, а спин-блокировку t.iock пометит как открытую.
3. Записать в поле function адрес функции, которая должна быть активизирована, когда время таймера истечет. Если потребуется записать в поле data значение параметра, передаваемого этой функции.
4. Если динамический таймер еще не занесен в список, присвоить соответствующее значение полю expires и вызвать функцию add timer (&t), которая поставит элемент t в соответствующий список.
5. В противном случае, если динамический таймер уже находится в списке, обновить поле expires, вызвав функцию mod timerо, которая заодно позаботится и о переносе объекта в подходящий список (обсуждается далее).
После того как время таймера истечет, ядро автоматически удалит элемент t из его списка. Впрочем, иногда процесс должен явным образом удалить таймер ИЗ списка С ПОМОЩЬЮ функции del_time, del timer sync о ИЛИ dei_singieshot_timer_sync. Действительно, спящий процесс может быть разбужен до истечения таймера, и в этом случае процесс может принять решение об уничтожении таймера. Вызов функции dei_timer для таймера, уже удаленного из списка, не причинит никакого вреда, поэтому операция удаления таймера в коде таймерной функции считается хорошей практикой.
В Linux динамический таймер привязан к процессору, который его активизировал, т. е. таймерная функция всегда будет выполняться на том процессоре, который первым вызвал функцию add_timer или впоследствии вызвал функцию modtimer о. Однако функция dei_timer и ее аналоги могут деак- тивизировать любой динамический таймер, даже если он не привязан к локальному процессору.
Динамические таймеры и проблема одновременного обращения
Будучи асинхронно активизируемыми, динамические таймеры подвержены проблемам, связанным с одновременным обращением к ресурсам. Например, рассмотрим динамический таймер, функция которого работает с непостоянным ресурсом (таким как модуль ядра или файловая структура). Освобождение ресурса без предварительной остановки таймера может привести к порче данных, если таймерная функция активизируется, когда ресурс уже не существует. Таким образом, "золотое правило” рекомендует останавливать таймер до освобождения ресурса:
del_timer (&t) ;X_Release_Resources ;
Однако в многопроцессорных системах этот код небезопасен, поскольку не исключено, что таймерная функция уже выполняется на другом процессоре, когда вызывается функция dei_timer. В результате ресурсы будут освобождены, в то время как таймерная функция еще работает с ними. Чтобы избежать такой ситуации, ядро предлагает вниманию разработчика функцию del timer sync . Она удаляет таймер из списка, а затем проверяет, выполняется ли таймерная функция на каком-то другом процессоре. Если это так, функция del timer sync ждет завершения таймерной функции.
Функция dei_timer_sync довольно сложна и медлительна, поскольку она должна аккуратно обрабатывать случай, когда таймерная функция заново активизирует сама себя. Если разработчик ядра уверен, что таймерная функция не запускает таймер снова, он может воспользоваться более простой и быстрой функцией dei_singieshot_timer_sync, когда требуется деактивизиро- вать таймер и подождать завершения таймерной функции.
Конечно, существуют и другие проблемы одновременного обращения. Например, корректный способ модификации поля expires у уже активизированного таймера заключается в вызове функции mod timerо, а не в удалении таймера и воссоздании его заново. Во втором случае два управляющих тракта ядра, стремящиеся модифицировать поле expires у одного таймера, могут сильно помешать друг другу. Безопасность реализации таймерных функций в многопроцессорных системах достигается с помощью спин-блокировки lock, имеющейся в каждом объекте timer iist. Всякий раз, когда ядро должно обратиться к динамическому таймеру, оно отключает прерывания и захватывает эту спин-блокировку.
Предыдущая страница | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | Следующая страница