ПроблемаГонка данных на смежных битовых полях происходит когда:
Несколько задач выполняют незащищенные операции на битовых полях, которые являются частью той же структуры.
Например, задача работает с полем errorFlag1
и другая задача на поле errorFlag2
в переменной этого типа:
struct errorFlags {
unsigned int errorFlag1 : 1;
unsigned int errorFlag2 : 1;
//...
};
Предположим, что операции не являются атомарными друг относительно друга. Другими словами, вы не реализовали механизмы защиты, чтобы гарантировать, что одна операция завершается, прежде чем другая операция начинается.По крайней мере одна из незащищенных операций является операцией записи.
Чтобы найти этот дефект, перед анализом, необходимо задать многозадачные опции. Чтобы задать эти опции, на панели Configuration, выбирают Multitasking. Для получения дополнительной информации смотрите Анализ Многозадачности Polyspace Конфигурирования Вручную.
РискСмежные битовые поля, которые являются частью той же структуры, могут храниться в одном байте в той же ячейке памяти. Операции чтения или операции записи на всех переменных включая битовые поля происходят один байт или слово за один раз. Чтобы изменить только определенные биты в байте, шаги, похожие на эти шаги, происходят в последовательности:
Байт загружается в RAM.
Маска создается так, чтобы только определенные биты были изменены к намеченному значению, и остающиеся биты остаются неизменными.
Операция битового "ИЛИ" выполняется между копией байта в RAM и маской.
Байт с определенными измененными битами копируется назад с RAM.
Когда вы получаете доступ к двум различным битовым полям, эти четыре шага должны быть выполнены для каждого битового поля. Если доступы не защищены, все четыре шага для одного битового поля не могут быть завершены, прежде чем четыре шага для другого битового поля начинаются. В результате модификация одного битового поля может отменить модификацию смежного битового поля. Например, в предыдущем примере, модификации errorFlag1
и errorFlag2
может произойти в следующей последовательности.
Шаги 1,2 и 5 относятся к модификации errorFlag1
и в то время как шаги 3,4 и 6 относятся к шагам 3,4 и 6 errorFlag2
.
Байт с обоими errorFlag1
и errorFlag2
немодифицированный копируется в RAM, в целях изменить errorFlag1
.
Маска, которая изменяет только errorFlag1
редактор битового "ИЛИ" с этой копией.
Байт, содержащий оба errorFlag1
и errorFlag2
немодифицированный копируется в RAM во второй раз, в целях изменить errorFlag2
.
Маска, которая изменяет только errorFlag2
редактор битового "ИЛИ" с этой второй копией.
Версия с errorFlag1
измененный копируется назад. Эта версия имеет errorFlag2
немодифицированный.
Версия с 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;
}