Реализация отменяемых системных вызовов в пространстве пользователя

Я работаю над реализацией отмены pthread в Linux без какого-либо «неприятного поведения» (некоторые могут сказать об ошибках), обсуждаемого в некоторых из моих недавних вопросов. Подход Linux / glibc к отмене pthread до сих пор заключался в том, чтобы рассматривать его как нечто, не нуждающееся в поддержке ядра, и это может быть обработано на уровне библиотеки просто путем включения асинхронной отмены перед выполнением системного вызова и восстановления предыдущего состояния отмены после возврата системного вызова. У этого есть как минимум 2 проблемы, одна из которых чрезвычайно серьезная:

  1. Отмена может действовать после того, как системный вызов вернулся из пространства ядра, но до того, как пользовательское пространство сохранит возвращаемое значение. Это приводит к утечке ресурсов, если системный вызов выделил ресурс, и нет способа исправить его с помощью обработчиков отмены.
  2. Если сигнал обрабатывается, пока поток заблокирован отменяемым системным вызовом, весь обработчик сигнала работает с асинхронная отмена включена. Это может быть чрезвычайно опасно, поскольку обработчик сигналов может вызывать функции, которые безопасны для асинхронных сигналов, но не безопасны для асинхронных отмен.

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

Я ищу новый подход и ищу отзывы о нем. Условия, которые должны быть выполнены:

  • Любой запрос отмены, полученный до завершения системного вызова, должен быть обработан до того, как системный вызов заблокируется в течение любого значительного интервала времени, но не во время ожидания перезапуска из-за прерывания обработчиком сигнала.
  • Любой запрос отмены, полученный после завершения системного вызова, должен быть отложен до следующей точки отмены.

Идея, которую я имею в виду, требует специальной сборки для оболочки отменяемого системного вызова. Основная идея была бы следующей:

  1. Поместить адрес предстоящей инструкции системного вызова в стек.
  2. Сохранить указатель стека в локальном хранилище потока.
  3. Проверить флаг отмены из локального хранилища потока; перейти, чтобы отменить подпрограмму, если она установлена.
  4. Выполните системный вызов.
  5. Очистите указатель, сохраненный в локальном хранилище потока.

Операция отмены будет включать:

  1. Установка флага отмены в целевом потоке локальное хранилище потока.
  2. Проверить указатель в локальном хранилище потока целевого потока; если он не равен нулю, отправить сигнал отмены в целевой поток.

Обработчик сигнала отмены должен:

  1. Проверить, что сохраненный указатель стека (в контексте сигнала) равен сохраненному указателю в локальном потоке место хранения. Если нет, то точка отмены была прервана обработчиком сигнала, и сейчас делать нечего.
  2. Убедитесь, что регистр программного счетчика (сохраненный в контексте сигнала) меньше или равен адресу, сохраненному в сохраненном стеке указатель. Если так, это означает, что системный вызов еще не завершен, и мы выполняем отмену.

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

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

Есть какие-нибудь мысли о том, какой подход лучше всего, или есть ли другие более фундаментальные недостатки, которые мне не хватает?

9
задан R.. 15 April 2011 в 16:20
поделиться