Читаем Real-Time Interrupt-driven Concurrency полностью

resources: foo::Resources {

// `x` уже инициализирована к этому моменту

x: &mut *x.as_mut_ptr(),

},

// ..

})

}

}

Важная деталь здесь то, что interrupt::enable ведет себя как like a compiler fence, которое не дает компилятору пореставить запись в X после interrupt::enable. Если бы компилятор мог делать такие перестановки появились бы гонки данных между этой записью и любой операцией foo, взаимодействующей с X.

Архитектурам с более сложным конвейером инструкций нужен барьер памяти (atomic::fence) вместо compiler fence для полной очистки операции записи перед включением прерываний. Архитектура ARM Cortex-M не нуждается в барьере памяти в одноядерном контексте.

<p id="Критические_секции"><strong><a l:href="#Критические_секции">Критические секции</a></strong></p>

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

Критическия секция состоит во временном увеличении динамического приоритета задачи. Пока задача находится в критической секции, все другие задачи, которые могут послать запрос переменной не могут запуститься.

Насколько большим должен быть динамический приориткт, чтобы гарантировать запрет изменений определенного ресурса? Анализ приоритетов отвечает на этот вопрос и будет обсужден в следующем разделе. В этом разделе мы сфокусируемся на реализации критической секции.

<p id="Прокси_ресурсы"><strong><a l:href="#Прокси_ресурсы">Прокси-ресурсы</a></strong></p>

Для упрощения, давайте взглянем на ресурс, разделяемый двумя задачами, запускаемыми с разными приоритетами. Очевидно, что одна задача может вытеснить другую; чтобы предотвратить гонку данных задача с низким приоритетом должна использовать критическую секцию, когда необходимо изменять разделяемую память. С другой стороны, высокоприоритетная задача может напрямую изменять разделяемую память, поскольку не может быть вытеснена низкоприоритетной задачей. Чтобы заставить использовать критическую секцию на задаче с низким приоритетом, мы предоставляем прокси-ресурсы, в которых мы отдаем уникальную ссылку (&mut-) высокоприоритетной задаче.

Пример ниже показывает разные типы, передаваемые каждой задаче:

#![allow(unused)]

fn main() {

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

mut app {

struct Resources {

#[init(0)]

x: u64,

}

#[interrupt(binds = UART0, priority = 1, resources = [x])]

fn foo(c: foo::Context) {

// прокси-ресурс

let mut x: resources::x = c.resources.x;

x.lock(|x: &mut u64| {

// критическая секция

*x += 1

});

}

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

fn bar(c: bar::Context) {

let mut x: &mut u64 = c.resources.x;

*x += 1;

}

// ..

}

}

Теперь давайте посмотрим. как эти типы создаются фреймворком.

#![allow(unused)]

fn main() {

fn foo(c: foo::Context) {

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

}

fn bar(c: bar::Context) {

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

}

pub mod resources {

pub struct x {

// ..

}

}

pub mod foo {

pub struct Resources {

pub x: resources::x,

}

pub struct Context {

pub resources: Resources,

// ..

}

}

pub mod bar {

pub struct Resources<'a> {

pub x: &'a mut u64,

}

pub struct Context {

pub resources: Resources,

// ..

}

}

mod app {

static mut x: u64 = 0;

impl rtic::Mutex for resources::x {

type T = u64;

fn lock(&mut self, f: impl FnOnce(&mut u64) -> R) -> R {

// мы рассмотрим это детально позднее

}

}

#[no_mangle]

unsafe fn UART0() {

foo(foo::Context {

resources: foo::Resources {

x: resources::x::new(/* .. */),

},

// ..

})

}

#[no_mangle]

unsafe fn UART1() {

bar(bar::Context {

resources: bar::Resources {

x: &mut x,

},

// ..

})

}

}

}

<p id="lock_1"><code><strong><a l:href="#lock_1">lock</a></strong></code></p>
Перейти на страницу:

Похожие книги

Компьютерные сети. 6-е изд.
Компьютерные сети. 6-е изд.

Перед вами шестое издание самой авторитетной книги по современным сетевым технологиям, написанное признанным экспертом Эндрю Таненбаумом в соавторстве со специалистом компании Google Дэвидом Уэзероллом и профессором Чикагского университета Ником Фимстером. Первая версия этого классического труда появилась на свет в далеком 1980 году, и с тех пор каждое издание книги неизменно становилось бестселлером. В книге последовательно изложены основные концепции, определяющие современное состояние компьютерных сетей и тенденции их развития. Авторы подробно объясняют устройство и принципы работы аппаратного и программного обеспечения, рассматривают все аспекты и уровни организации сетей — от физического до прикладного. Изложение теоретических принципов дополняется яркими, показательными примерами функционирования интернета и компьютерных сетей различного типа. Большое внимание уделяется сетевой безопасности. Шестое издание полностью переработано с учетом изменений, произошедших в сфере сетевых технологий за последние годы, и, в частности, освещает такие технологии, как DOCSIS, 4G и 5G, беспроводные сети стандарта 802.11ax, 100-гигабитные сети Ethernet, интернет вещей, современные транспортные протоколы CUBIC TCP, QUIC и BBR, программно-конфигурируемые сети и многое другое.

Дэвид Уэзеролл , Ник Фимстер , Эндрю Таненбаум

Учебные пособия, самоучители