О качественных граблях: select()
02.09.2009 21:04:39Написал я тут хороший (но очень секретный!) сервер со страшной многопоточностью и прицелом на большое число клиентов. И всё бы хорошо, да вот незадача — валится он странным образом при наваливании всё большего числа тех самых клиентов. Как оказалось, наткнулся я на достаточно известные (если знать что искать), но очень, очень качественные грабельки, так что грех не поделиться.
Суть проста как апельсин. Есть select, который смотрит за кучкой файловых дескрипторов и возвращается когда из них можно почитать, в них можно записать или тому подобное. В мануале описано, что select-у передаётся fd_set, набор дескрипторов, за которыми надо смотреть, и номер самого большого дескриптора плюс один. Последнее насторожило сразу, но должного значения не было придано.
Если порыть ещё чуть-чуть, можно во многих источниках популярно для себя пояснить, что у select-а есть ограничение по количеству дескрипторов, за которым он призван смотреть. Ограничение порядка 1024, чего, для моих применений, выше крыши, у меня же два штука этих дескрипторов по максимуму.
Однако, сервер падает. И попытки выяснить происходящее вывели на прекрасное: select возвращал единичку, но ни один из передаваемых дескрипторов не был установлен. А потом начинал возвращать больше единички. А потом мог и ошибку возвратить. Ну а потом и вовсе падал весь сервер.
В чём же дело? А в том, что неспроста select-у отдаётся номер максимального дескриптора плюс один. В том, что вся структура fd_set является битовой маской. И, самое главное, то, что эта битовая маска читается очень просто — номер бита соответствует номеру дескриптора.
А из последнего, к сожалению, следует мало приятный вывод — не количество просматриваемых дескрипторов ограничено, а качество. Если номер дескриптора вылазит за границы маски (а у меня это поле было ограничено 1024 битами), его нельзя с помощью select просмотреть вообще. Ну а всякие FD_SET начинают писать куда попало, select находит то, чего не может быть, и происходят прочие чудеса.
Мораль? Считаю, что за такие фокусы использование select должно быть запрещено напрочь. Ну и, конечно, после переписывания на использование poll сервер заработал как часы. Чего всем и желаю.
P.S. Ещё, кстати, полезно иметь ввиду, что количество доступных пользователю дескрипторов чаще всего лимитировано именно на уровне 1024, так что ‘ulimit -n‘ бывает очень полезен.