Если ваш код предназначен для многозадачности, задачи в вашем коде могут получить доступ к общей общей переменной. Чтобы предотвратить гонки данных, можно защитить операции чтения и записи переменной. В этой теме показаны различные механизмы защиты, которые Polyspace® может распознать.
Обнаружить незащищенный доступ можно с помощью Bug Finder или Code Prover. Code Prover является более исчерпывающим и доказывает, защищена ли общая переменная от параллельного доступа.
Bug Finder обнаруживает незащищенный доступ с помощью результирующего Data race. См. Data race
.
Code Prover обнаруживает незащищенный доступ с помощью результирующего Shared unprotected global variable. См. Potentially unprotected variable
(Polyspace Code Prover).
Предположим, вы анализируете этот код, определяя signal_handler_1
и signal_handler_2
как циклические задачи. Используйте опцию анализа Cyclic tasks (-cyclic-tasks)
.
#include <limits.h> int shared_var; void inc() { shared_var+=2; } void reset() { shared_var = 0; } void signal_handler_1(void) { reset(); inc(); inc(); } void signal_handler_2(void) { shared_var = INT_MAX; } void main() { }
Bug Finder показывает гонку данных на shared_var
. Code Prover показывает, что shared_var
является потенциально незащищенной общей переменной. Code Prover также показывает, что операция shared_var += 2
может переполниться. Переполнение происходит, если вызов inc
в signal_handler_1
немедленно следует операции shared_var = INT_MAX
в signal_handler_2
.
Одним из возможных решений является защита операций с общими переменными с помощью критических разделов.
В предыдущем примере измените код так, чтобы операции на shared_var
находятся в одном и том же критическом разделе. Используйте функции take_semaphore
и give_semaphore
чтобы начать и завершить критические участки. Чтобы задать эти функции, которые начинают и заканчивают критические сечения, используйте опции анализа Critical section details (-critical-section-begin -critical-section-end)
.
#include <limits.h> int shared_var; void inc() { shared_var+=2; } void reset() { shared_var = 0; } /* Declare lock and unlock functions */ void take_semaphore(void); void give_semaphore(void); void signal_handler_1() { /* Begin critical section */ take_semaphore(); reset(); inc(); inc(); /* End critical section */ give_semaphore(); } void signal_handler_2() { /* Begin critical section */ take_semaphore(); shared_var = INT_MAX; /* End critical section */ give_semaphore(); } void main() { }
Вы не видите гонки данных в Bug Finder. Code Prover доказывает, что общая переменная защищена. Вы также не видите переполнение, потому что вызов reset()
в signal_handler_1
всегда предшествует вызовам в inc()
.
Можно также использовать примитивы, такие как POSIX® функции pthread_mutex_lock
и pthread_mutex_unlock
чтобы начать и закончить критические участки. Список примитивов, которые Polyspace может обнаружить автоматически, см. в разделах Автоматическое обнаружение создания потоков и Критическое сечение в Polyspace.
Другое возможное решение - задать группу задач как временно эксклюзивную. Временные исключающие задачи не могут прерывать друг друга.
В предыдущем примере задайте, что signal_handler_1
и signal_handler_2
являются временными исключительными. Используйте опцию Temporally exclusive tasks (-temporal-exclusions-file)
.
Вы не видите гонки данных в Bug Finder. Code Prover доказывает, что общая переменная защищена. Вы также не видите переполнение, потому что вызов reset()
в signal_handler_1
всегда предшествует вызовам в inc()
.
Другое возможное решение состоит в том, чтобы указать, что одна задача имеет более высокий приоритет над другой.
В предыдущем примере задайте, что signal_handler_1
является прерыванием. Сохраните signal_handler_2
как циклическая задача. Используйте опции Cyclic tasks (-cyclic-tasks)
и Interrupts (-interrupts)
.
Bug Finder больше не показывает дефект гонки данных. Причина в следующем:
Область операции shared_var = INT_MAX
в signal_handler_2
является атомарным. Поэтому операции в signal_handler_1
невозможно прервать его.
Операции в signal_handler_1
не может быть прервана операцией в signal_handler_2
потому что signal_handler_1
имеет более высокий приоритет.
Вы можете задать до четырех различных приоритетов с этими опциями (с наивысшим приоритетом, перечисленным первым):
Задача с более высоким приоритетом является атомарной относительно задачи с более низким приоритетом. Обратите внимание, что шашка Data race including atomic operations
игнорирует различия в приоритетах и продолжает показывать гонку данных. Смотрите также «Определение предопределяемых прерываний и незапускаемых задач».
Code Prover не рассматривает приоритеты задач. Поэтому Code Prover все еще показывает shared_var
как потенциально незащищенная глобальная переменная.
При анализе Bug Finder можно защитить группу операций, отключив все прерывания. Используйте опцию Disabling all interrupts (-routine-disable-interrupts -routine-enable-interrupts)
.
После того, как вы вызываете стандартную программу, чтобы отключить прерывания, все последующие операции являются атомарными, пока вы не вызываете другую стандартную программу, чтобы возобновить прерывания. Операции являются атомарными в отношении операций во всех других задачах.