Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

SystemSoftware2/rMach

Open more actions menu

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

49 Commits
49 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

rMach

reallyMach — Mach, но без лишнего.

Смысл

Это микроядро, но без тонны жира.

Здесь мы отошли от стандартных способов сделать многозадачность на MicroPython.

Мы сделали ставку на порты.

Как работает?

У нас есть данные технологии:

  1. Handoff scheduling - шлёшь сообщение и другой процесс уже обрабатывает его.
  2. Reference counting (на порты) - чтобы не было пустых портов в памяти.
  3. VM для процессов - вы скажете что это ненормально, но это единственный способ сделать MicroPython с вытеснением.
  4. O(1) Linux 2.6 scheduler - есть недочеты, но работает примерно в O(1).
  5. State machines - для событий и handoff scheduling.
  6. Асинхронные порты - ну... Единственный способ общение.
  7. Software isolating - в VM нету команд "залезть в чужую память".
  8. Права на порты - чтобы чужие процессы не лезли в порты которые им не дали.

Очень сложный механизм, но 702 строк вы прочитаете с удовольствием.

Также у нас 4 уровня:

  1. Железо. Тут процессор и всё такое
  2. MicroPython.
  3. Ядро.
  4. VM.
  5. Процессы.

Если от нуля считать то их 4.

Да. Ядро на MicroPython. А что?

Твой первый процесс

Создаем порт и выводим сообщение:

CREATE_PORT    # 1. Создали порт (ID теперь в стеке)
STORE my_port  # 2. Сохранили ID в переменную 'my_port' (стек пуст)

PUSH 42        # 3. Положили данные (42)
PUSH 0         # 4. Порт для ответа (нам не нужен, пишем 0)
FETCH my_port  # 5. Достали ID порта из переменной (куда слать)
SEND           # 6. Улетело! (Стек снова пуст)

FETCH my_port  # 7. Снова берем ID нашего порта
RECV           # 8. Ядро лезет в порт, достает '42' и кладет в СТЕК!
PRINT          # 9. ВОТ ТЕПЕРЬ ОНО ВЫВЕДЕТ 42
HALT           # 10. Конец

Как работать?

Во-первых, почитайте о Mach. Это будет большой бонус вам.

Во-вторых, почитайте код main.py - там основной функционал.

Вот команды для VirtualMachine:

  • PUSH [val] — Положить число или объект в стек. Основа всего.
  • FETCH [var] — Взять значение переменной из окружения (env) и положить в стек.
  • STORE [var] — Забрать значение из стека и сохранить в переменную.
  • ADD, SUB, MUL, DIV — Классическая арифметика над двумя верхними значениями стека.
  • LT, GT, EQ, NOTEQ — Логика сравнения. Результат (1 или 0) падает обратно в стек.
  • JZ / JNZ [addr] — Переход по адресу, если в стеке 0 (или не 0). Основа циклов и условий.
  • JMP [addr] — Безусловный прыжок. Навигация по байт-коду.
  • SEND — Системный вызов: забрать из стека (данные, порт_ответа, порт_назначения) и отправить.
  • RECV — Системный вызов: ждать сообщения на порту. Если пусто — VM засыпает (WAITING).
  • CREATE_PORT — Рождение новой точки доступа. Возвращает ID порта в стек.
  • LIST / DICT — Сборка сложных структур данных из последних n элементов стека.
  • INDEX — Доступ к элементу списка или словаря по ключу/индексу из стека.
  • APPEND [var] — Добавление элемента в существующий список или словарь.
  • PRINT — Вывод верхнего элемента стека в консоль.
  • RETURN - yield в моей vm.
  • HALT — Завершение исполнения.

Но, если что почитайте src/proc.py.

VM вроде понятная.

Может быть немного проблемно понять стек, но если чуть-чуть поиспытать то у вас все получится.

Также у нас есть инлайнинг:

.func TEST
 PUSH 1
 PUSH 2
 ADD
 STORE a
.end

Вот как вызвать такое:

TEST

Лучше делать JMP из функций.

И ещё кстати появились метки:

# Программа которая слушает порт
.func receive
 FETCH my_port
 RECV
 STORE res
 FETCH res
 PRINT
.end

CREATE_PORT
STORE my_port

:LOOP
  receive
JMP :LOOP

Что-то вроде так ^-^.

В последнее время добавил:

  1. ^ - пробел (hello^world)
  2. ~ - срез строки (hello~world выдаст: hello world)

В-третьих, копируйте все файлы из src на контроллер. Не саму папку.

В-четвертых, py handlers - ваш способ общаться с железкой без VM.

Как регистрировать?

ipc_obj.mk_py_h(handler)

В них приходят сообщения:

(id_вашего_py_handler, куда_вы_отвечаете, сообщение_процесса)

То есть вы делаете:

ipc_obj.syscall_send(py_handler_id, msg[1], res)

Пример такого хандлера:

def my_handler(msg, ipc):
    ipc.syscall_send(msg[0], msg[1], 'hello!')

Ещё предупреждаю - не пишите циклы в py handler'ах. Они не вытесняются и не планируются.

Также, можете почитать rMachIPC() из файла src/ipc.py.

Потребление памяти.

Вот тест памяти:

from sched import *
from ipc import *
import gc

sched = PrioSched()
ipc = rMachIPC()
kernel = Kernel(sched, ipc)

gc.collect()

free = gc.mem_free()
alloc = gc.mem_alloc()
total = free + alloc

usage_pct = (alloc / total) * 100

f_kb = free / 1024
a_kb = alloc / 1024

print(f"{f_kb:.1f}")
print(f"{a_kb:.1f} ({usage_pct:.2f}%)")

Результат:

181.0
19.9 (9.69%)

181.5 - свободно КБ.

19.9 - КБ используется.

9.90% занято.

Что делать если проблемы с написанием?

Во-первых, платформа ещё в тестировании.

Во-вторых, при багах:

dimasoft976@gmail.com

Мой gmail, пишите что хотите.

Кстати.

Если увидите DIED в конце работы main.py - это нормально. Просто там небольшой race condition.

Morty Proxy This is a proxified and sanitized view of the page, visit original site.