Ефективна взаємодія між нативними процесами Arduino та Linux

Використовуючи скетчі Arduino у роботі з платами Intel Galileo та Intel Edison, ви можете зіткнутися з ситуацією, коли вам потрібно додати додаткову функціональність, задіявши набір Yocto для розробки вбудованих систем на основі Linux OS. І тут ми маємо вирішити завдання, про яке ми вже згадали в заголовку нашого посту: як налагодити ефективне «спілкування» між цими двома світами.

arduino
Давайте визначимо деякі критерії, які нам при цьому потрібно враховувати:

1. Відсутність обміну даними через диск (SD-карта, eMMC) для зменшення зносу диска та підвищення продуктивності. 2. Комунікація викликається виключно будь-якими подіями (зокрема, нам не потрібно періодично перевіряти статус, але ми хочемо отримувати повідомлення про ту чи іншу подію, інакше – не активувати обмін даними).

Міжпроцесна взаємодія (IPC) у Linux

Програма, створена серед Arduino (скетч) і запущена на Intel Galileo чи Intel Edison, – це Linux-процес, виконуваний паралельно коїться з іншими подібними процесами. Оскільки ми маємо справу з повноцінною системою Linux, запущеною на цих платах, можна також використовувати і стандартні засоби для міжпроцесної взаємодії (IPC) між процесами Arduino і нативними процесами. Для Linux є різні методи IPC. Один із них – це «відображена в пам'ять IPC». По суті, це означає, що IPC-процеси ділять між собою одну й ту саму область пам'яті. У свою чергу це означає, що будь-які модифікації, зроблені за допомогою одного процесу, який використовує певну область пам'яті, відразу ж стають видимими для всіх інших процесів. Така поведінка задовольняє нашому першому критерію: під час передачі ми працюємо з останнімивиключно в пам'яті вони не пишуться на диск.

М'ютекси та умовні змінні

Використання пам'яті, що розділяється, відразу піднімає пару питань, таких як:

1.Як переконатися в тому, що в даних, що розділяються, в якийсь певний час виконується тільки один процес (синхронізація)?

2.Як миттєво сповістити інший процес (або процеси) про зміну даних (повідомлення)?

Нижче ми почергово розглянемо ці два питання. Ми уможливимо використання «мьютексів» та «умовних змінних», які включені до бібліотеки потоків POSIX (Pthreads), доступної для системи Linux.

Синхронізація – М'ютекс

Взаємний виняток, або м'ютекс (mutex) – це стандартний принцип, який реалізований, мабуть, у будь-якій сучасній багатозадачній ОС. У цьому пості ми не описуватимемо всіх принципів і деталей, а лише поділимося конкретною інформацією щодо м'ютексів з набору потоків POSIX (Pthreads). Щоб дізнатися більше інформації, зверніться до паперових видань (наприклад, Tanenbaum, Woodhull: Operating Systems 3rd ed. Pearson 2006) або скористайтеся пошуком в Інтернеті.

Як випливає з назви бібліотеки, набір Pthreads переважно призначений для потокового програмування. У той же час він також пропонує потужні інструменти, які застосовуються для управління процесами. Більш того, середовище розробки Arduino IDE для плат Intel Galileo та Intel Edison підтримує бібліотеку Pthreads (тобто посилання на цю бібліотеку) з коробки, таким чином пропонуючи інтеграцію, що легко реалізується. Отже, використання Pthreads для наших цілей виглядає цілком логічним рішенням.

Як правило, м'ютекс слідкує за тим, щоб доступ до певної критичної секції коду отримував лише один потік. Тому що ми тутРозбираємося з процесами, давайте використовуємо м'ютекси таким чином, щоб тільки один процес міг отримати доступ до запиту pthread_mutex_lock у коді. Всі інші процеси будуть переведені в сплячий режим за допомогою самої ОС - до того моменту, як м'ютекс отримає команду pthread_mutex_unlock, після чого ОС розбудить всі інші процеси, запитуючи pthread_mutex_lock.

Це має бути зроблено тим самим способом як для блокування запису, так і блокування доступу до читання. Інакше в процесі читання можна отримати доступ до частково оновлених даних. У наступному розділі ми розповімо про принципи роботи елементів Pthreads, які дозволяють отримати повідомлення у разі зміни даних.

Повідомлення – Умовна змінна

Подібно до принципу, коли процес намагається отримати доступ до заблокованого м'ютексу, бібліотека Pthreads також включає умовні змінні. Умовна змінна дозволяє потоку або (у нашому випадку) процесу запитати дозвіл заснути доти, доки не буде збуджений. Це реалізується за допомогою функції pthread_cond_wait.

М'ютекс та умовна змінна в поєднанні дають наступний псевдокод:

Іншому процесу необхідно розблокувати м'ютекс і надіслати сигнал про зміну, виконавши виклик pthread_cond_signal. Це виведе процес зі сплячого режиму.

Більше інформації можна знайти у спеціалізованих виданнях або на відповідних онлайн-ресурсах. У наступному розділі наведено приклад реалізації коду.

Реалізація

    І для м'ютексу, і для умовної змінної нам потрібно явно встановити атрибути для того, щоб дозволити міжпроцесне використання.

Ми вибрали використання IPC, що розділяється, за допомогою відображених у пам'ять файлів,оскільки Arduino IDE не підтримує всі бібліотеки, необхідні реалізації прямого розподілу пам'яті IPC. Поміщення файлу для обміну даними у файлову систему, відображену в основну пам'ять, по суті забезпечує ідентичну функціональність. Платформа Yocto Linux, яка йде в комплекті з платою Intel Edison, так само як і з образом Yocto для SD-карти, включає тимчасову папку /tmp, змонтовану в сховищі tmpfs, яке розміщується в пам'яті. Тобто нам підійде будь-який файл у цій папці. Ми вибрали файл /tmp/arduino, він призначений виключно для використання в IPC.

Ми припускаємо, що саме процес Arduino запустить м'ютекс та умовну змінну, оскільки саме цей процес виконується під час системного завантаження.

У нашому прикладі показана ситуація, коли процес Arduino чекає на дані від нативного процесу Linux і виконується на основі отриманих даних. Для інших варіантів код повинен бути змінений відповідним чином.

Для того, щоб показати принцип дії, ми поміщаємо дві булеві (логічні) у відображену в пам'ять структуру mmapData, яка визначає включення та відключення індикаторів (вбудованого та зовнішнього на IO 8):

Очевидно, що сюди можна розмістити будь-які дані. Дві інші змінні у структурі mmapData – це м'ютекс та умовна змінна.

Примітка:

Приклад коду нижче поставляється за MIT License. Нижче ви знайдете три файли:

  • mmap.ino: скетч для запуску в середовищі розробки Arduino IDE
  • mmap.cpp: нативний процес, що відповідає за надсилання даних
  • mmap.h: файл заголовка – файл для використання в Arduino IDE, а також у Linux
Тобто на стороні Arduino у вас має бути папка, яка міститьфайли mmap.ino та mmap.h у директорії скетчів Arduino. У Linux слід використовувати папку, що містить файли mmap.cpp та mmap.р.

Щоб запустити скетч, відкрийте програму mmap у середовищі розробки Arduino IDE та завантажте її у відповідну плату (Intel Galileo Gen 1, Intel Galileo Gen 2 або Intel Edison). Пакет для розробників Intel IoT постачається з крос-компілятором. Крім того, платформа Yocto, яка йде в комплекті з Intel Edison, так само як з образом Yocto для SD-карти, або з платою Intel Galileo, поставляється з встановленим компілятором С++. Для запуску встановленого компілятора вам потрібно виконати:

В папку слід помістити файли mmap.cpp та mmap.h. Це дозволить згенерувати бінарний файл, який можна запустити так:

…де «» виступає для 0 або 1. Наприклад, вираз «./mmap 00» вимкне обидва індикатори, у той час як команда ./mmap 00 їх увімкне. Деяка додаткова інформація виводиться на монітор послідовного інтерфейсу (Ctrl + Shift + M).