Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология Образование Политология Производство Психология Стандартизация Технологии |
Приоритетное прерывание обслуживания
Приоритетное прерывание обслуживания - это переключение одной задачи на другую. Мы упоминали как schedule () и scheduler_tick() решают, на какую задачу переключиться далее, но мы еще не описали, как Linux решает, когда выполнить переключение. В ядре 2.6 представлено приоритетное прерывание обслуживания, означающее, что как программы пользовательского пространства, так и программы пространства ядра могут быть переключены в одно и то же время. Так как в Linux 2.6 приоритетное прерывание обслуживания является стандартным, мы опишем, как работает приоритетное прерывание обслуживания для ядра и пользовательских задач в Linux. Явное приоритетное прерывание обслуживания в ядре Проще всего понять явное приоритетное прерывание обслуживания в ядре. Оно происходит в пространстве ядра, когда код ядра вызывает schedule (). Код ядра может вызывать schedule () двумя способами: либо с помощью прямого вызова schedule О, либо с помощью блокировки. Когда происходит явное приоритетное прерывание обслуживания в ядре, как, например, в драйвере устройства, ожидающем в wait_queue, управление просто передается планировщику и для выполнения выбирается новая задача. 7.2.2 Неявное пользовательское приоритетное Когда ядро завершает обработку задачи пользовательского пространства и готово передать управление задаче пользовательского пространства, оно сперва проверяет, какой задаче можно передать управление. Это не должна быть задача пользовательского пространства, которая передает управление ядру. Например, если задача А порождает системный вызов, после завершения системного вызова ядро передает управление системой задаче В. Каждая задача в системе имеет флаг «необходимости перепланировки», который устанавливается, когда задачу следует перепланировать: Глава 7 • Планировщик и синхронизация ядра include/linux/sched.h 988 static inline void set_tsk_need_resched(struct task_struct *tsk) 989 { 990 set_tsk_thread_flag(tsk, TIFJSTEED_RESCHED); 991 } 993 static inline void clear_tsk_need_resched(struct task_struct *tsk) 994 { 995 clear_tsk_thread_flag(tsk, TIF_NEED_RESCHED); } 1003 static inline int need__resched(void) 1004 { 1005 return unlikely (test_thread_f lag (TIF_NEED_RESCHED) ); 1006 } Строки 988-996 Интерфейсы set__tsk_need_resched и clear_tsk_need_resched позволяют устанавливать архитектурно-специфический флаг TIF_NEED_RESCHED. Строки 1003-1006 need_resched проверяет, установлен ли TIF_NEED_RESCHED во флаге текущего процесса. Когда ядро возвращается в пользовательское пространство, оно выбирает процесс, которому нужно передать управление, как описано в schedule () и scheduler_ tick(). Тогда как scheduler_tick() может пометить задачу как требующую перепланировки, только schedule () может манипулировать этим знанием; schedule () циклически выбирает новую задачу для выполнения до тех пор, пока новая выбранная задача не потребует перепланировки. После завершения schedule () новая задача получает управление процессором. Поэтому, пока процесс запущен, системный таймер порождает прерывания, запускающие scheduler_tick(); scheduler_tick() может помечать задачу как требующую перепланировки и перемешает ее в массив истекших. При завершении операций ядра за scheduler_tick() могут следовать другие прерывания и ядро будет передавать управление процессором, a schedule () вызываться для выбора следующей запускаемой задачи. Поэтому scheduler_tick() отмечает процессы и сортирует очереди, но schedule () выбирает следующую задачу и передает управление процессором. Неявное приоритетное прерывание обслуживания ядра В Linux 2.6 реализовано новое неявное приоритетное прерывание обслуживания ядра. Когда задача ядра получает управление процессором, его обслуживание может приори- Приоритетное прерывание обслуживания тетно прерываться только другой задачей ядра, если она не содержит никаких блокировок. Каждая задача имеет поле preempt_count, помечающее задачу как приоритетно прерываемую. Счетчик увеличивается каждый раз, когда задача блокируется, и уменьшается, когда разблокируется. Функция schedule () отключает приоритетное прерывание обслуживания, когда определяет, какую задачу запустить следующей. Существует две возможности для неявного приоритетного прерывания обслуживания ядра: либо код ядра вызывается из блока кода, для которого отключено приоритетное прерывание обслуживания, или процесс возвращается в код ядра из прерывания. Если управление возвращается в пространство ядра из прерывания, прерывание вызывает schedule () и новая задача вызывается тем же, описанным выше способом. Если код ядра вызывается из блока кода с отключенным приоритетным прерыванием обслуживания, включение приоритетного прерывания обслуживания может привести к приоритетному прерыванию обслуживания текущей задачи: include/linux/preempt.h 46 #define preempt__enable() \ 47 do { \
48 preempt_enable_no_resched(); \ 49 preempt_check__resched(); \ 50 } while (0) Строки 46-50 preempt__enable () вызывает preempt_enable_no_resched(), уменьшающую preempt_count текущей задачи на единицу, и затем вызывает preempt_check_resched (). include/linux/preempt.h 40 #define preempt_check_resched() \ 41 do { \ 42 if (unlikely(test_thread_flag(TIF_NEED_RESCHED) ) ) \ 43 preempt_schedule(); \ 44 } while (0) Строки 40-44 Preempt_check_resched () смотрит, помечена ли текущая задача как требующая перепланировки; если это так, вызывается preempt_schedule (). kernel/sched.с 2328 asmlinkage void _ sched preempt_schedule(void) 2329 { Глава 7 • Планировщик и синхронизация ядра 2330 struct thread_info *ti = current_thread__inf о (); 2332 /* 2333 * Если отключено ненулевое количество preempt__count или 233 4 * прерываний, мы не хотим приоритетно прерывать обслуживание 233 5 * текущей задачи. Просто возвращаемся. */ 2336 if (unlikely(ti-> preempt__count || irqs_disabled() ) ) 233 7 return; 233 9 need_resched: 2340 ti-> preempt__count = PREEMPT_ACTI VE; 2341 scheduleO; 2342 ti-> preempt_count = 0; 2343 2344 /* мы можем потерять возможность приоритетного прерывания обслуживания между планировщиком и текущим моментом */ 2345 barrier(); 2346 if (unlikely (test_thread_flag(TIF_NEED_RESCHED) ) ) 2347 goto need__resched; 2348 } Строки 2336-2337 Если текущая задача все еще имеет положительное значение preempt_count, как при рекурсивной команде preempt_disable (), или если для текущей задачи отключены прерывания, мы возвращаем управление процессором текущей задаче. Строки 2340-2347 Текущая задача не имеет блокировок, так как preempt_count равно 0, a IRQ включены. Поэтому мы устанавливаем preempt_count текущей задачи для обозначения входящего приоритетного прерывания обслуживания и вызываем schedule (), выбирающую другую задачу. Если задача запущена из кода, требующего перепланировки, ядру нужно удостовериться, что у текущей задачи можно забрать управление процессором. Ядро проверяет значение preempt_count задачи. Если preempt_count равно 0 и поэтому текущая задача не содержит блокировок, вызывается schedule () и для выполнения выбирается новая задача. Если preempt_count не равно 0, передавать управление другой задаче небезопасно, а управление возвращается в текущую задачу до тех пор, пока с нее не будут сняты все блокировки. Когда текущая задача освобождает блокировку, проверяют, требует ли задача перепланировки. Когда текущая задача освобождает последнюю блокировку и preempt_count достигает 0, производится немедленное перепланирование. Популярное:
|
Последнее изменение этой страницы: 2016-03-25; Просмотров: 827; Нарушение авторского права страницы