Как работат select()?
Я бы хотел узнать, что в нем происходит. Видел исходники, но разобраться в них мне крайне сложно. Просто суть хочу узнать. Есть набор файловых дескрипторов, пусть мы их все проверяем на готовность к чтению. У select есть бесконечный цикл, там, он, по всей видимости, пробегается по всем дескрипторам и... И что дальше? какую информацию в себе хранит файловый дескриптор, что ему говорит, что файл готов к чтению?
https://elixir.bootlin.com/linux/latest/source/fs/select.c
Ответы (3 шт):
Вызывая select, Вы говорите операционной системе - вот эти сокеты мне интересны, на этих факт чтения, на этих - запись. Операционная система сама решает, когда произошло запрашиваемое событие и отдает результат. То есть, с точки зрения приложение, это блокирующий запрос, с точки зрения операционной системы - это просто запрос, который поставили в очередь.
То есть, как именно внутри реализован select - это детали конкретной операционной системы (скажу больше в windows и linux они только визуально похожи, а внутри совершенно по разному устроены).
В приведенных сорцах просто заполняют другую структуру, более удобную ядру и отдают ему. Если я не ошибаюсь, то похоже там используют другую функцию с такой же функциональностью - poll.
Что же такое "файловый дескриптор"? считайте это просто неким номерком, по которому операционная система внутри себя сопоставляет с реальным объектом.
У select есть бесконечный цикл
В ядре ОС нет никакого цикла. Иначе бы Ваш вызов select подвесил всю систему. Всё построено на прерываниях.
какую информацию в себе хранит файловый дескриптор
Это совершенно не важно. Тем боле, что в разных ОС - разную. Просто с точки зрения ОС, ваш сокет - это почти обычный файл. А все файлы описываются соответствующими дескрипторами и имеют свой номер в таблице дескрипторов.
Вызывая select() Вы просто говорите ОС, что вы ожидаете прихода прерывания от любого из перечисленных в наборе сокетов (файлов).
что ему говорит, что файл готов к чтению?
Предположим, что Вы пытаетесь отправить на печать строку текста. А принтер не готов. Что произойдёт? Функция вывода (например - printf()) повиснет на ожидании готовности. Вы нажимаете на принтере кнопочку "Вкл", принтер начинавет работать и посылает прерывание ОС. Ядро ОС забирает буфер из printf() и отправляет его на устройство.
Аналогично происходит и с сокетами. Передавая select() наборы сокетов, Вы говорите ОС, что Вас интересует возможность прочитать или записать данные в сокеты, которые заданы их парами IP/Port. Когда драйвер сетевой карточки получает пакет данных с соответствующей парой IP/Port он посылает прерывание сетевой подсистеме ядра ОС. Та снимает с ожидания Вашу программу и сообщает ей, какой конкретно сокет готов к передаче данных. Вы их забираете, обрабатывает и опять повисаете на srlrct()...
Если разобрать фунцию do_select(), то можно выяснить следующее.
В структуре связанной с файловым дескриптором (продолжение тут и тут) есть указатель на специфичную для каждого типа файловых дестрипторов (сокет, терминал, plain file и т.д.) функцию poll, которая вызывается вот тут mask = vfs_poll(f.file, wait); в "бесконечном цикле" в do_select().
Именно она определяет, есть ли готовые для чтения данные или готов ли дескриптор в данный момент для записи данных или есть ли в данный момент прочие события, связанные с дескриптором.
Если такие (заданные в передаваемой в select маске) дескрипторы обнаружены, то мы возвращаемся (также возвращаемся при получении сигнала и истечении таймаута). Если готовых дескрипторов нет, то ожидаем, передавая управление в scheduler, тем самым возобновляя какой-то из других процессов OS, готовых к исполнению.