Как планировщик узнает, что поток заблокирован в ожидании ввода? - PullRequest
0 голосов
/ 17 ноября 2018

Когда поток, выполняющий пользовательский код, ожидает ввода, как планировщик знает, как его прервать, или как поток знает, как вызвать планировщик, поскольку маловероятно, что средний программист простого однопоточного приложения вставит sched_yield () везде.Вставляет ли компилятор sched_yield () при оптимизации или поток просто вращает блокировку до тех пор, пока не сработает общее прерывание таймера, установленное планировщиком, или пользователю необходимо явно указать функции wait (), sleep () для переключения контекста?

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

Ответы [ 3 ]

0 голосов
/ 17 ноября 2018

Будьте осторожны, чтобы не перепутать вытеснение со способностью процесса спать . Процессы могут бездействовать даже с не выгружаемым планировщиком. Это то, что происходит, когда процесс ожидает ввода-вывода. Процесс выполняет системный вызов, такой как read(), и устройство определяет, что данные недоступны. Затем он внутренне переводит процесс в спящий режим, обновляя структуру данных, используемую планировщиком. Затем планировщик выполняет другие процессы, пока не произойдет прерывание или какое-либо другое событие, которое разбудит исходный процесс. Затем проснувшийся процесс снова становится пригодным для планирования.

С другой стороны, preemption - это способность планировщика архитектуры останавливать выполнение процесса без его взаимодействия . Прерывание может произойти где угодно в потоке команд программы. Управление возвращается планировщику, который затем может выполнить другие процессы и позже вернуться к прерванному (прерванному) процессу. Большинство планировщиков выделяют временные интервалы, когда процессу разрешено запускаться в течение заданного промежутка времени, после чего он прерывается, если процессам с более высоким приоритетом требуются временные интервалы.

Если вы не пишете драйверы или код ядра, вам не нужно слишком сильно беспокоиться о базовых механизмах. При написании приложений пользовательского пространства ключевые концепции: (1) некоторые системные вызовы могут блокировать , что означает, что ваш процесс находится в спящем режиме до наступления события, и (2) в вытесняемых системах (все основные современные операционные системы). системы) ваша программа может быть прервана в любое время, чтобы могли запускаться другие процессы.

* Обратите внимание, что на некоторых платформах, таких как Linux, поток на самом деле является просто другим процессом, который разделяет свое виртуальное адресное пространство с другим процессом. Поэтому процессы и потоки обрабатываются планировщиком одинаково.

0 голосов
/ 12 декабря 2018

Ядро может быть вытесняющим, а не планировщиком.

Первые sched_yield() и wait() являются типами добровольного вытеснения , когда сам процесс выдает ЦП, даже если ядро ​​не имеет вытеснения.

Если ядро ​​имеет возможность переключаться на другой процесс, когда квант времени истек или процесс с более высоким приоритетом становится работоспособным, тогда речь идет о невольном вытеснении , то есть вытесняющем ядре, и это может произойти наразличные места объяснены ниже.

Разница заключается в том, что в sched_yield() процесс остается в состоянии выполнения TASK_RUNNING , но просто переходит в конец очереди выполнения для статического приоритета.Процесс должен ждать, чтобы снова получить процессор.

С другой стороны, wait() переводит процесс в состояние сна TASK_ (UN) INTERRUPTABLE в очереди ожидания вызывает schedule() и ожидает наступления события.Когда происходит событие, процесс снова перемещается для запуска очереди.Но это не значит, что они сразу получат процессор.

Здесь объясняется, когда schedule() может быть вызвано после пробуждения процесса:

Пробуждения на самом деле не вызывают вход в расписание().Они добавляют задачу в очередь выполнения и все.Если новая задача, добавленная в очередь выполнения, вытесняет текущую задачу, тогда функция пробуждения устанавливает TIF_NEED_RESCHED, и schedule () вызывается при ближайшем возможном случае:

  • Если ядро ​​выгружается (CONFIG_PREEMPT = y):

    • в контексте системного вызова или исключения, при ближайшем следующем preempt_enable ().(это может произойти, как только функция spin_unlock ()!)
    • в контексте IRQ вернется из обработчика прерываний в приоритетный контекст
  • Если ядро ​​не выгружается (CONFIG_PREEMPT не установлено), то при следующем:

    • вызов cond_resched ()
    • явное расписание () вызов
    • возврат изсистемный вызов или исключение из пользовательского пространства
    • возврат из обработчика прерываний в пользовательское пространство
0 голосов
/ 17 ноября 2018

Мне не ясно, касается ли ваш вопрос теории или практики.На практике в каждой современной операционной системе операции ввода-вывода являются привилегированными.Это означает, что для того, чтобы пользовательский процесс или поток могли обращаться к файлам, устройствам и т. Д., Он должен выполнить системный вызов.Тогда ядро ​​имеет возможность делать все, что считает целесообразным.Например, он может проверить, заблокирует ли операция ввода-вывода, и, следовательно, переключить запущенный (то есть «вызвать» планировщик) процесс после выполнения операции.Обратите внимание, что этот механизм может работать, даже если ядро ​​не прерывает таймер.Во всяком случае в целом это будет зависеть от вашей системы.Например, во встроенной системе, где не существует ОС (или минимальной), пользовательский код может нести полную ответственность за вызов планировщика перед выполнением операции блокировки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...