CERT C: Rule POS51-C

Избегайте взаимоблокировки с резьбами POSIX путем блокировки в предопределенном порядке

Описание

Определение правила

Избегайте взаимоблокировки с потоками POSIX путем блокировки в предопределенном порядке.[1]

Реализация Polyspace

Этот чекер проверяет на Тупик.

Примеры

расширить все

Проблема

Взаимоблокировка возникает, когда несколько задач застревают в их критических разделах (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 detailsStarting routineEnding routine
begin_critical_section_1end_critical_section_1
begin_critical_section_2end_critical_section_2

Deadlock происходит, потому что инструкции могут выполняться в следующей последовательности:

  1. task1 вызывает begin_critical_section_1.

  2. task2 вызывает begin_critical_section_2.

  3. task1 достигает команды begin_critical_section_2();. Начиная с task2 уже звонил begin_critical_section_2, task1 ждет task2 для вызова end_critical_section_2.

  4. 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 detailsStarting routineEnding routine
lock1unlock1
lock2unlock2
lock3unlock3

Deadlock происходит, потому что инструкции могут выполняться в следующей последовательности:

  1. task1 вызывает lock1.

  2. task2 вызывает lock2.

  3. task3 вызывает lock3.

  4. task1 достигает команды lock2();. Начиная с task2 уже звонил lock2, task1 ждет вызова, чтобы unlock2.

  5. task2 достигает команды lock3();. Начиная с task3 уже звонил lock3, task2 ждет вызова, чтобы unlock3.

  6. task3 достигает команды lock1();. Начиная с task1 уже звонил lock1, task3 ждет вызова, чтобы unlock1.

Коррекция - разрыв циклического порядка

Чтобы разбить циклический порядок между критическими разделами, обратите внимание на каждую функцию блокировки в вашем коде в определенной последовательности, например:

  1. lock1

  2. lock2

  3. lock3

Если вы используете в задаче несколько функций блокировки, используйте их в том порядке, в котором они появляются в последовательности. Для примера можно использовать 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();
 } 
}

Проверяйте информацию

Группа: Правило 50. POSIX (POS)
Введенный в R2019a

[1] Это программное обеспечение было создано MathWorks, включающее фрагменты: «Сайт SEI CERT-C», © 2017 Университет Карнеги Меллон, Веб-сайт SEI CERT-C + + © 2017 Университет Карнеги Меллон, "Стандарт кодирования SEI CERT C - Правила разработки безопасных, Надежные и безопасные системы - 2016 Edition ", © 2016 Университет Карнеги Меллон, и "Стандарт кодирования SEI CERT C++ - Правила разработки безопасных, Надежные и безопасные системы в C++ - 2016 Edition "© 2016 Университет Карнеги Меллон, с специального разрешения от его Института программной инженерии.

ЛЮБОЙ МАТЕРИАЛ УНИВЕРСИТЕТА КАРНЕГИ МЕЛЛОН И/ИЛИ ЕГО ИНЖЕНЕРНОГО ИНСТИТУТА ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ, СОДЕРЖАЩИЙСЯ В НАСТОЯЩЕМ ДОКУМЕНТЕ, ПОСТАВЛЯЕТСЯ НА БАЗИСЕ «КАК ЕСТЬ». УНИВЕРСИТЕТ КАРНЕГИ МЕЛЛОН НЕ ДАЕТ НИКАКИХ ГАРАНТИЙ, ВЫРАЖЕННЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ, В ОТНОШЕНИИ ЛЮБОГО ВОПРОСА, ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИВАЯСЬ, ГАРАНТИЮ ПРИГОДНОСТИ ДЛЯ ЦЕЛЕЙ ИЛИ КОММЕРЧЕСКОЙ ВЫГОДЫ, ИСКЛЮЧИТЕЛЬНОСТИ, ИЛИ УНИВЕРСИТЕТ КАРНЕГИ МЕЛЛОН НЕ ДАЕТ НИКАКИХ ГАРАНТИЙ В ОТНОШЕНИИ СВОБОДЫ ОТ ПАТЕНТА, ТОВАРНОГО ЗНАКА ИЛИ НАРУШЕНИЯ АВТОРСКИХ ПРАВ.

Это программное обеспечение и связанная с ним документация не были рассмотрены и не одобрены Университетом Карнеги-Меллон или его Институтом программной инженерии.