Выбрать главу

Чтобы переиспользовать задачу-обработчик программно, назначенный ей обработчик прерывания должен быть вызван с помощью FFI (смотрите пример ниже). FFI требует unsafe код, что уменьшает желание конечных пользователей вызывать обработчик прерывания.

#![allow(unused)]

fn main() {

#[rtic::app(device = ..)]

mod app {

#[init]

fn init(c: init::Context) { .. }

#[interrupt(binds = UART0)]

fn foo(c: foo::Context) {

static mut X: u64 = 0;

let x: &mut u64 = X;

// ..

//~ `bar` может вытеснить `foo` в этом месте

// ..

}

#[interrupt(binds = UART1, priority = 2)]

fn bar(c: foo::Context) {

extern "C" {

fn UART0();

}

// этот обработчик прерывания вызовет задачу-обработчик `foo`, что сломает

// ссылку на статическую переменную `X`

unsafe { UART0() }

}

}

}

Фреймворк RTIC должен сгенерировать код обработчика прерывания, который вызывает определенные пользователем задачи-обработчики. Мы аккуратны в том, чтобы обеспечить невозможность вызова этих обработчиков из пользовательского кода.

Пример выше раскрывается в:

#![allow(unused)]

fn main() {

fn foo(c: foo::Context) {

// .. пользовательский код ..

}

fn bar(c: bar::Context) {

// .. пользовательский код ..

}

mod app {

// все в этом блоке невидимо для пользовательского кода

#[no_mangle]

unsafe fn USART0() {

foo(..);

}

#[no_mangle]

unsafe fn USART1() {

bar(..);

}

}

}

Обработчик прерывания также может быть вызван без программного вмешательства. Это может произойти, если один обработчик будет назначен двум или более прерываниям в векторе прерываний, но синтаксиса для такого рода функциональности в RTIC нет.

Одна из основ RTIC - контроль доступа. Контроль того, какая часть программы может получить доступ к какой статической переменной - инструмент обеспечения безопасности памяти.

Статические переменные используются для разделения состояний между обработчиками прерываний, или между обработчиком прерывания и нижним контекстом выполнения, main. В обычном Rust коде трудно обеспечить гранулированный контроль за тем, какие функции могут получать доступ к статическим переменным, поскольку к статическим переменным можно получить доступ из любой функции, находящейся в той же области видимости, в которой они определены. Модули дают частичный контроль над доступом к статическим переменным, но они недостаточно гибкие.

Чтобы добиться полного контроля за тем, что задачи могут получить доступ только к статическим переменным (ресурсам), которые им были указаны в RTIC атрибуте, фреймворк RTIC производит трансформацию структуры кода. Эта трансформация состоит из размещения ресурсов (статических переменных), определенных пользователем внутри модуля, а пользовательского кода вне модуля. Это делает невозможным обращение пользовательского кода к статическим переменным.

Затем доступ к ресурсам предоставляется каждой задаче с помощью структуры Resources, чьи поля соответствуют ресурсам, к которым получает доступ задача. Есть лишь одна такая структура на задачу и структура Resources инициализируется либо уникальной ссылкой (&mut-) на статическую переменную, либо с помощью прокси-ресурса (см. раздел критические секции).

Код ниже - пример разных трансформаций структуры кода, происходящих за сценой:

#![allow(unused)]

fn main() {

#[rtic::app(device = ..)]

mod app {

static mut X: u64: 0;

static mut Y: booclass="underline" 0;

#[init(resources = [Y])]

fn init(c: init::Context) {

// .. пользовательский код ..

}

#[interrupt(binds = UART0, resources = [X])]

fn foo(c: foo::Context) {

// .. пользовательский код ..