Подтвердить что ты не робот

Как системные вызовы, такие как select() или poll(), работают под капотом?

Я понимаю, что операции async ввода/вывода с помощью select() и poll() не используют процессорное время, то есть его не занятый цикл, а затем как они действительно реализованы под капотом? Поддерживается ли это как-то в аппаратных средствах и почему не так много очевидных затрат на процессор для их использования?

4b9b3361

Ответ 1

Это зависит от того, что ждет select/poll. Рассмотрим несколько случаев; Я собираюсь принять одноядерную машину для упрощения.

Сначала рассмотрим случай, когда select ожидает другой процесс (например, другой процесс может выполнять некоторые вычисления, а затем выводит результат по конвейеру). В этом случае ядро ​​будет отмечать ваш процесс как ожидающий ввода, и поэтому он не будет предоставлять процессорного времени вашему процессу. Когда другой процесс выводит данные, ядро ​​пробудит ваш процесс (дайте ему время на CPU), чтобы он мог обрабатывать входные данные. Это произойдет, даже если другой процесс все еще запущен, потому что современные ОС используют превентивную многозадачность, а это означает, что ядро ​​будет периодически прерывать процессы, чтобы дать другим процессам возможность использовать CPU ( "временное масштабирование" ).

Изображение меняется, когда select ожидает ввода-вывода; сетевые данные, например, или ввод с клавиатуры. В этом случае, в то время как архаичное оборудование должно было бы открутить процессор, ожидающий ввода, все современное оборудование может поместить процессор в состояние ожидания "малой мощности", пока аппаратное обеспечение не обеспечит прерывание - событие с особым управлением, которое обрабатывает ядро. В обработчике прерываний ЦП будет записывать входящие данные, а после возвращения из прерывания пробудит ваш процесс, чтобы он мог обрабатывать данные.

Ответ 2

Нет поддержки аппаратного обеспечения. Ну, есть... но ничего особенного, и это зависит от того, какой дескриптор файла вы смотрите. Если есть драйвер устройства, реализация зависит от драйвера и/или устройства. Например, сокеты. Если вы ожидаете чтения некоторых данных, есть последовательность событий:

  • Некоторые вызовы вызова poll()/select()/epoll() для ожидания данных в сокете. Существует переключатель контекста из пользовательского режима в ядро.
  • Сетевой адаптер прерывает процессор, когда приходит какой-либо пакет. Процедура прерывания в драйвере нажимает пакет в конце очереди.
  • Существует поток ядра, который берет данные из этой очереди и просыпает сетевой код внутри ядра для обработки этого пакета.
  • Когда пакет обрабатывается, ядро ​​определяет ожидающий его сокет, сохраняет данные в буфере сокета и возвращает системный вызов обратно в пространство пользователя.

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

Другой пример, в котором не задействованы никакие драйверы, - это unix-сокет. Если вы ждете данных от одного из них, процесс, который ждет, добавляется в список. Когда другой процесс с другой стороны сокета записывает данные, ядро ​​проверяет этот список, и точка 4 применяется снова.

Надеюсь, это поможет. Я думаю, что примеры лучше всего использовать.