Ile mamy pamięci?
Jak już wiemy z poprzedniego odcinka tego kursu, MicroPython korzysta z pamięci Flash mikrokontrolera do przechowywania plików. Możemy zapisywać pliki wykonywalne *.py, pliki uprzednio skompilowane *.mpy, a także pliki wszystkich innych typów. Nic nie stoi na przeszkodzie, by zainstalować dodatkowy dysk w postaci karty MicroSD lub pamięci EEPROM i na nim przechowywać dodatkowe pliki, jeżeli wbudowana pamięć nam nie wystarcza.
Przed wykonaniem skryptu *.py najpierw jest on kompilowany przez mikrokontroler. Tak powstaje bytecode, który zapisywany jest w pamięci RAM, a następnie może być on wykonany przez wirtualną maszynę MicroPythona. Ów bytecode może oczywiście tworzyć różne zmienne, tablice i inne obiekty, które również są przechowywane w pamięci RAM. Proces kompilacji i wykonywania programu jest szczegółowo opisany w dokumentacji MicroPythona, dostępnej pod adresem [2].
W pierwszej części tego odcinka napiszemy prosty moduł, który poda nam informacje o tym, ile wykorzystujemy pamięci Flash i RAM, a także ile zostało nam jeszcze wolnej pamięci.
Przeanalizujmy kod zaprezentowany na listingu 1. Zawiera on tylko dwie funkcje.
# Plik mem_used.py
import os # 1
import gc # 2
def print_rom_used(path = „”): # 3
stats = os.statvfs(path) # 4
block_size = stats[0] # 5
total_blocks = stats[2] # 6
free_blocks = stats[3] # 7
total_rom = total_blocks * block_size
used_rom = (total_blocks – free_blocks) * block_size
print(f”ROM: {used_rom} / {total_rom}”) # 8
def print_ram_used(): # 9
gc.collect() # 10
total_ram = gc.mem_alloc() + gc.mem_free() # 11
used_ram = gc.mem_alloc()
print(f”RAM: {used_ram} / {total_ram}”)
if __name__ == „__main__”: # 12
print_rom_used()
print_ram_used()
Listing 1. Kod pliku mem_used.py
W pierwszych dwóch liniach importujemy moduły, które będą nam potrzebne. Moduł os zawiera różne funkcje związane z systemem plików, zaś gc to garbage collector, czyli „odśmiecacz” pamięci RAM. Ten drugi jest domyślnie aktywny, a jego zadaniem jest wyszukiwanie oraz usuwanie nieużywanych zmiennych – po to, by zwolnić zajmowaną przez nie pamięć. Moduł gc zawiera różne funkcje do konfiguracji odśmiecania oraz informujące o zajętej i dostępnej pamięci RAM.
W linii 3 rozpoczynamy funkcję, której zadaniem jest wyświetlenie ilości miejsca w pamięci Flash zajmowanego przez wszystkie pliki, znajdujące się pod ścieżką określoną argumentem path. Domyślną wartością tego argumentu jest "", czyli katalog główny w systemie plików ESP32. Możliwość wskazania ścieżki przyda nam się w kolejnych odcinkach, kiedy będziemy tworzyć dyski na karcie MicroSD lub w pamięci EEPROM.
Funkcja statvfs() z modułu os zwraca krotkę, którą zapisujemy do zmiennej stats (linia 4). Zawiera ona różne informacje na temat systemu plików. Następnie, aby ułatwić sobie analizę tej krotki, przepiszemy kilka jej części składowych do zmiennych o nieco bardziej zrozumiałych nazwach. W linii 5 odczytujemy, jaki jest rozmiar bloku pamięci w bajtach, ile jest wszystkich dostępnych bloków na dysku (linia 6) i ile jest wolnych bloków (linia 7). Potem przeprowadzamy kilka prostych obliczeń, aby uzyskać ilość dostępnej i zajętej pamięci w bajtach. W linii 8 wyświetlamy wynik na konsoli, stosując f-string.
Kolejna funkcja wyświetli nam informację nt. dostępnej i zajętej pamięci RAM (linia 9). W linii 10 wywołujemy funkcję collect() z modułu gc. Zadaniem tej funkcji jest uruchomienie odśmiecacza, aby przeskanował pamięć i usunął wszystkie niepotrzebne obiekty. W dwóch kolejnych liniach funkcja mem_alloc() zwraca ilość zajętej pamięci, a mam_free() informuje nas o tym, ile pozostało wolnej pamięci.
Przechodzimy teraz do linii 12 zawierającej instrukcję if, która może nieco zaskoczyć osoby dopiero uczące się Pythona. Plik *.py da się bowiem załadować na dwa sposoby. Może on zostać zaimportowany przez inny moduł (w takiej sytuacji zmienna __name__ przechowuje nazwę modułu zaimportowanego) lub uruchomiony bezpośrednio, np. klawiszem F5 w Thonny. W takiej sytuacji zmienna __name__ ma wartość „__main__”. Identycznie jest w przypadku plików boot.py oraz main.py, które są uruchamiane automatycznie po starcie systemu. Celem linii 12 jest sprawdzenie, czy plik został zaimportowany, czy też uruchomiony. W tej drugiej sytuacji zostanie wykonany kod pod instrukcją if, odpowiedzialny za wywołanie dwóch omówionych wcześniej funkcji.
Zapisz plik z listingu 1 w ESP32 pod nazwą mem_used.py, a następnie wciśnij F5. Powinieneś zobaczyć na konsoli komunikaty podobne do poniższych:
>>> %Run -c $EDITOR_CONTENT
MPY: soft reboot
ROM: 315392 / 6291456
RAM: 1648 / 8190464