Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология Образование Политология Производство Психология Стандартизация Технологии |
Пользовательская память и память ядра
Если мы просто употребляем memcpy () для копирования буфера из пространства ядра в пользовательское пространство, операция копирования может не сработать из-за того, что адрес пользовательского пространства может быть замещен при возникновении mempcy (). В Linux есть функции copy_to_user () и copy_f rom_user (), что позволяет драйверам перемещать данные между пространством ядра и пользовательским пространством. В read_random () это делается в функции extract_entropy (), но есть и дополнительные особенности: drivers/char/random.с 1: static ssize_t extract^entropy(struct entropy_store *r, void * buf, 2: size_t nbytes, int flags) 3: { 1349 static ssize_t extract_entropy(struct entropy_store *r, void * buf, 13 50 size_t nbytes, int flags) 1351 { 1452 /* Копирование данных в буфер назначения */ 1453 i = min(nbytes, HASH_BUFFER_SIZE*sizeof ( u32)/2); 1454 if (flags & EXTRACT_ENTROPY_USER) {
1455 i -= copy_to_user(buf, (_ u8 const *)tmp, i); 1456 if (! i) {
1457 ret = -EFAULT; 1458 break; 1459 } 1460 } else 1461 memcpy(buf, (_ u8 const *)tmp, i); extract_entropy () имеет следующие параметры: • г. Указатель на внутреннее хранилище информации, игнорируемое в целях нашей дискуссии. • buf. Указатель на область памяти, заполняемую данными. 10.1 Обход исходников • nbytes. Размер записываемых в buf данных. • flags. Информируем функцию, где находится buf в пользовательской памяти или же в памяти ядра. extract_entropy () возвращает ssize_t, хранящую размер в байтах случайно сгенерированных данных. Строки 1454-1455 Если flags говорит нам, что buf указывает на место в нашей памяти, мы используем copy_to_user () для копирования указываемой trap памяти ядра в пользовательскую память, указываемую buf. Строки 1460-1461 Если buf указывает на место в памяти ядра, мы просто используем memcpy () для копирования данных. Получение случайных данных необходимо как пользовательским программам, так и программам ядра; пространство программ ядра может избегать накладок copy_to_ user (), не устанавливая соответствующий флаг. Например, ядро может реализовывать зашифрованную файловую систему и может избегать накладок на копирование в пользовательское пространство. Очереди ожидания Мы немного отвлечемся на объяснение того, как данные перемещаются между пользовательской памятью и памятью ядра. Давайте вернемся в read_random () и посмотрим, как она применяет очередь ожидания. Иногда драйверу может потребоваться подождать некоторого события, например доступа к системному ресурсу. В этом случае мы не хотим, чтобы ядро ожидало возможности доступа. Заставлять ядро ждать проблематично, так как все остальные системы в этом случае замрут1. С помощью объяснения очереди ожидания вы можете отложить время обработки до тех пор, пока не произойдет ожидаемое вами событие. Для обеспечения этого ожидания используются две структуры: очередь ожидания и голова очереди ожидания. Модуль должен создавать голову очереди ожидания и иметь части, использующие макросы sleep_on и wake_up для управления. Именно это происходит в random_read(): к На самом деле выполняющий задачу процессор будет ожидать. На многопроцессорных системах другие процессоры продолжают выполнение. Глава 10 • Добавление вашего кода в ядро 1588 static ssize_t 1589 random__read(struct file * file, char user * buf, size_t nbytes, loff_t *ppos) 1590 { 1591 DECLARE_WAITQUEUE(wait, current); 1597 while (nbytes > 0) { 1608 n = extract_entropy(sec_random_state, buf, n, 1609 EXTRACT_ENTROPY_USER | 1610 EXTRACT_ENTROPY_LIMIT | 1611 EXTRACT_ENTROPY_SECONDARY); 1618 if (n == 0) { 1619 if (file-> f_flags & 0_NONBLOCK) { 1620 retval = -EAGAIN; 1621 break;
1622 } 1623 if (signal__pending( current) ) {
1624 retval = -ERESTARTSYS; 1625 break; 1626 } 1632 set_current_state(TASK_INTERRUPTIBLE); 1633 add_wait_queue(& random_read_wai t, & wai t); 1635 if (sec_random_state-> entropy_count / 8 == 0) 1636 scheduleO; 1638 set_current_state (TASK_RUNNING); 1639 remove_wait_queue(& random_read_wait, & wait); 1645 continue; 1646 } Строка 1591 Очередь ожидания wait инициализируется текущей задачей. Макрос current ссылается на указатель task_struct текущей задачи. Строки 1608-1611 Мы извлекаем порцию случайных данных из устройства. Обход исходников Строки 1618-1626 Если мы не извлекли необходимое количество случайных данных из пула энтропии и задача не заблокирована или поступил требуемый ею сигнал, мы возвращаем ошибку в код вызова. Строки 1631-1633 Настройка очереди ожидания; random__read () использует собственную очередь ожидания, a random_read_wait, наоборот, использует системную очередь ожидания. Строки 1635-1636 В данном месте мы выполняем блокирующее чтение, и, если у нас не хватает 1 байта энтропии, мы освобождаем управление процессором с помощью вызова schedule (). (Переменная entropy_count хранит биты и не биты, поэтому необходимо производить деление на 8 для определения наличия полного байта энтропии.) Строки 1638-1639 Когда мы в конце концов выполняем перезапуск, мы очищаем нашу очередь ожидания. ПРИМЕ ЧА Н И Е. Устройство random в Linux требует заполнения очереди энтропии перед возвратом. Устройство urandom не предъявляет таких требований и возвращается независимо от количества данных, доступных в пуле энтропии. Давайте подробнее рассмотрим, что происходит при вызове schedule () задачи. 2184 asmlinkage void _ sched schedule(void) 2185 { 2209 prev = current; 2233 switch_count = & prev-> nivcsw; 2234 if (prev-> state & & ! (preempt_count() & PREEMPT_ACTIVE)) {
2235 switch_count = & prev-> nvcsw; 2236 if (unlikely((prev-> state & TASK_INTERRUPTIBLE) & &
2237 unlikely(signal_pending(prev)))) 2238 prev-> state = TASK_RUNNING; 2239 else 2240 deactivate_task(prev/ rq); 2241 } 2242... Глава 10 • Добавление вашего кода в ядро Строка 2209 Указатель на структуру текущей задачи сохраняется в переменную prev. В случае, когда сама задача вызывает schedule (), current указывает на эту задачу. Строка 2233 Мы сохраняем счетчик переключения задач nivcsw в switch_count. Далее после удачного переключения он будет увеличен1. Строка 2234 Мы попадаем сюда только в том случае, когда состояние задачи prev-> state не равно нулю и нет приоритетного прерывания обслуживания ядра. Другими словами, мы попадаем в этот блок кода, только если состояние задачи не равно TASK_RUNNING и ядро не выполняет приоритетного прерывания обслуживания задачи. Строки 2235-2241 Если задача прерывается, мы можем быть уверены, что она хочет освободить управление. Если подан сигнал для задачи, которая хочет освободить управление, мы устанавливаем состояние задачи в TASK_RUNNING, чтобы иметь возможность выбрать с помощью планировщика, какой задаче передать управление. Если сигнал не поступил, что обычно и происходит, мы деактивируем задачу и устанавливаем switch_count в nivcsw. Планировщик увеличит switch__count позже. При этом увеличивается nvcsw или nivcsw. Далее функция schedule () выбирает следующую задачу в очереди выполнения планировщика и переключает управление на эту задачу2. С помощью вызова schedule () мы можем позволить задаче передать управление процессором другой задаче ядра, когда текущая задача решит, что она будет по какой-либо причине ожидать. Другие задачи в ядре могут использовать это время, и можно надеяться, что, когда вызов schedule () вернет управление этой задаче, причина ожидания исчезнет. Возвращаясь из нашего отступления по поводу планировщика к функции random., read (), ядро в конечном счете передает управление обратно в random_read () и мы очищаем нашу очередь ожидания и продолжаем. Это происходит в цикле, и, если система сгенерировала достаточно энтропии, мы можем вернуться с требуемым количеством случайных байтов. random_read () устанавливает свое состояние в TASK_INTERRUPTIBLE перед вызовом schedule () для того, чтобы позволить прерывать себя поступающими сигналами во время нахождения в очереди ожидания. Собственный код драйвера генерирует 1 См. гл. 4 и 7, где описано использование счетчиков переключения контекста. 2 Подробную информацию см. в подразд. «swith_toQ» гл. 7. Обход исходников эти сигналы, когда накапливается избыточное количество энтропии, с помощью вызова wake_up_interruptible () в batch_entropy_j? rocess () и random_ioctl (). TASK_UNINTERRUPTIBLE обычно используется когда процесс ожидает ответа от аппаратного обеспечения в отличие от программного обеспечения (когда обычно используется TASK_INTERRUPTIBLE). Код, используемый random_read () для передачи управления другой задаче (см. строки 1632-1639, drivers /char /random, с) - является вариацией interruptible_sleep_on () из кода планировщика. 2489 #define SLEEP_ON_VAR \ 2490 unsigned long flags; \ 2491 wait_queue_t wait; \ 2492 init_waitqueue_entry(& wait, current); 2493 2494 #define SLEEP__ON_HEAD \ 2495 spin_lock_irqsave(& q-> lock, flags); \ 2496 ___ add_wait_queue(q, & wait); \ 2497 spin_unlock(& q-> lock); 2499 #define SLEEP_ON__TAIL \ 2500 spin_lock_irq(& q-> lock); \ 2501 __ remove_wait_queue(q, & wait); \ 2502 spin__unlock_irqrestore(& q-> lock, flags); 2504 void fastcall _____ sched interruptible_sleep_on(wait_queue_head_t *q) 2505 { 2506 SLEEP_ON_VAR 2508 current-> state = TASK_INTERRUPTIBLE; 2509 2510 SLEEP_ON_HEAD 2511 scheduleO; 2512 SLEEP_ON_JTAIL 2513 } q-это структура wait_queue_head, координирующая сон и ожидание модуля. Строки 2494-2497 Атомарное добавление нашей задачи в очередь ожидания q. Строки 2499-2502 Атомарное удаление задачи из очереди ожидания q. Глава 10 • Добавление вашего кода в ядро Строки 2504-2513 Добавление очереди ожидания. Передает управление процессором другой задаче. Когда мы передаем управление, сама задача удаляется из очереди ожидания. random_read () использует собственную очередь ожидания вместо стандартного макроса, но, по существу, выполняет interruptible_sleep_on() за исключением того, что, если у нас больше чем нужно байтов энтропии, мы не передаем управление, а повторяем цикл и получаем всю требуемую энтропию. Если энтропии недостаточно, random_read () ожидает до тех пор, пока не будет пробуждена с помощью interruptible_sleep_on () из собирающего энтропию процесса драйвера. Популярное:
|
Последнее изменение этой страницы: 2016-03-25; Просмотров: 856; Нарушение авторского права страницы