Есть ли способ выполнить обратный вызов (в Linux), когда дескриптор файла закрыт

Я работаю над библиотекой эмуляции kevent/kqueue для Linux. Я новый сопровождающий в этом проекте, и, к сожалению, предыдущий сопровождающий больше не вовлечен (поэтому я не могу разобраться с этим).

Под FreeBSD и macOS, когда вы close() дескриптор файла, предоставленный kqeueue() Вы освобождаете любые ресурсы и события, связанные с ним.

Кажется, что существующий код не предоставляет подобный интерфейс. Прежде чем добавить функцию в API (или восстановить старую) для явного освобождения ресурсов kqueue, мне было интересно, есть ли способ связать триггеры с дескриптором файла в linux, чтобы при его закрытии мы могли очистить все, что связано с ФД.

Сам файловый дескриптор может быть любого типа, то есть того, который предоставлен eventfd, или epoll, или чего-либо еще, что создает файловые дескрипторы.

1 ответ

Когда последний дескриптор файла записи из вызова pipe() закрывается, epoll()/poll() официанты увидят событие [E]POLLHUP на всех открытых дескрипторах файла чтения. Предположительно, то же самое верно для любого fd, который представляет соединение, а не состояние.

Решение этой проблемы довольно простое, хотя и немного раздражает в реализации. Он опирается наfcntl называется F_SETSIG, чтобы указать сигнал, используемый для передачи изменений состояния FD, и fcntl называется F_SETOWN_EX чтобы указать, в какой поток должен быть доставлен сигнал.

Когда приложение запускается, оно порождает отдельный поток мониторинга. Этот поток используется для приема сигналов, генерируемых FD.

В нашем конкретном случае использования поток мониторинга должен запускаться неявно при первом создании отслеживаемого FD и уничтожаться без явного соединения. Это потому, что мы эмулируем FreeBSD API (kqueue), в котором нет явных функций инициализации и деиницирования.

Поток мониторинга:

  1. Слушает сигнал, который мы передалиF_SETSIG.
  2. Получает идентификатор потока и сохраняет его в глобальном.
  3. Сообщает приложению, что поток мониторинга запущен (и глобальное пространство заполнено), используяpthread_cond_broadcast.
  4. Звонки pthread_detach чтобы убедиться, что он очищен правильно без необходимости другого потока выполнять явное pthread_join.
  5. Звонки sigwaitinfo ждать доставки сигнала.

Поток (и) приложения:

  1. Использует pthread_once для запуска потока мониторинга при первом создании FD, затем ожидает полного запуска потока мониторинга.
  2. Использует F_SETSIG чтобы указать сигнал, отправляемый, когда FD открыт / закрыт, иF_SETOWN_EX чтобы направить эти сигналы в поток мониторинга.

Когда контролируемый ФД закрыт, sigwaitinfo вызов в потоке мониторинга возвращается. В нашем случае мы используем конвейер для представления kqueue, поэтому нам необходимо сопоставить FD, для которого мы получили сигнал, с тем, который связан с ресурсами (kqueues), которые нам нужно освободить. Как только это сопоставление будет выполнено, мы можем (см. Ниже для получения дополнительной информации) очистить ресурсы, связанные с парой FD, и вызватьsigwaitinfo снова дождаться новых сигналов.

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

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

В случае, когда сигнал не доставляется до повторного использования FD при создании новой пары канал / ресурс, счетчик ссылок для этого конкретного FD будет> 0. Когда это произойдет, мы немедленно освобождаем ресурс и повторно инициализируем его, увеличивая ссылку считать. Когда доставляется сигнал, указывающий, что FD был закрыт, счетчик ссылок уменьшается (но не до нуля), и ресурс не освобождается.

В качестве альтернативы, если сигнал доставляется до того, как FD был повторно использован, поток мониторинга уменьшит счетчик ссылок до нуля и немедленно освободит связанные ресурсы.

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

Примечание. Наша реализация отличается от описанной выше (в частности, мы не проверяем счетчик ссылок FD при создании нового сопоставления FD/ ресурсов). Я думаю, это потому, что мы полагаемся на тот факт, что закрытие одного из каналов не обязательно приводит к закрытию другого конца, поэтому FD открытого конца не доступен для повторного использования немедленно. К сожалению, разработчик, написавший код, недоступен для запроса.

Другие вопросы по тегам