Files
lab-rv32i-freertos-heap4/doc/card-plan.md
T
2026-07-20 12:04:14 +02:00

7.4 KiB
Raw Blame History

FC01 — FreeRTOS heap_4: od kursora do wolnych bloków

Decyzja dydaktyczna

To jest jedna 30-minutowa lekcja i bezpośrednia kontynuacja projektu K&R 5.4 z karty C07. Karta nie uczy pięciu implementacji heap jako pięciu równorzędnych tematów. heap_1, heap_2, heap_3 i heap_5 zajmują łącznie maksymalnie minutę kontekstu. Cała właściwa praca dotyczy heap_4.

Task 1 i Task 2 są krótkimi modelami prowadzonymi przez nauczyciela. Uczeń najpierw zapisuje predykcję, ale nie buduje i nie debugguje tych programów osobno podczas lekcji. Task 3, linkujący upstream heap_4.c, jest jedynym centralnym przebiegiem ucznia.

Stan wejściowy z K&R 5.4

Uczeń przychodzi z działającym kodem:

allocbuf[64]
allocp
allocator_reset()
alloc_local(5) -> offset 0
alloc_local(7) -> offset 5
allocp         -> offset 12
alloc_local(0) oraz OOM -> NULL bez przesunięcia kursora

Nie powtarzamy sposobu działania tego kodu. Zaczynamy od jego granicy:

Jak zwolnić obszar 5 B spod offsetu 0, zachować późniejszy obszar 7 B spod offsetu 5 i ponownie wykorzystać powstałą dziurę?

W projekcie nie istnieje operacja zwolnienia pojedynczego obszaru. Nawet proste cofnięcie kursora unieważniłoby wszystkie późniejsze przydziały. Brakuje tożsamości bloku, jego rozmiaru, zbioru dziur i mechanizmu ich łączenia.

Cel w jednym zdaniu

Po 30 minutach uczeń potrafi wyjaśnić i pokazać w debuggerze, jak heap_4 dodaje do liniowej areny nagłówki, wybór first-fit, podział bloku, listę wolnych bloków oraz scalanie sąsiadów, a następnie odróżnić bieżącą ilość wolnej pamięci, największy wolny blok i minimum-ever.

Most K&R 5.4 -> heap_4

K&R 5.4 — stan projektu Ograniczenie heap_4 — nowy mechanizm
allocbuf[64] i jeden allocp istnieje tylko wolny ogon areny ucHeap[] i bloki opisane nagłówkami
wynik to surowy adres allocator nie zna później rozmiaru obszaru BlockLink_t przed payloadem przechowuje rozmiar
kolejne przydziały tylko przesuwają kursor nie można użyć dziury lista wolnych bloków i wybór first-fit
brak zwalniania pojedynczego obszaru reset odzyskuje wszystko naraz vPortFree() odzyskuje wskazany blok
brak relacji między dziurami sąsiednie dziury pozostają rozdzielone porządek adresowy i coalescing w obie strony
tylko sukces albo NULL brak informacji o fragmentacji free, largest, liczba bloków i minimum-ever

W realnym heap_4.c najwyższy bit xBlockSize oznacza blok przydzielony. Uproszczone modele Task 12 pokazują geometrię i listę, ale nie odtwarzają tego bitu. To jawna granica modelu.

Przygotowanie przed lekcją

Budowanie, flashowanie i wykrywanie debug probe nie wchodzą do budżetu 30 minut. Przed wejściem uczniów:

  1. zbuduj Task 3 dla pico2_w / RP2350 RISC-V;
  2. wgraj zweryfikowany obraz i otwórz sesję na tym samym ELF;
  3. ustaw pierwszy breakpoint na heap4_fragmented_checkpoint;
  4. przygotuj widok ucHeap, g_fragmented_stats, g_final_stats, xFreeBytesRemaining i xMinimumEverFreeBytesRemaining;
  5. przygotuj Hazard3 jako awaryjny zamiennik — podczas lekcji użyj jednej platformy, nigdy obu.

heap_4 jest implementacją wybraną i linkowaną przez projekt. Nie jest sprzętowym heapem ani peryferium RP2350.

Przebieg 30 minut

Czas Tryb Działanie i dowód
03 min problem Przypomnij wyłącznie offsety 0 -> 5 -> 12. Uczeń próbuje zwolnić pierwszy obszar bez utraty drugiego i nazywa brakującą operację.
34 min kontekst Jedno zdanie: FreeRTOS dostarcza wymienne implementacje tego samego API; ta karta i oficjalne przykłady Pico 2 W linkują heap_4.
49 min Task 1 — demo Do żądania dodaj nagłówek, wyrównaj rozmiar, wybierz pierwszy pasujący blok i podziel go. Uczeń zapisuje wanted i remainder przed odsłonięciem wyniku.
916 min Task 2 — demo Dla `A:48
1627 min Task 3 — RUN Na przygotowanym Pico 2 W przejdź przez prawdziwy heap_4.c: trzy alokacje, free A i C, checkpoint z dwoma blokami, free B, checkpoint z jednym blokiem, OOM=NULL i jeden malloc-failed hook. Uczeń wypełnia tabelę relacji.
2730 min wyjście Uczeń wymienia trzy dodatki ponad K&R (header, free list, coalescing) i wyjaśnia: suma wolnych bajtów może być większa od największego bloku, a minimum-ever nie rośnie po free.

Task 1 — model nagłówka i splitu

Task 1 nie jest osobnym laboratorium. Na RV32 nagłówek modelu ma 8 B, więc żądanie 13 B daje wanted=24 B i remainder=232 B z areny 256 B. Na AMD64 odpowiednie wartości to 16 B, 32 B i 224 B. Różnica ABI jest tylko pomocą w zrozumieniu sizeof i wyrównania; podczas głównego przebiegu obowiązuje RP2350.

Predykcja ucznia:

free list: [16 B] -> [80 B] -> [160 B]
request + header po wyrównaniu: 24 B
first-fit wybiera: __________
po split: allocated ______ B, remainder ______ B

Wykonywalny Task 1 dowodzi obecnie geometrii splitu jednego bloku. Samo przeszukanie dwóch kandydatów jest elementem demonstracji i realnego pvPortMalloc(), dlatego nie opisujemy Task 1 jako pełnego testu first-fit.

Task 2 — model listy adresowej i coalescingu

pamięć:    [ A:48 FREE ][ B:48 USED ][ C:48 FREE ]
free list: A --------------------------------> C

vPortFree(B)
  1. wstaw B pomiędzy A i C według adresu;
  2. jeśli end(B) == addr(C), połącz B+C;
  3. jeśli end(A) == addr(BC), połącz A+BC;

wynik:     [ ABC:144 FREE ]
free list: A -> END

Uczeń przed odsłonięciem rysuje listę i wpisuje 2 -> 1, 96 -> 144 oraz 48 -> 144 odpowiednio dla liczby bloków, sumy i largest.

Task 3 — centralny przebieg upstream heap_4.c

RESET/INIT
  -> ALLOC A(24)
  -> ALLOC B(40)
  -> ALLOC C(16)
  -> FREE A
  -> FREE C
  -> heap4_fragmented_checkpoint      # 2 free blocks
  -> FREE B
  -> OOM request
  -> task03_debug_checkpoint          # 1 free block, pass=1

Uczeń zapisuje tylko relacje, ponieważ dokładne bajty zależą od ABI i konfiguracji:

Obserwacja Oczekiwany związek
po trzech alokacjach after_allocations < initial_free
stan pofragmentowany free_blocks == 2, largest < total_free
po free(B) final_free == initial_free, free_blocks == 1
low-water mark minimum_ever <= after_allocations i nie rośnie po free
OOM wynik NULL, hook 1, brak uszkodzenia listy

Minimalny dowód w GDB:

p g_fragmented_stats
p g_final_stats
p xFreeBytesRemaining
p xMinimumEverFreeBytesRemaining
p xStart
p pxEnd
x/96bx ucHeap

Kryterium zaliczenia

Uczeń zalicza lekcję, jeżeli:

  • wskaże, dlaczego jeden kursor nie wystarcza do zwolnienia obszaru ze środka;
  • rozróżni nagłówek allocatora od payloadu i stosu sp;
  • przewidzi przejście wolnej listy 2 -> 1 po zwolnieniu B;
  • wyjaśni różnicę total free kontra largest free block;
  • wyjaśni, dlaczego minimum-ever nie wraca po free;
  • pokaże aligned=1 oom=1 hooks=1 asserts=0 pass=1 w Task 3.

Poza lekcją

Jako rozszerzenie lub pracę własną pozostają: pełne czytanie heap_4.c, uruchomienie Task 12 na AMD64 i Hazard3, samodzielny deploy RP2350 oraz porównanie z heap_1, heap_2, heap_3 i heap_5.