Использование sig_atomic_t в функции маски сигнала linux - PullRequest
2 голосов
/ 08 октября 2010

Я недавно изучал книгу под названием «Расширенное программирование в Linux» и столкнулся с таким вопросом: в книге сказано, что вы должны использовать переменную типа sig_atomic_t, чтобы быть уверенным, что если вы установите глобальный флаг или счетчик в функции обработчика сигнала, то контекстПереключение между арифметическими операциями (т.е. ++) и их сохранением в регистр не происходит.

Мой вопрос: что может произойти, если мы не используем sig_atomic_t и просто используем другой тип и контекстпроисходит переключение?Я имею в виду, что программа просто вернется и сохранит ее, например, позже.Может кто-нибудь дать мне сценарий, который сделает наш код нестабильным или глючным?

Ответы [ 3 ]

2 голосов
/ 08 октября 2010

Вот пример, который приводит к небезопасному поведению:

int64_t a = 2^32-1;

void some_signal_handler()
{
   ++a;
}

void f()
{
  if( a == 0 )
    printf("a is zero");
}

Предполагается 32-битная архитектура.Переменная a в действительности сохраняется как 2 32-битные целые числа и начинается как {0,2 ^ 32-1}.Сначала f читает верхнюю половину a как 0. Затем происходит сигнал, и выполнение переключается на обработчик сигнала.Увеличивает a с 2 ^ 32-1 до 2 ^ 32 a, новое значение равно {1,0}.Обработчик сигнала завершается, и выполнение f продолжается.f читает нижнюю половину a как 0. В целом f прочитал a как ноль, который никогда не был предназначен.

2 голосов
/ 08 октября 2010

Риск, который вы запускаете в описываемом вами сценарии (чтение из памяти для регистрации, обновление регистра, запись в память и переключение контекста между этими операциями), заключается в том, что вы можете потерять обновление, выполненное в другом контексте. 1001 *

Например:

main context:
  read i (=10) from memory to register R1
  add 5 to R1
    <interrupt. Switch to interrupt context>
    read i (=10) from memory to register R1
    add 10 to R1
    write R1 to i in memory (i = 20)
    <end of interrupt. Back to main context>
  write R1 to i in memory (i = 15)

Как видите, обновление от прерывания потеряно.

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

Например:

main context:
  read first half of i (=10) from memory to register R1
  read second half of i (=10) from memory to register R2
  add 5 to R1/R2 pair
  write R1 to first half of i in memory
    <interrupt. Switch to interrupt context>
    read first half of i (= ??) from memory to register R1
    read second half of i (= ??) from memory to register R2
    add 10 to R1/R2 pair
    write R1 to first half of i in memory
    write R2 to second half of i in memory
    <end of interrupt. Back to main context>
  write R2 to second half of i in memory

Здесь нет информации о том, какое значение я получу.

При sig_atomic_t эта вторая проблема не может возникнуть, потому что тип гарантированно использует атомарные операции чтения / записи.

1 голос
/ 08 октября 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...