Проблема
Гонка данных на смежных битовых полях происходит когда:
Несколько задач выполняют незащищенные операции на битовых полях, которые являются частью той же структуры.
Например, задача работает с полем errorFlag1
и другая задача на поле errorFlag2
в переменной этого типа:
struct errorFlags {
unsigned int errorFlag1 : 1;
unsigned int errorFlag2 : 1;
...
}
Предположим, что операции не являются атомарными друг относительно друга. Другими словами, вы не реализовали механизмы защиты, чтобы гарантировать, что одна операция завершается, прежде чем другой начинает.По крайней мере одна из незащищенных операций является операцией записи.
Чтобы найти этот дефект, необходимо задать многозадачные опции перед анализом. Чтобы задать эти опции, на панели Configuration, выбирают Multitasking. Для получения дополнительной информации смотрите Анализ Многозадачности Polyspace Конфигурирования Вручную.
Риск
Смежные битовые поля, которые являются частью той же структуры, могут храниться в одном байте в той же ячейке памяти. Операции чтения или операции записи на всех переменных включая битовые поля происходят один байт или слово за один раз. Чтобы изменить только определенные биты в байте, шаги, похожие на это, происходят в последовательности:
Байт загружается в RAM.
Маска создается так, чтобы только определенные биты были бы изменены к намеченному значению, и остающиеся биты остаются неизменными.
Операция битового "ИЛИ" выполняется между копией байта в RAM и маской.
Байт с определенными измененными битами копируется назад с RAM.
Если к двум различным битовым полям получают доступ, эти четыре шага должны быть выполнены для каждого битового поля. Если доступы не защищены, все четыре шага для одного битового поля не могут завершиться перед четырьмя шагами для другого начинающиеся. В результате модификация одного битового поля может отменить модификацию смежного битового поля. Например, модификация errorFlag1
и errorFlag2
может произойти в следующей последовательности. Шаги отметили 1, относятся к модификации errorFlag1
и шаги отметили 2, относятся к тому из errorFlag2
.
1a. Байт с обоими errorFlag1
и errorFlag2
немодифицированный копируется в RAM, в целях изменить errorFlag1
.
1b. Маска, которая изменяет только errorFlag1
редактор битового "ИЛИ" с этой копией.
2a. Байт, содержащий оба errorFlag1
и errorFlag2
немодифицированный копируется в RAM во второй раз, в целях изменить errorFlag2
.
2b. Маска, которая изменяет только errorFlag2
редактор битового "ИЛИ" с этой второй копией.
1c. Версия с errorFlag1
измененный копируется назад. Эта версия имеет errorFlag2
немодифицированный.
2c версия с errorFlag2
измененный копируется назад. Эта версия имеет errorFlag1
немодифицированный и перезаписи предыдущая модификация.
Фиксация
Чтобы зафиксировать этот дефект, защитите операции на битовых полях, которые являются частью той же структуры с помощью критических разделов, временного исключения, или другой означает. Смотрите Меры защиты для Совместно используемых переменных в Многозадачном Коде.
Чтобы идентифицировать существующие меры защиты, которые можно снова использовать, см. таблицу и графики, сопоставленные результатом. Таблица показывает каждую пару конфликтных вызовов. Столбец Access Protections показывает существующие меры защиты на вызовах. Чтобы видеть, что последовательность вызова функции ведет к конфликтам, кликните по значку. Для примера смотрите ниже.
Пример - незащищенная операция на глобальной переменной от нескольких задач
typedef struct
{
unsigned int IOFlag :1;
unsigned int InterruptFlag :1;
unsigned int Register1Flag :1;
unsigned int SignFlag :1;
unsigned int SetupFlag :1;
unsigned int Register2Flag :1;
unsigned int ProcessorFlag :1;
unsigned int GeneralFlag :1;
} InterruptConfigbits_t;
InterruptConfigbits_t InterruptConfigbitsProc12;
void task1 (void) {
InterruptConfigbitsProc12.IOFlag = 0;
}
void task2 (void) {
InterruptConfigbitsProc12.SetupFlag = 0;
}
В этом примере, task1
и task2
доступ различные битовые поля IOFlag
и SetupFlag
, которые принадлежат той же структурированной переменной InterruptConfigbitsProc12
.
Чтобы эмулировать многозадачное поведение, задайте следующие опции:
На командной строке можно использовать следующее:
polyspace-bug-finder
-entry-points task1,task2
Коррекция – использует критические разделы
Одна возможная коррекция должна перенести доступы к битовому полю в критический раздел. Критический раздел находится между вызовом функции блокировки и разблокировать функцией. В этой коррекции критический раздел находится между вызовами функций begin_critical_section
и end_critical_section
.
typedef struct
{
unsigned int IOFlag :1;
unsigned int InterruptFlag :1;
unsigned int Register1Flag :1;
unsigned int SignFlag :1;
unsigned int SetupFlag :1;
unsigned int Register2Flag :1;
unsigned int ProcessorFlag :1;
unsigned int GeneralFlag :1;
} InterruptConfigbits_t;
InterruptConfigbits_t InterruptConfigbitsProc12;
void begin_critical_section(void);
void end_critical_section(void);
void task1 (void) {
begin_critical_section();
InterruptConfigbitsProc12.IOFlag = 0;
end_critical_section();
}
void task2 (void) {
begin_critical_section();
InterruptConfigbitsProc12.SetupFlag = 0;
end_critical_section();
}
В этом примере, чтобы эмулировать многозадачное поведение, задают следующие опции:
На командной строке можно использовать следующее:
polyspace-bug-finder
-entry-points task1,task2
-critical-section-begin begin_critical_section:cs1
-critical-section-end end_critical_section:cs1
Коррекция – избегает битовых полей
Если у вас нет ограничений памяти, используйте char
тип данных вместо битовых полей. char
переменные в структуре занимают по крайней мере один байт и не имеют проблем потокобезопасности, которые прибывают из побитовых обработок в операции размера байта. Гонки данных не следуют из незащищенных операций на различном char
переменные, которые являются частью той же структуры.
typedef struct
{
unsigned char IOFlag;
unsigned char InterruptFlag;
unsigned char Register1Flag;
unsigned char SignFlag;
unsigned char SetupFlag;
unsigned char Register2Flag;
unsigned char ProcessorFlag;
unsigned char GeneralFlag;
} InterruptConfigbits_t;
InterruptConfigbits_t InterruptConfigbitsProc12;
void task1 (void) {
InterruptConfigbitsProc12.IOFlag = 0;
}
void task2 (void) {
InterruptConfigbitsProc12.SetupFlag = 0;
}
Хотя средство проверки не отмечает эту коррекцию, не используйте эту коррекцию в C99 или ранее. Только от C11 вперед делает Стандартный мандат C что отличный char
к переменным нельзя получить доступ с помощью того же слова.
Коррекция – вставляет битовое поле размера 0
Можно ввести не член битового поля или член битового поля без имени размера 0 между двумя смежными битовыми полями, к которым можно получить доступ одновременно. Не член битового поля или размер, 0 членов битового поля гарантируют, что последующее битовое поле начинает с новой ячейки памяти. В этом откорректированном примере размер 0 членов битового поля гарантируют тот IOFlag
и SetupFlag
хранятся в отличных ячейках памяти.
typedef struct
{
unsigned int IOFlag :1;
unsigned int InterruptFlag :1;
unsigned int Register1Flag :1;
unsigned int SignFlag :1;
unsigned int : 0;
unsigned int SetupFlag :1;
unsigned int Register2Flag :1;
unsigned int ProcessorFlag :1;
unsigned int GeneralFlag :1;
} InterruptConfigbits_t;
InterruptConfigbits_t InterruptConfigbitsProc12;
void task1 (void) {
InterruptConfigbitsProc12.IOFlag = 0;
}
void task2 (void) {
InterruptConfigbitsProc12.SetupFlag = 0;
}