2015-01-18 2 views
1

Я пытаюсь исправить ошибку в цикле событий, который вызывает select(). Когда select() возвращает EBADF, регистрируется ошибка, тогда набор fd повторно инициализируется и выбор вызывается снова. Это приводит к бесконечному жесткому циклу регистрации, генерируя гигабайты журнала в считанные секунды.Должен ли я утверждать сбой при выборе() EBADF?

Эта ошибка возникает, если один из серверов tcp, к которому подключена моя программа, выполняет нечистое отсоединение (например, segfaults). В этом случае я бы идеально хотел, чтобы моя программа удаляла этот fd и продолжала работать (или отключается, если это невозможно).

Мой вопрос в том, должен ли() когда-либо возвращаться EBADF, или это указание на то, что моя программа глючит? То есть Должен ли я утверждать, что я не согласен с EBADF, или каким-то другим образом, как мне обращаться с ним? Пропустил бы ли я цикл fd, чтобы найти «плохой» дескриптор файла?

+2

Это результат неаккуратного администрирования fd. В качестве последнего средства * вы можете вызвать 'fstat()' на всех fds в своих fd_sets и отсортировать их. – wildplasser

ответ

4

У вас есть ошибка в коде. Почини это. Где-то вы закрываете сокет, не удаляя его из набора FD, используемого селектором. Или вы только что составили FD, который не является FD и использует его в наборе FD.

В отличие от других утверждений, сетевые проблемы не могут вызвать эту ошибку. Неисправности сети не закрывают сокеты, и это единственный способ стать недействительными. Это только закрытие. Сокет, соединение которого не работает, в конечном итоге вызовет ECONNRESET, если вы сохраните его. Сокет, чей одноранговый узел отсоединился, станет читаемым, и recv() на нем вернет нуль.

+0

wallyk указывает на отсоединение сетевого соединения, к которому привязан сокет, может вызвать этот сценарий. Я тоже пытаюсь доказать это неправильно. –

+2

@ JonathonReinhart. Это не так. Сокет остается открытым, и вы можете называть 'send()' на нем, не получая EBADF. TCP не волшебным образом закрывает сокеты из-под вас. – EJP

+1

Я не подозревал столько, но я хотел это подтвердить.В самом деле, я использовал SO_BINDTODEVICE для привязки сокета к USB-адаптеру USB и отключил его (от USB-порта) перед вызовом 'select', который успешно вернулся. –

2

Да, я думаю, это было бы неплохо для assert().

На странице select(2) людей:

EBADF недопустимый дескриптор файла был дан в одном из множеств. (Возможно, дескриптор файла, который уже был закрыт, или один на , которая произошла ошибка.)

Это означает, что вы прошли дескриптор файла, который на самом деле не совпадает действующий открытый файл.

Примеры сценариев, которые не вызвать FD, чтобы сделать select неудачно с EBADF:

  • Удаленный сокет закрытия соединения. (recv возвращает 0).
  • Отключение сетевого подключения (или даже USB-устройства).

Глядя на исходный код ядра Linux, мы видим, что select может вернуться EBADF, если будет установлено, что один из FDS, переданных в вашем наборе не соответствует открытому файлу в вашем процессе. Это проверяется в max_select_fd в fs/select.c

После этой проверки do_select всегда будет возвращать счетчик «интересных» фс. Функция file_operations.poll для базового файла даже не способна сделать do_select вернуть что-то другое.

Теперь выглядит полностью невозможно, что select вернется EBADF для любого сценария, кроме программирования пропускания FD, которая была либо закрыта, либо никогда не откроется.

+0

да, но может ли это произойти, например, если сервер неожиданно закрыл сокет? т. е. это то, что я должен учитывать (т. е. обрабатывать, а не утверждать), или это может произойти только в том случае, если у MY-программы есть ошибка? – grasevski

+0

@grasevski Я не думаю, что это может произойти, если удаленный конец закрывает сокет. Но я кодирую пример для проверки этой теории. –

+0

[Этот ответ] (http://stackoverflow.com/questions/883282) подозревает, что вы не 'FD_ZERO' your' fd_set' –

3

Мой вопрос в том, должен ли() когда-либо возвращаться EBADF, или это указывает на то, что моя программа глючит?

select() возвращает EBADF, если вы передадите дескриптор в одном из недопустимых fd_set.Вы не должны этого делать, и это указывает на ошибку в вашей программе - возможно, вы закрыли(), где-то в файловом дескрипторе, но не удалили ее из fd_set.

+0

Теперь я смущен. ваш ответ конфликтует с ответом @wallyk. Также я думаю, что на самом деле сервер закрывает(), и это может вызвать EBADF, а не я. – grasevski

+1

@grasevski Ответственная вещь, которую нужно сделать, - это проверить этот сценарий. Я не думаю, что так бывает. –

+1

@grasevski: любые сбои в сети ** или ** плохое программирование могут вызвать эту ошибку. – wallyk

Смежные вопросы