LINUX.ORG.RU

[c/c++] Размышления про close()


1

2

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

Я тут смотрю многие вообще не проверяют возврат функции close().

Интересует:

1. Насколько это безопасно? Мне кажется, что очень небезопасно.

2. Кто? как? работает с close()? Если писать явно код обработки возвращаемого значения для каждого close(), то получится очень громоздко. Может у кого-нибудь есть красивые решения?

1. небезопасно только на третьем уровне. на первом и втором - самое оно.
2. у меня нет.

//хороший, годный вброс.

mi_estas
()

Всегда проверяю, что возвращает close.

Не проверять значение, возвращаемое функцией close - обычная, но от этого не менее серьезная ошибка программирования. Вполне возможно, что ошибка в предыдущей операции write(2) впервые даст о себе знать при выполнении close. Отсутствие проверки возвращаемого значение при закрытии файла, может привести к потере данных. Особенно это касается таких аспектов как NFS и дисковые квоты.

rg-400
()
Ответ на: комментарий от Boy_from_Jungle

просто closе, если несколько потоков, то дождаться их а после закрывать.

Т. е. в однопоточных приложениях результат выполнения close() ты вообще не проверяешь?

pathfinder ★★★★
() автор топика
Ответ на: комментарий от rg-400

Всегда проверяю, что возвращает close.

Терпишь громоздкость в своем коде, или у тебя есть какие-то фокусы?

pathfinder ★★★★
() автор топика
Ответ на: комментарий от mi_estas

1. небезопасно только на третьем уровне. на первом и втором - самое оно.

Не разпарсил. О каких уровнях идет речь?

//Так и хочется сказать: «Я программист 80-го уровня.» :)

pathfinder ★★★★
() автор топика

>Может у кого-нибудь есть красивые решения?

Юзать более высокоуровневые абстракции. Раз уж в заголовке упомятут С++, я как бы намеку на fstream и asio

yoghurt ★★★★★
()
Ответ на: комментарий от pathfinder

да понятие такое есть «ожидаемое качество кода». почитай если интерестно. для задачки, которую запустят один раз нет смысла проверять. А вот для драйвера например - есть.

mi_estas
()

Зачем проверять, как завершился close? Если тебе нужно, чтобы данные гарантированно легли на блины, используй fsync/fdatasync.

tailgunner ★★★★★
()
Ответ на: комментарий от pathfinder

нет не проверяю, если у тебя мега работата с файлами чтение-запись можешь в конце вызвать (f)sync.

Boy_from_Jungle ★★★★
()

Человек описал вопрос как-то абстрактно, поэтому ответы очевидны.

Boy_from_Jungle ★★★★
()
Ответ на: комментарий от yoghurt

Допустим, close вернул ошибку. Т.е. файловый дескриптор остался занятым. Вопрос: как действовать в этом случае?

Eddy_Em ☆☆☆☆☆
()

1) Конечно не безопасно.
Последствия для примитивной программы и демона работающего 24/7 конечно разные - в первом случае это не очень критично, а во втором есть риск что кончатся дескрипторы и тогда демон не сможет делать практически ничего полезного.
2) Вариантов тут не так много и все опять же зависит от контекста.
Просто перезапускать вызов при получении EINTR не получится, потому что повторный вызов может тоже вернуть EINTR. В этом случае нужен цикл с ожиданием между попытками. Опять же что делать если после N попыток все еще возвращается EINTR, то есть для программы ошибка больше не soft, а hard.
Здесь уже классическая проблема о том как обрабатывать ошибки. Все опять же зависит от контекста и предложить можно:
1) Завершать приложение
2) Выводить сообщение в лог, но продолжать на свой страх и риск
3) Кидать exception если код вызывающий close не знает как обработать ошибку, а код на уровне выше знает.

Если особо не заморачиваться то можно использовать функцию с таким прототипом (она может быть в библиотеке):
do_close(fd, retires, timeout, hard_error_handling_policy)
Первые три параметра очевидны, а 4-й может задавать политику обработки hard ошибок.

Krivenok_Dmitry
()
Ответ на: комментарий от yoghurt

>> Зачем проверять, как завершился close?

Чтобы из лимита открытых дескрипторов не вылезти, не?

Ну, разве что. А есть вообще программы, интенсивно открывающие/закрывающие файлы под потоком сигналов?

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Ну, разве что. А есть вообще программы, интенсивно открывающие/закрывающие файлы под потоком сигналов?

Не, ну уж в такой мелочи можно позволить себе писать вместо «почти корректного» - «корректный» код.

pathfinder ★★★★
() автор топика
Ответ на: комментарий от Krivenok_Dmitry

Опять же что делать если после N попыток все еще возвращается EINTR

А я не вижу ничего плохого в бесконечном цикле при EINTR. Вот при других ошибках надо однозначно выходить из цикла и что-то делать.

pathfinder ★★★★
() автор топика
Ответ на: комментарий от pathfinder

> Не, ну уж в такой мелочи можно позволить себе писать вместо «почти корректного» - «корректный» код.

Конечно. Но меня интересует другой вопрос... например, сколько раз срабатывала твоя try_close?

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Конечно. Но меня интересует другой вопрос... например, сколько раз срабатывала твоя try_close?

Подозреваю, что за все время работы софта - ни разу.

pathfinder ★★★★
() автор топика
Ответ на: комментарий от mi_estas

небезопасно только на третьем уровне. на первом и втором - самое оно.

Какие еще уровни?

bk_ ★★
()
Ответ на: комментарий от tailgunner

Мне в голову пришёл только GNU Smalltalk. Блокирующие сокеты в нём реализованы через SIGIO/SIGPOLL, которые приходят на каждую активность, и в обработчике поднимают соответствующий спящий на сокете зелёный тред. Поэтому сферический нагруженный веб сервер/сервис на GSt есть вполне себе пример. :)

yoghurt ★★★★★
()
Ответ на: комментарий от Eddy_Em

>Вопрос: как действовать в этом случае?

Действовать, как настоящий мужик - рестартовать процесс в надежде на светлое будущее :)))

yoghurt ★★★★★
()
Ответ на: комментарий от pathfinder

А я не вижу ничего плохого в бесконечном цикле при EINTR. Вот при других ошибках надо однозначно выходить из цикла и что-то делать.


При других ошибках в цикл можно и не входить :)

Все зависит от архитектуры программы - представь что ты пишешь однопоточный event-driven сервер. Тогда бесконечный цикл это чистый DoS.
Если же сервер многопоточный, то один поток может висеть в бесконечно цикле пытаясь корректно закрыть дескриптор. Но и в этом случае сервер будет работать недолго, так как если потоки не будут завершаться (или возвращаться обратно в пул если это pre-threading), то в итоге получим тот же DoS.

Krivenok_Dmitry
()
Ответ на: комментарий от tailgunner

И что? Интерпретатор ведь тоже человекнаписан на Си.

yoghurt ★★★★★
()
Ответ на: комментарий от Krivenok_Dmitry

Все зависит от архитектуры программы - представь что ты пишешь однопоточный event-driven сервер. Тогда бесконечный цикл это чистый DoS.

Я по большей части пишу именно однопоточный event-driven софт. Если будет идти сплошной поток сигналов, то какая разница, программа жрет 100% CPU в цикле с close() или в цикле с epoll()?

Сам close() я считаю «быстрой» операцией, это тебе не какой-нибудь блокирующий read().

pathfinder ★★★★
() автор топика
Ответ на: комментарий от AptGet

SA_RESTART[/thread]

К close() это не относится, но:

On Mon, 11 Jul 2005, Theodore Ts'o wrote:
> 
> According to the Single Unix Specification V3, all functions that
> return EINTR are supposed to restart if a process receives a signal
> where signal handler has been installed with the SA_RESTART flag.  

That can't be right.
...
(bla-bla-bla)
...
		Linus

Хе-хе-хе. У любого правила должны быть исключения, чтоб жизнь мёдом не казалась.

pathfinder ★★★★
() автор топика
Ответ на: комментарий от pathfinder

Не перезапускается nanosleep(), и т.п., pause() и т.п., select(), poll() и еще несколько, те что работают с сокетами.

AptGet ★★★
()
Ответ на: комментарий от pathfinder

Я по большей части пишу именно однопоточный event-driven софт. Если будет идти сплошной поток сигналов, то какая разница, программа жрет 100% CPU в цикле с close() или в цикле с epoll()?

Похоже мы говорим о разных вещах. Я имел вииду что do_close реализована так (если с бесконечным циклом):

do_close(fd, timeout)
{
  do
    {
      r = close(fd);
      if(r!=-1)
        return; // Close succeeded
      else if(errno != EINTR)
        handle_hard_error();
      else
        sleep(timeout);
    }
  while(1);
}
То есть не busy wait, а с вызовом блокирующего sleep. Если sleep убрать, то да, будет жрать CPU.

Но в любом случае ты epoll_wait вызвать просто не сможешь.

Или ты имеешь ввиду возможность проверки close через общий event loop программы? :))

Krivenok_Dmitry
()
Ответ на: комментарий от Krivenok_Dmitry

Если sleep убрать, то да, будет жрать CPU.

ИМХО sleep НАДО убрать. Не понимаю, почему неудачно выскочивший сигнал должен вызывать секундные лаги в программе на пустом месте. А цикл можно сделать неограниченным. Ограниченное количество перезапусков ничего принципиально- полезного не принесет. Если и возникнет этот самый сплошной поток сигналов, то он положит на лопатки приложение вне зависимости от способа работы с close(). Тот же epoll_wait в цикле уязвим точно так же, как и close в цикле.

pathfinder ★★★★
() автор топика

в венде так

#ifdef NDEBUG
# define VERIFY(e) ((void)(e))
#else
# define VERIFY(e) assert(e)
#endif

в отладочном режиме вылетает ассерт а в релизном обработки ошибок нет обычно этого хватает чтоб отловить всякие косяки

VERIFY(close(fd) != bad);

anonymous
()

Ну у вас тут и цирк.

Я у себя использую заглушку try_close() которая перезапускает close() в случае EINTR


man _GNU_SOURCE

1. Насколько это безопасно?


Для Linux - 100%. Инфа - 100%.

LamerOk ★★★★★
()
Ответ на: комментарий от LamerOk

VERIFY применяется только к CloseHandle,close, etc... а для обычных функций есть проверка и в релизе

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.