Описание
Мертвая блокировка происходит, когда несколько задач застревают в своих критических разделах (CS) потому что:
Каждый CS ожидает другого CS, чтобы закончиться.
Форма критических разделов (CS) замкнутый цикл. Например:
CS № 1 ожидает CS № 2, чтобы закончиться, и CS № 2 ожидает CS № 1, чтобы закончиться.
CS № 1 ожидает CS № 2, чтобы закончиться, CS № 2 ожидает CS № 3, чтобы закончиться, и CS № 3 ожидает CS № 1, чтобы закончиться.
Polyspace® ожидает, что критические разделы кода будут следовать за определенным форматом. Критический раздел находится между вызовом функции блокировки и вызовом разблокировать функции. Когда задача, my_task
вызывает функцию блокировки my_lock
, другие задачи, вызывая my_lock
, должна ожидать до вызовов my_task
соответствие разблокировало функцию. Обе блокировки и разблокировали функции, должен иметь форму void func(void)
.
Чтобы найти этот дефект, необходимо задать многозадачные опции перед анализом. Чтобы задать эти опции, на панели Configuration, выбирают Multitasking.
Риск
Каждая задача ожидает критического раздела в другой задаче закончиться и не может продолжить. Программа может заморозиться неопределенно.
Фиксация
Фиксация зависит от первопричины дефекта. Можно попытаться повредить циклический порядок между задачами одним из этих способов:
Запишите все критические разделы, вовлеченные в мертвую блокировку в определенной последовательности. Каждый раз, когда вы вызываете функции блокировки критических разделов в задаче, уважаете порядок в той последовательности. Смотрите пример ниже.
Если один из критических разделов, вовлеченных в мертвую блокировку, происходит в прерывании, попытайтесь отключить все прерывания во время критических разделов во всех задачах. Смотрите Disabling all interrupts (-routine-disable-interrupts -routine-enable-interrupts)
.
Рассмотрение этого дефекта является возможностью проверять, предназначены ли все операции в вашем критическом разделе действительно, чтобы быть выполненными как атомарный блок. Это - хорошая практика, чтобы сохранить критические разделы как минимум.
Если вы не хотите устранять проблему, добавьте комментарии в свой результат или код, чтобы избежать другого анализа. Смотрите Результаты Polyspace Адреса Через Исправления ошибок или Комментарии.
Пример - заходит в тупик с двумя задачами
void task1(void);
void task2(void);
int var;
void perform_task_cycle(void) {
var++;
}
void begin_critical_section_1(void);
void end_critical_section_1(void);
void begin_critical_section_2(void);
void end_critical_section_2(void);
void task1() {
while(1) {
begin_critical_section_1();
begin_critical_section_2();
perform_task_cycle();
end_critical_section_2();
end_critical_section_1();
}
}
void task2() {
while(1) {
begin_critical_section_2();
begin_critical_section_1();
perform_task_cycle();
end_critical_section_1();
end_critical_section_2();
}
}
В этом примере, чтобы эмулировать многозадачное поведение, необходимо задать следующие опции:
Опция | Спецификация |
---|
Configure multitasking manually | |
Entry points | task1
task2
|
Critical section details | Starting routine | Ending routine |
begin_critical_section_1 | end_critical_section_1 |
begin_critical_section_2 | end_critical_section_2 |
Deadlock происходит, потому что инструкции могут выполниться в следующей последовательности:
task1
вызывает begin_critical_section_1
.
task2
вызывает begin_critical_section_2
.
task1
достигает инструкции begin_critical_section_2();
. Поскольку task2
уже вызвал begin_critical_section_2
, task1
ожидает task2
, чтобы вызвать end_critical_section_2
.
task2
достигает инструкции begin_critical_section_1();
. Поскольку task1
уже вызвал begin_critical_section_1
, task2
ожидает task1
, чтобы вызвать end_critical_section_1
.
Исправление - следует за той же последовательностью блокировки в обеих задачах
Одно возможное исправление должно следовать за той же последовательностью вызовов, чтобы заблокировать и разблокировать функции и в task1
и в task2
.
void task1(void);
void task2(void);
void perform_task_cycle(void);
void begin_critical_section_1(void);
void end_critical_section_1(void);
void begin_critical_section_2(void);
void end_critical_section_2(void);
void task1() {
while(1) {
begin_critical_section_1();
begin_critical_section_2();
perform_task_cycle();
end_critical_section_2();
end_critical_section_1();
}
}
void task2() {
while(1) {
begin_critical_section_1();
begin_critical_section_2();
perform_task_cycle();
end_critical_section_2();
end_critical_section_1();
}
}
Пример - заходит в тупик больше чем с Двумя задачами
int var;
void performTaskCycle() {
var++;
}
void lock1(void);
void lock2(void);
void lock3(void);
void unlock1(void);
void unlock2(void);
void unlock3(void);
void task1() {
while(1) {
lock1();
lock2();
performTaskCycle();
unlock2();
unlock1();
}
}
void task2() {
while(1) {
lock2();
lock3();
performTaskCycle();
unlock3();
unlock2();
}
}
void task3() {
while(1) {
lock3();
lock1();
performTaskCycle();
unlock1();
unlock3();
}
}
В этом примере, чтобы эмулировать многозадачное поведение, необходимо задать следующие опции:
Опция | Спецификация |
---|
Configure multitasking manually | |
Entry points | task1
task2
task3
|
Critical section details | Starting routine | Ending routine |
lock1 | unlock1 |
lock2 | unlock2 |
lock3 | unlock3 |
Deadlock происходит, потому что инструкции могут выполниться в следующей последовательности:
task1
вызывает lock1
.
task2
вызывает lock2
.
task3
вызывает lock3
.
task1
достигает инструкции lock2();
. Поскольку task2
уже вызвал lock2
, task1
ожидает вызова unlock2
.
task2
достигает инструкции lock3();
. Поскольку task3
уже вызвал lock3
, task2
ожидает вызова unlock3
.
task3
достигает инструкции lock1();
. Поскольку task1
уже вызвал lock1
, task3
ожидает вызова unlock1
.
Исправление — повреждает циклический порядок
Чтобы повредить циклический порядок между критическими разделами, отметьте каждую функцию блокировки в своем коде в определенной последовательности, например:
Если вы используете больше чем одну функцию блокировки в задаче, используйте их в порядке, в котором они появляются в последовательности. Например, можно использовать lock1
, сопровождаемый lock2
, но не lock2
, сопровождаемым lock1
.
int var;
void performTaskCycle() {
var++;
}
void lock1(void);
void lock2(void);
void lock3(void);
void unlock1(void);
void unlock2(void);
void unlock3(void);
void task1() {
while(1) {
lock1();
lock2();
performTaskCycle();
unlock2();
unlock1();
}
}
void task2() {
while(1) {
lock2();
lock3();
performTaskCycle();
unlock3();
unlock2();
}
}
void task3() {
while(1) {
lock1();
lock3();
performTaskCycle();
unlock3();
unlock1();
}
}