14.3.3. Fájlrendszer-higiénia¶
Egy kiszállított kamera flash- és SD-tárolója megtelik olyan fájlokkal, amelyeket egyetlen kezelő sem fog kézzel kitakarítani. Két döntés ezzel a tárolóval kapcsolatban a termék egész élettartamára megmarad: melyik felület tárolja az adatok melyik fajtáját, és hogyan épülnek fel a könyvtárak, hogy a fájlműveletek továbbra is működjenek, ahogy az alkalmazás egyre több rekordot halmoz fel.
14.3.3.1. Hová kerülnek a dolgok¶
A kód és az erőforrások a fagyasztott modulokban és a ROMFS-ben utaznak, amelyeket a build a kiszállítás idején rögzít. Az alkalmazás állapotának – mindennek, amit az alkalmazás futás közben ír, mindennek, ami növekszik, mindennek, ami az indítások között változik – máshol kell laknia. A kamera két írható felületet kínál erre:
Belső flash memória a
/flashútvonalon: egy kis írható fájlrendszer, amely az alkalmazás kódjának futása előtt csatolódik. A megfelelő hely kis, fix méretű, újraindítást túlélő rekordok számára: az alkalmazás által futás közben frissített konfiguráció, a legutóbbi ismert kalibráció, egy gördülő számláló, egy egysoros jelzőfájl, amely azt mondja: „ezt a kamerát provisionálták”. Korlátozott írási ciklusok – a modern belső flash memória szektoronként több ezertől több tízezerig terjedő írást visel el, nem milliókat, így az írásoknak ritkáknak kell lenniük, nem képkockánkéntinek.SD-kártya a
/sdcardútvonalon: egy nagyobb írható fájlrendszer, amely akkor csatolódik, amikor kártya van jelen. A megfelelő hely terjedelmes, változó méretű fájlok számára: kép- és videófelvételek, naplófájlok, modell-finomhangolási adatok, bármi, ami megabájtokra vagy gigabájtokra nőhet. Nagyobb írási kapacitás, mint a belső flash memóriáé, de még mindig véges; eltávolítható, cserélhető, és az a felület, amely a legvalószínűbben eltűnik, amikor az alkalmazás éppen írás közben van.
A megfelelő válasz arra, hogy hová írjunk valamit, szinte mindig az, hogy „flash a kis, fix rekordoknak, SD minden másnak”. A kettő nem cserélhető fel: egy olyan alkalmazás, amely a gördülő naplófájlját a /flash felületre firkálja, fel fogja emészteni a flash írási élettartamát egy olyan telepítésben, amely SD-n teljesen rendben lett volna.
14.3.3.2. Mindkettőt kezelje hibázhatóként¶
A /flash és a /sdcard egyaránt hibázhat. Az SD-kártyát ki lehet venni, a flash megsérülhet írás közbeni áramkimaradás miatt, mindkettő kifogyhat a helyből, és bármelyiken végzett bármely művelet OSError hibát válthat ki olyan okokból, amelyeket az alkalmazás a terepen nem fog tudni diagnosztizálni.
Két minta segít az alkalmazásnak túlélni ezt:
A csatolásokat és műveleteket try blokkokba csomagolja. Minden
open(),os.listdir(),os.rename()a felhasználói adatok útvonalain potenciálisan hibázhat. Kapja el azOSErrorhibát, naplózza, és térjen vissza egy meghatározott alternatívához – írjon a/flashfelületre, ha a/sdcardeltűnt, hagyja ki a műveletet, ha egyik sem elérhető.Atomi írások az áramkimaradást túlélni köteles fájlokhoz. Írjon egy ideiglenes útvonalra, zárja le a leírót, majd
os.rename()művelettel írja felül az élő nevet. Vagy az átnevezés sikerült és a fájl az új verzió, vagy nem sikerült és a fájl a régi verzió. Nincs harmadik állapot, amelyben a fájl félig van megírva:import os def write_config_atomic(path, contents): tmp = path + '.tmp' with open(tmp, 'w') as f: f.write(contents) f.flush() os.rename(tmp, path)
A minta flash-en és SD-n egyaránt működik. Nem működik olyan fájlokra, amelyek elég nagyok ahhoz, hogy az ideiglenes fájl felhasználja a fájlrendszer szabad helyét; tartsa fenn kis rekordokhoz.
14.3.3.3. A lassú könyvtár csapdája¶
A MicroPython VFS nem indexeli a könyvtárak tartalmát úgy, ahogy egy asztali fájlrendszer teszi. Az os.listdir() és az os.stat() lineárisan járja végig a mögöttes fájltáblát. Egy száz fájlt tartalmazó könyvtár rendben van; egy tízezer fájlt tartalmazó könyvtár használhatatlanul lassú, ahol minden os.listdir() másodperceket vesz igénybe, és minden open() áthaladás közben ellenőrzi a táblát.
Azok az alkalmazások, amelyek naplókat vagy felvételeket írnak lemezre, érik el ezt a leggyorsabban. Egy naiv /sdcard/logs/<timestamp>.log séma, amely percenként nyit egy új fájlt, egy év telepítés alatt félmillió fájllal tölti meg a logs/ könyvtárat. Jóval ez előtt az alkalmazás elkezdi elveszíteni a képkockasebességét, mert minden fájlmegnyitás tovább tart, mint egy képkocka-intervallum.
A megfelelő minta az, hogy a fájlokat dátumozott alkönyvtárak fája között osztja szét, így egyetlen könyvtár sem tartalmaz soha néhány száznál több bejegyzést:
import os
import time
LOG_ROOT = '/sdcard/logs'
def log_path(now=None):
if now is None:
now = time.localtime()
year, month, day, hour = now[0], now[1], now[2], now[3]
directory = '{}/{:04d}/{:02d}/{:02d}'.format(
LOG_ROOT, year, month, day)
_makedirs(directory)
return '{}/{:02d}.log'.format(directory, hour)
def _makedirs(path):
# os.makedirs equivalent -- create each level if missing
parts = path.split('/')
for i in range(2, len(parts) + 1):
sub = '/'.join(parts[:i])
try:
os.mkdir(sub)
except OSError:
pass
Egy év óránkénti egy fájlt jelentő naplózás immár 365 napi könyvtárra van elosztva, amelyek mindegyike legfeljebb 24 fájlt tartalmaz; az os.listdir() bármely egyetlen könyvtáron olcsó marad, és az alkalmazás képkocka-ciklusa nem akad el a fájlműveleteken, ahogy a telepítés öregszik.
Ugyanez az elv vonatkozik a képfelvételekre, az érzékelő nyomvonalaira, vagy bármi másra, amihez az alkalmazás eseményenként egy fájlt ír. Ha az eseményráta magas, a fának mélyebbnek kell lennie (év/hónap/nap/óra, vagy év/hónap/nap/óra/perc), hogy minden levélkönyvtár kicsi maradjon. Ha az eseményráta alacsony, egy év/hónap fa elég.
14.3.3.4. Eszközönkénti útvonalak¶
Egy egynél több kamerából álló flottában a naplófájloknak azonosítaniuk kell, melyik fizikai egységből származnak. A machine.unique_id() egy hardveres azonosítót ad vissza, amelyet a gyárban sütöttek a kamerába; ez ugyanaz az érték az újraindítások során, a firmware-frissítések során és az SD-kártyacserék során. Ágyazza be a naplóútvonalba vagy a naplórekordokba, és egy kezelő, aki egy halom SD-kártyát vagy egy központosított naplót néz, meg tudja mondani, melyik melyik:
import binascii
import machine
UNIT_ID = binascii.hexlify(machine.unique_id()).decode()
LOG_ROOT = '/sdcard/logs/' + UNIT_ID
A dátumozott alkönyvtár-mintával kombinálva az elrendezés a következővé válik: /sdcard/logs/<unit-id>/2026/06/09/14.log – egy egység egyórányi rekordja, egy elég sekély könyvtárban a bejáráshoz, egy olyan útvonalon, amely magán a fájlrendszeren megnevezi az egységet.
14.3.3.5. Mindezt összerakva¶
Egy kiszállított kamera írható tárolója nagyjából így néz ki:
/flash– konfiguráció, kalibráció, egy provisioning-jelző. Ritkán írt, gyakran olvasott. Atomi-átnevezés minta minden olyan fájlhoz, amelynek elvesztése megakadályozná a következő indítást./sdcard/logs/<unit-id>/<year>/<month>/<day>/<hour>.log– a működési napló. Folyamatosan írt, az útvonal által forgatott, soha nem több ezer testvérrel rendelkező könyvtáron keresztül írt./sdcard/captures/<unit-id>/<year>/<month>/<day>/– az alkalmazás által készített kép- vagy videófelvételek. Ugyanaz a faalak, ugyanaz az ok.
Ez az elrendezés körülbelül húsz sornyi kódba kerül az alkalmazásnak, és megmenti azoktól a hibamódoktól, amelyek a telepítés kezdete után hónapokkal leveszik a kamerát a lábáról.