Реализация отменяемых системных вызовов в пользовательском пространстве - PullRequest
9 голосов
/ 16 апреля 2011

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1 Ответ

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

Решение 2 похоже на меньшее взломать. Я не думаю, что это вызовет проблему, которую вы предлагаете, потому что отменяемые системные вызовы, вызываемые в обработчике системных вызовов, будут проверять флаг отмены в TLS, который, должно быть, уже установлен, если обработчик сигнала отмены запущен и все равно связан с маской сигналов.

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

...