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


Пользовательская память и память ядра



Если мы просто употребляем 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);
1634

1635 if (sec_random_state-> entropy_count / 8 == 0)

1636 scheduleO;
1637

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 бай­та энтропии, мы освобождаем управление процессором с помощью вызова sched­ule (). (Переменная 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);
2498

2499 #define SLEEP_ON__TAIL \

2500 spin_lock_irq(& q-> lock); \

2501 __ remove_wait_queue(q, & wait); \

2502 spin__unlock_irqrestore(& q-> lock, flags);
2503

2504 void fastcall _____ sched interruptible_sleep_on(wait_queue_head_t *q)

2505 {

2506 SLEEP_ON_VAR
2507

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; Нарушение авторского права страницы


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