Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


Организация пула и прерывания



Когда драйвер устройства посылает команды в управляемое устройство, существует два способа определения успешности выполнения команды: можно организовать пул устрой­ства или использовать прерывания устройства.

При организации пула устройства драйвер устройства периодически проверяет устройства для проверки того, что команда успешно доставлена. Из-за того что драйвер устройства является частью ядра, при прямой организации пула присутствует риск заста­вить ядро ожидать завершения операции пула. Пул драйвера устройства может быть орга­низован через таймер. Когда драйвер устройства хочет заполнить пул устройства, он по­мещает в планировщик вызов функции из драйвера устройства. Эта функция выполняет проверку устройства без остановки ядра.

Перед тем как углубиться в подробности работы прерываний ядра, мы должны объ­яснить основные методы блокировки доступа к критическим секциями в коде ядра - цик­лическим блокировкам. Циклические блокировки работают с помощью установки специ­ального флага для некоторых значений перед вхождением в код критической секции и сбрасывания этого значения после оставления кода критической секции. Циклическая блокировка должна применяться, когда контекст задачи не блокируется, что и происходит в коде ядра. Давайте рассмотрим код циклической блокировки для архитектур х86 и РРС.

include/asm-i386/spinlock.h

32 #define SPIN_LOCK_UNLOCKED (spinlock_t) { 1 SPINLOCK_MAGIC_INIT }

34 #define spin_lock_init (x) do { * (x) = SPIN_LOCK_UNLOCKED; } while(O)

43 #define spin_is_locked(x) (*(volatile signed char *) (& (x)-> lock) < = 0)

44 #define spin_unlock_wait(x) do { barrier(); }

while(spin_is_locked(x))

include/asm-ppc/spinlock.h

25 idefine SPIN_LOCK_UNLOCKED (spinlock_t) { 0 SPINLOCK_DEBUG_INIT }

27 #define spin_lock_init (x) do { * (x) = SPIN_LOCK_UNLOCKED; } while(O)

28 #define spin_is_locked(x) ((x)-> lock! = 0)

while(spin_is_locked(x))

29 tdefine spin_unlock_wait(x) do { barrier(); }

while(spin_is_locked(x))

На архитектуре х86 флаг циклической блокировки устанавливается в 1 если она сня­та, а на РРС в 0. Отсюда видно, что при написании драйверов вам нужно использовать


Написание кода



предоставляемый макрос вместо сырых значений для кроссплатформенной совместимо­сти.

Задачи, которые хотят получить блокировку, в пустом цикле последовательно про­веряют значение специального флага до тех пор, пока оно не станет меньше 0, т. е. выпол­няется циклическое ожидание в задаче. См. sipn__unlock_wait () в двух блоках кода.

Циклические блокировки для драйверов обычно используются при обработке преры­ваний, когда коду ядра необходимо выполнить критическую секцию без прерывания дру­гими прерываниями. В предыдущих версиях ядра Linux использовались функции cli () и sti () для отключения и включения прерываний. Как и в 2.5.28, cli () и sti() поэтапно заменены циклическими блокировками. Новый способ выполнения разделов кода ядра, которые не могут быть прерваны - следующий:

Documentation/cli-sti-removal.txt

б

spinlock_t driver_lock = SPIN_LOCK_UNLOCKED; struct driver_data;

irq_handler (...) {

unsigned long flags;

spin_lock_irqsave(& driver_lock, flags);

driver_data.finish = 1; driver_data.new_work = 0;

spin__unlock_irqrestore (& driver__lock, flags)

ioctl_func (...) {

spin_lock_irq(& driver_lock);

driver_data.finish = 0; driver_data.new_work = 2;

spin__unlock_irq(& driver_lock);



Глава 10 • Добавление вашего кода в ядро


Строка 8

Перед началом кода критической секции прерывание сохраняется во флаг и выпол­няется блокировка driver_lock.

Строки 9-12

Этот код критической секции может выполняться только одной задачей од­новременно.

Строка 27

Эта строка заканчивает код критической секции. Восстанавливается состояние прерываний и разблокируется driver_lock.

С помощью spin_lock_irq_save () [и spin_lock_irq_reatore () ] мы проверяем, что прерывание отключено перед запуском обработчика прерывания и оста­лось отключенным после его завершения.

Когда ioctl__f unc () блокирует driver_lock, могут запускаться другие вызовы ircj__handler (). Поэтому нам нужно убедиться, что критическая секция в ioctl_ f unc () завершается как можно быстрее, для того чтобы гарантировать минимальное вре­мя ожидания irq_handler (), обработка прерывания верхнего уровня.

Давайте рассмотрим последовательность создания обработчика прерывания и его высокоуровневого обработчика (см. нижнюю половину, использующую очередь выпол­нения в подразд. 10.2.5)

#define mod_num_tries 3 static int irq = 0;

int count = 0;

unsigned int irqs = 0;

while ((count < mod_num_tries) & & (irq < = 0) ) {

irqs = probe_irq_on(); /* Заставляет устройство запустить прерывание. Некоторая задержка, требуемая на подтверждение получения прерывания */

irq = probe_irq_off(irqs);

/* If irq < 0 получение множественных прерываний. If irq == 0 нет полученных прерываний. */

count++; } if ((count == mod_num__tries) & & (irq < =0) ) {

printk(*Couldn't determine interrupt for %s\n" / MODULE_NAME); }


Написание кода



Этот код будет частью раздела инициализации драйвера устройства и завершится не­удачно, если прерывания не будут обнаружены. Теперь, когда у нас есть прерывание, мы можем зарегистрировать это прерывание и наш высокоуровневый обработчик прерыва­ния в ядре:

retval = request_irq(irq, irq_handler, SA__INTERRUPT,

DEVICE_NAME, NULL); if (retval < 0) {

printk(*Request of IRQ %n failed for %s\n",

irq, MODULE_NAME); return retval; }

request_irq () имеет следующий прототип:

arch/ i38б/kernel/irq.с

590 /**

591 * request__irq - выделение строки прерывания

592 * @irq: строка прерывания для выделения

593 * ©handler: Функция, вызываемая при наступлении IRQ

594 * @irqflags: тип флага прерывания

595 * ©devname: ascii имя для обрабатываемого устройства

596 * @dev_id: cookie передаваемое в функцию-обработчик

622 int request__irq(unsigned int irq,

623 irqreturn_t (*handler) (int, void *, struct pt_regs *),

624 unsigned long irqflags,

625 const char * devname,

626 void *dev_id)

Параметр irqflags может иметь значение ord следующего макроса:

• SAJSHIRQ для разделяемого прерывания;

• SA__INTERRUPT для отключения локальных прерываний во время выполнения

handler;

• SA_SAMPLE_RANDOM если прерывание является источником энтропии.

dev_id должно равняться NULL, если прерывание неразделяемое, и, если оно все-таки разделяемое, обычно адресует структуру данных устройства, так как это значение по­лучает handler.



Глава 10 • Добавление вашего кода в ядро


В этом месте полезно вспомнить, что любое запрашиваемое прерывание нужно осво­бодить при выходе из модуля с помощью f ree_irq ():

arch/ i386/kernel/irq.c 669 /**

67 0 * free_irq - освобождение прерывания

671 * @irq: линия прерывания для освобождения

672 * @dev_id: идентификатор устройства для освобождения

682 */

684 void free_irq(unsigned int irq, void *dev_id)

Если dev_id разделяет irq, модуль должен быть уверен, что прерывания вы­ключены перед вызовом этой функции. Кроме того, f ree_irq () никогда нельзя вызы­вать из контекста прерывания. Вызов f ree_irq () в модуле - стандартный способ очистки. [См. spin_lock_irq () и spin__unlock_irq ().]

В данной точке мы зарегистрировали наш обработчик прерывания и связали его с irq. Теперь нам нужно написать настоящий обработчик верхнего уровня, определяемый как irq_handler ().

void irq_handler(int irq, void *dev_id, struct pt_regs *regs) {

/* См. выше код циклической блокировки */

/* Копирование данных прерывания в очередь выполнения для обработчика нижней половины */

schedule_work( WORK_QUEUE );

/* Освобождение spin_lock */ }

Если вам нужен быстрый обработчик прерывания, вы можете использовать тасклет вместо очереди выполнения:

}


10.2 Написание кода



10.2.5 Очереди выполнения и тасклеты

Основную работу в обработчике прерываний обычно выполняет очередь выполнения. В предыдущем подразделе мы рассмотрели, как обработчики прерываний верхнего уров­ня копируют нужные им данные из прерывания в структуры данных и затем вызывают schedule_work ().

Для запуска задачи из очереди выполнения она должна быть помещена в work__struct. Для объявления структуры выполнения во время компиляции исполь­зуется макрос DECLARE_WORK (). Например, следующий код помещает в наш модуль инициализацию структуры выполнения и связывает ее с функцией и данными.

struct bh_data_struct { int data_one; int *data_array; char *data_text;

}

static bh__data_struct bh_data;

static DECLARE_WORK(my_mod_work, my_mod_bh, & bh_data);

static void my_mod_bh(void *data) {

struct bh_data_struct *bh_data = data;

/* весь замечательный код нижней половины */ }

Обработчик верхней половины настраивает все требуемые данные через my_mod_bh в bh_data и затем вызывает schedule_work(my_niod_work).

schedule_work () - это функция, доступная для любого модуля; тем не менее это значит, что работающий планировщик помещает «события» в общую очередь выполне­ния. Некоторые модули могут желать обладать собственными очередями выполнения, но требуемые для этого функции экспортируются только для GPL-совместимых модулей. Поэтому, если вы хотите оставить ваш модуль проприетарным, вы должны использовать общую очередь выполнения.

Очередь выполнения создается с помощью макроса create__workqueue (), ко­
торый вызывает__ create_workqueue () со вторым параметром, равным 0.

kernel/workqueue.с

3 04 struct workqueue_struct *_____ create_workqueue(const char *name,



Глава 10 • Добавление вашего кода в ядро


name может иметь длину до 10 символов.

Если single thread равно 0, ядро создает поток workqueue для процессора; если single thread равен 1, ядро создает единственный поток single thread для всей сис­темы.

Структуры выполнения создаются тем же способом, который был описан ранее, но они помещаются в отдельную очередь выполнения с помощью queue_work () вместо schedule_work ().

kernel/workqueue.с

97 int fastcall queue_work(struct workqueue_struct *wq, struct work_struct *work)

98 {

wq - это специальная очередь выполнения, созданная с помощью create_workqueue ().

work - это структура выполнения, помещаемая в wq.

Другие функции очереди выполнения можно найти в kernel /workqueue. с, включая следующее:

• queue_work_delayed(). Проверяет, что функция структуры выполнения вызывается по истечении указанного количества мгновений.

• flush_workqueue(). Заставляет вызывающего подождать до тех пор, пока завершится работа планировщика с очередью. Обычно используется при выходе из драйвера устройства.

• destroy_workqueue(). Обрабатывает и затем освобождает очередь выполнения.

Похожие функции schedule_work_delayed() и flush_sheduled_work() существуют и для общей очереди выполнения.


Поделиться:



Популярное:

  1. A. между органами государственного управления и коммерческими организациями
  2. II. Организация выполнения выпускной квалификационной работы
  3. II. Организация Российского флота в 1914–1918 гг
  4. III. Организация деятельности службы
  5. IV. Организация и несение караульной службы в подразделениях
  6. IV. Организация функционирования сооружений и устройств железнодорожного транспорта
  7. Автономная некоммерческая организация
  8. Акционерное общество (АО) – коммерческая организация, образованная одним или несколькими лицами, с уставным капиталом, разделенным на доли, права на которые удостоверяются ценными бумагами – акциями.
  9. В роли займодавца по кредитному договору может выступать только банк или иная кредитная организация, имеющая соответствующую лицензию Центробанка.
  10. Вначале организация проводит реформацию баланса и списывает прибыль после уплаты налогов проводкой: Дебет 99 Кредит 84.
  11. Внутренняя организация СовФед ФедСоб РФ
  12. Вопрос 34.Административно-правовая организация управления экономикой


Последнее изменение этой страницы: 2016-03-25; Просмотров: 666; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.027 с.)
Главная | Случайная страница | Обратная связь