ПроблемаГонка данных на смежных битовых полях происходит когда:
Несколько задач выполняют незащищенные операции на битовых полях, которые являются частью той же структуры.
Например, задача работает с полем errorFlag1
и другая задача на поле errorFlag2
в переменной этого типа:
struct errorFlags {
unsigned int errorFlag1 : 1;
unsigned int errorFlag2 : 1;
...
}
Предположим, что операции не являются атомарными друг относительно друга. Другими словами, вы не реализовали механизмы защиты, чтобы гарантировать, что одна операция завершается, прежде чем другой начинает.По крайней мере одна из незащищенных операций является операцией записи.
РискСмежные битовые поля, которые являются частью той же структуры, могут храниться в одном байте в той же ячейке памяти. Операции чтения или операции записи на всех переменных включая битовые поля происходят один байт или слово за один раз. Чтобы изменить только определенные биты в байте, шаги, похожие на это, происходят в последовательности:
Байт загружается в 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 показывает существующие меры защиты на вызовах. Чтобы видеть, что последовательность вызова функции ведет к конфликтам, кликните по значку. Для примера смотрите ниже.
Пример - незащищенная операция на глобальной переменной от нескольких потоков POSIX#include <stdlib.h>
#include <pthread.h>
#define thread_success 0
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* arg) {
InterruptConfigbitsProc12.IOFlag = 0;
//Additional code
}
void* task2 (void* arg) {
InterruptConfigbitsProc12.SetupFlag = 0;
//Additional code
}
void main() {
pthread_t thread1, thread2;
if(thread_success != pthread_create(&thread1, NULL, task1, NULL)){
//Handle error
}
if(thread_success != pthread_create(&thread2, NULL, task2, NULL)){
//Handle error
}
}
В этом примере, потоках с ID thread1
и thread2
доступ различные битовые поля IOFlag
и SetupFlag
, которые принадлежат той же структурированной переменной InterruptConfigbitsProc12
.
Коррекция - использует критические разделыОдна возможная коррекция должна перенести доступы к битовому полю в критический раздел. Критический раздел находится между вызовом функции блокировки и разблокировать функцией. В этой коррекции критический раздел находится между вызовами функций pthread_mutex_lock
и pthread_mutex_unlock
.
#include <stdlib.h>
#include <pthread.h>
#define thread_success 0
#define lock_success 0
pthread_mutex_t lock;
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* arg) {
if( lock_success != pthread_mutex_lock(&lock)) {
//Handle error
}
InterruptConfigbitsProc12.IOFlag = 0;
if( lock_success != pthread_mutex_unlock(&lock)) {
//Handle error
}
//Additional code
}
void* task2 (void* arg) {
if( lock_success != pthread_mutex_lock(&lock)) {
//Handle error
}
InterruptConfigbitsProc12.SetupFlag = 0;
if( lock_success != pthread_mutex_unlock(&lock)) {
//Handle error
}
//Additional code
}
void main() {
pthread_t thread1, thread2;
if(thread_success != pthread_create(&thread1, NULL, task1, NULL)){
//Handle error
}
if(thread_success != pthread_create(&thread2, NULL, task2, NULL)){
//Handle error
}
}
Коррекция – вставляет битовое поле размера 0Можно ввести не член битового поля или член битового поля без имени размера 0 промежуточных два смежных битовых поля, к которым можно получить доступ одновременно. Не член битового поля или размер, 0 членов битового поля гарантируют, что последующее битовое поле начинает с новой ячейки памяти. В этом откорректированном примере размер 0 членов битового поля гарантируют тот IOFlag
и SetupFlag
хранятся в отличных ячейках памяти.
#include <stdlib.h>
#include <pthread.h>
#define thread_success 0
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* arg) {
InterruptConfigbitsProc12.IOFlag = 0;
//Additional code
}
void* task2 (void* arg) {
InterruptConfigbitsProc12.SetupFlag = 0;
//Additional code
}
void main() {
pthread_t thread1, thread2;
if(thread_success != pthread_create(&thread1, NULL, task1, NULL)){
//Handle error
}
if(thread_success != pthread_create(&thread2, NULL, task2, NULL)){
//Handle error
}
}