Как быть уверенным, что файловый дескриптор уже закрыт? - PullRequest
1 голос
/ 18 апреля 2011

Перед голосованием, чтобы закрыть, пожалуйста, прочитайте, я знаю, что есть похожие вопросы (:

Вот моя ситуация - у меня есть приложение, которое является многопоточным. Итак, допустим, у меня есть 10 потоков. Все они читаются из одного файлового дескриптора (на самом деле это сокет). И в очень редкой ситуации, когда возникает критическая ошибка, сокет должен быть shutdown одним из потоков. Дело в том, что любой из этих потоков может это сделать. Если закрытие сокета завершилось неудачно, _Exit( FAILURE ) выполняется (я знаю, что это звучит как ужасный дизайн или проблема в коде, но на самом деле это не так, так как это вызвано сторонним lib. , что есть ошибка).

А вот проблемная ситуация - все они могут попробовать shutdown сокет одновременно. И один закрывает его, но другие не могут его закрыть (shutdown возвращает -1, так как сокет уже закрыт), а плохой _Exit( FAILURE ) выполняется, и это все разрушает.

Очевидно, мне нужна дополнительная проверка - если сокет уже закрыт (возможно, все потоки не смогли закрыть сокет по какой-то причине, и тогда хотя бы один из них должен выполнить _Exit, поэтому проверяет код возврата shutdown недостаточно).

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

И вот вопрос - как я могу изменить ситуацию, если сокет уже закрыт или по какой-то причине не может быть закрыт? Будет ли fcntl убедить меня, что если один поток закрыл сокет и в то же время, если другой поток попытается shutdown сокет, произойдет сбой, и затем, если я выполню эту проверку (с fcntl), это будет работать для меня?

Я также видел другие ответы, такие как: «вы можете использовать select или poll», но они все еще являются системными вызовами, и я не знаю, будут ли они лучшим выбором. Я также не знаю, как именно их использовать, но это не имеет большого значения, я думаю.

Спасибо!


Я также могу проверить errno, установленный shutdown, но что значит «подключен»? И в чем разница между «подключен» и «не допустимый дескриптор»

ENOTCONN
    The specified socket is not connected.

Кроме того, меня беспокоит то, что FD, который я пытаюсь закрыть, может быть недействительным, поскольку я беру его из /proc/net/tcp, сопоставленного с proc/PID/fd, и я не знаю, будут ли все файлы выглядеть кстати, они выглядят на моей ОС (ОС будет наверняка RHEL4 или RHEL5, если это имеет значение)

Doh! Это чертовски долго, но я не могу объяснить это короче.

Ответы [ 5 ]

4 голосов
/ 18 апреля 2011

Я полагаю, вы говорите, что ваше приложение может продолжить работу после завершения работы сокета?

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

3 голосов
/ 19 апреля 2011

Всякий раз, когда у вас есть ресурс, который используется более чем одним потоком и который может быть освобожден одним из них, вы должны защитить весь доступ с помощью блокировок.В противном случае у вас будут опасные условия гонки.Я бы использовал блокировку чтения-записи на int, содержащем дескриптор файла.Любой поток, желающий использовать fd, должен удерживать блокировку чтения на время, в течение которого он его использует, а любой поток, желающий изменить переменную fd (например, close и заменить ее -1, чтобы предотвратить дальнейшее использование), должен удерживать блокировку записи..

По сути это то же самое, что использование динамически выделяемой памяти и free.

.
1 голос
/ 18 апреля 2011

Проверка errno, безусловно, ваш лучший вариант. С shutdown(2) я вижу:

  • EBADF s не является допустимым дескриптором
  • ENOTCONN Указанный разъем не подключен
  • ENOTSOCK s - это файл, а не сокет.

То, как я это вижу: EBADF означает, что оно уже закрыто, а ENOCONN означает, что за этим дескриптором нет связи (не трехстороннее рукопожатие и весь этот джаз).

Лучший способ выяснить: сделайте perror(3) после того, как вызов shutdown не удастся, и посмотрите, что он говорит.

Приветствия

0 голосов
/ 25 ноября 2012

Правильный ответ был R .., который имел дело с проблемами многопоточности. Ответ на вопрос «как узнать статус fd?»: fstat()

За POSIX: Он вернет -1 при любой проблеме с файловым дескриптором или сокетом. errno будет установлен на EBADF в дескрипторе файла или сокете, который уже закрыт. Семейство вызовов stat специально предназначено для тестирования: файлы (stat), ссылки (lstat) и файловые дескрипторы (fstat).

Другие вызовы, такие как close и shutdown, также возвращают ошибки на уже закрытых файлах. Но их цель в другом, и тестирование подключенного сокета является побочным эффектом.

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

0 голосов
/ 18 апреля 2011

Вы думали о защите закрытия сокета с помощью мьютекса?Используя один из них, вы можете убедиться, что только один поток пытается закрыть сокет вообще.Вам придется сделать несколько системных вызовов, а именно pthread_mutex_init при инициализации и pthread_mutex_trylock перед тем, как фактически закрыть сокет.Однако эти вызовы должны быть оптимизированы для быстрого возврата.

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

...