\documentclass[10pt]{article} \usepackage[T1]{fontenc} \usepackage[utf8]{inputenc} \usepackage[polish]{babel} \usepackage[a4paper,margin=1.55cm]{geometry} \usepackage{array,tabularx,booktabs} \usepackage{amsmath,amssymb} \usepackage{xcolor,listings} \usepackage{hyperref,fancyhdr,lastpage} \usepackage{enumitem} \definecolor{accent}{HTML}{16324A} \definecolor{accentlight}{HTML}{EEF3F7} \definecolor{rulegray}{HTML}{D7DEE5} \definecolor{success}{HTML}{236B45} \hypersetup{colorlinks=true,linkcolor=accent,urlcolor=blue} \IfFileExists{build-meta.tex}{\input{build-meta.tex}}{\newcommand{\BuildCommit}{local}} \newcommand{\PublisherDomain}{mpabi} \newcommand{\DocumentAuthor}{M. Pabiszczak} \newcommand{\DocumentYear}{2026} \newcommand{\CardArea}{inf} \newcommand{\CardSeries}{rv32i-freertos} \newcommand{\CardNumber}{01} \newcommand{\CardCount}{15} \newcommand{\CardSlug}{heap4} \newcommand{\CardVersion}{v0.3} \newcommand{\DocumentKey}{\PublisherDomain/\CardArea/\CardSeries/\CardNumber/\CardSlug/\CardVersion} \newcommand{\DocumentUUID}{4654524c-73f2-46df-83a3-d448ba6d6c5a} \lstset{ language=C,basicstyle=\ttfamily\scriptsize,columns=fullflexible, keepspaces=true,frame=single,breaklines=true,showstringspaces=false, numbers=none,backgroundcolor=\color{accentlight},rulecolor=\color{rulegray} } \pagestyle{fancy} \fancyhf{} \lhead{\textbf{FC01 · FreeRTOS \texttt{heap\_4}}} \rhead{\small 30 minut · Pico 2 W / RP2350} \lfoot{\scriptsize commit \BuildCommit} \cfoot{\scriptsize \thepage/\pageref{LastPage}} \rfoot{\scriptsize V11.3.0 / \CardVersion} \setlength{\headheight}{14pt} \setlength{\footskip}{19pt} \setlist[itemize]{nosep,leftmargin=1.45em} \setlist[enumerate]{nosep,leftmargin=1.65em} \newcommand{\checkline}{\(\square\)\;} \newcommand{\blank}[1]{\rule{#1}{.2pt}} \begin{document} \sloppy \begin{center} {\LARGE\bfseries FreeRTOS \texttt{heap\_4}: od kursora do wolnych bloków}\par \vspace{.25em} {\large bezpośrednia kontynuacja projektu K\&R 5.4}\par \end{center} \noindent\begin{tabularx}{\textwidth}{@{}p{1.45cm}Xp{1.4cm}X@{}} \toprule Karta & FC01 / \CardCount & Czas & 30 minut \\ Płytka & Raspberry Pi Pico 2 W & Układ & RP2350, rdzeń RISC-V \\ Allocator & FreeRTOS-Kernel V11.3.0 & Źródło & upstream \texttt{heap\_4.c} \\ Wersja & \CardVersion & Data & 19.07.2026 \\ \bottomrule \end{tabularx} \section*{Punkt startowy — tego już nie powtarzamy} Z poprzedniej karty działa projekt: \begin{lstlisting} allocbuf[64]; allocp = allocbuf; alloc_local(5) -> offset 0 alloc_local(7) -> offset 5 allocp -> offset 12 alloc_local(0) i OOM -> NULL; kursor pozostaje na 12 \end{lstlisting} \noindent\fcolorbox{accent}{accentlight}{% \begin{minipage}{.94\textwidth} \textbf{Pytanie projektowe.} Jak zwolnić obszar 5 B spod offsetu 0, zachować późniejszy obszar 7 B spod offsetu 5 i ponownie wykorzystać powstałą dziurę? W naszym projekcie nie istnieje \texttt{free}; reset odzyskuje całą arenę. Samo cofnięcie kursora zniszczyłoby również późniejsze przydziały. \end{minipage}} \section*{Cel lekcji} Potrafię wskazać trzy elementy dodane przez \texttt{heap\_4} do alokatora liniowego --- \textbf{nagłówek bloku, listę wolnych bloków i scalanie} --- oraz potwierdzić ich działanie na prawdziwym \texttt{heap\_4.c}. \section*{Plan 30 minut} \begin{tabularx}{\textwidth}{@{}p{1.55cm}p{3.0cm}X@{}} \toprule Czas & Tryb & Wynik \\ \midrule 0--3 & problem K\&R & nazwać, dlaczego jeden kursor nie zwolni obszaru ze środka \\ 3--4 & kontekst & FreeRTOS linkuje jedną implementację; tutaj pracujemy tylko z \texttt{heap\_4} \\ 4--9 & Demo 1 & przewidzieć nagłówek, first-fit i split \\ 9--16 & Demo 2 & przewidzieć listę adresową i scalenie \texttt{2 -> 1} \\ 16--27 & Task 3 RUN & dwa checkpointy prawdziwego \texttt{heap\_4.c} na Pico 2 W \\ 27--30 & wyjście & odróżnić total free, largest free i minimum-ever \\ \bottomrule \end{tabularx} \vfill \noindent\textbf{Jedna minuta kontekstu:} \texttt{heap\_1} przypomina rosnący kursor bez \texttt{free}; \texttt{heap\_2} nie scala; \texttt{heap\_3} używa alokatora biblioteki C; \texttt{heap\_5} rozszerza mechanikę \texttt{heap\_4} na wiele regionów. Nie są to tematy tej lekcji. \newpage \section{Most: od K\&R 5.4 do \texttt{heap\_4}} \begin{tabularx}{\textwidth}{@{}p{3.5cm}p{4.8cm}X@{}} \toprule K\&R 5.4 — co mamy & Ograniczenie & Co dodaje \texttt{heap\_4} \\ \midrule \texttt{allocbuf[64]}, \texttt{allocp} & istnieje tylko wolny ogon areny & arena podzielona na opisane bloki \\ wynik to surowy adres & później nie znamy rozmiaru obszaru & nagłówek \texttt{BlockLink\_t} przed payloadem \\ przydział przesuwa kursor & nie wykorzystamy dziury & lista wolnych bloków i first-fit \\ reset całej areny & brak zwolnienia jednego obszaru & \texttt{vPortFree()} wskazanego bloku \\ brak relacji między dziurami & sąsiednie dziury są rozdzielone & porządek adresowy i coalescing \\ sukces albo \texttt{NULL} & brak miary fragmentacji & free, largest, block count, minimum-ever \\ \bottomrule \end{tabularx} \subsection*{Nagłówek realnego bloku} \begin{lstlisting} typedef struct A_BLOCK_LINK { struct A_BLOCK_LINK *pxNextFreeBlock; size_t xBlockSize; /* MSB oznacza blok przydzielony */ } BlockLink_t; [ BlockLink_t | payload zwracany przez pvPortMalloc() ] ^ metadata ^ adres widziany przez aplikacje \end{lstlisting} \textbf{Granica modelu:} Task 1--2 używają uproszczonego nagłówka \texttt{\{next,size\}}. Bit allocated i pełne zabezpieczenia oglądamy dopiero w upstream \texttt{heap\_4.c}. \section{Demo 1 — pierwszy pasujący blok i split (5 minut)} Na RP2350/RV32 nagłówek modelu ma 8 B. Dla żądania 13 B: \[ \texttt{wanted}=\operatorname{align8}(8+13)=24\ \mathrm{B} \] \begin{lstlisting} free list przed: [ block 16 B ] -> [ block 256 B ] -> END request po naglowku i align8: 24 B \end{lstlisting} \textbf{Predykcja — wpisz przed odsłonięciem:} \begin{enumerate} \item Pierwszy pasujący blok ma \blank{1.5cm} B. \item Blok przydzielony ma \blank{1.5cm} B, a remainder \blank{1.5cm} B. \item Równość zachowania areny: \blank{2cm} + \blank{2cm} = 256 B. \end{enumerate} \noindent\textbf{Sprawdzenie po predykcji:} \texttt{16 B} nie mieści żądania, więc first-fit wybiera \texttt{256 B}; split daje \texttt{24 B + 232 B}. Realny \texttt{heap\_4} dzieli blok tylko wtedy, gdy reszta jest większa od minimalnego rozmiaru bloku. \noindent\textbf{Nie uruchamiamy osobnego debugowania Task 1.} Model służy do policzenia geometrii; centralny RUN jest w Task 3. \newpage \section{Demo 2 — lista adresowa i coalescing (7 minut)} \begin{center} \texttt{[ A:48 FREE ][ B:48 USED ][ C:48 FREE ]} \end{center} \begin{lstlisting} pamiec: A -------- B -------- C free list: A ------------------> C -> END \end{lstlisting} \textbf{Predykcja:} po \texttt{vPortFree(B)} narysuj listę i uzupełnij: \begin{tabular}{@{}lccc@{}} \toprule Stan & liczba wolnych bloków & suma wolna & największy blok \\ \midrule przed zwolnieniem B & \blank{1.2cm} & \blank{1.2cm} & \blank{1.2cm} \\ po zwolnieniu B & \blank{1.2cm} & \blank{1.2cm} & \blank{1.2cm} \\ \bottomrule \end{tabular} \subsection*{Odsłoń po zapisaniu predykcji} \begin{enumerate} \item Wstaw B między A i C według adresu. \item \texttt{end(B) == address(C)}: połącz B z C. \item \texttt{end(A) == address(BC)}: połącz A z BC. \end{enumerate} \begin{lstlisting} przed: A:48 -> C:48 -> END blocks=2 total=96 largest=48 po: A:144 -> END blocks=1 total=144 largest=144 \end{lstlisting} Porządek po adresie sprawia, że fizycznych sąsiadów można sprawdzić podczas jednego wstawienia. To jest mechanizm funkcji \texttt{prvInsertBlockIntoFreeList()} z \texttt{heap\_4.c}. \section{Task 3 — jedyny centralny RUN (11 minut)} Obraz i sesja muszą być przygotowane przed lekcją. Nie kompilujemy ani nie flashujemy w czasie tych 30 minut. \begin{lstlisting}[language=bash] # wykonane przed lekcja: stemctl debug rp2350 freertos heap4 3 --device /dev/bus/usb/BBB/DDD \end{lstlisting} \begin{lstlisting} RESET/INIT -> ALLOC A(24) -> ALLOC B(40) -> ALLOC C(16) -> FREE A -> FREE C -> heap4_fragmented_checkpoint # STOP 1 -> FREE B -> OOM request -> task03_debug_checkpoint # STOP 2 \end{lstlisting} \textbf{STOP 1:} zapisz liczbę bloków, sumę i largest. Przed \texttt{F5} przewidź, co zmieni \texttt{FREE B}. \textbf{STOP 2:} sprawdź odzyskanie areny, minimum-ever, OOM i hook. \newpage \section{Dowód na Pico 2 W / RP2350} \textbf{Ważne:} \texttt{heap\_4} jest plikiem wybranym i linkowanym przez projekt. Nie jest sprzętowym heapem ani peryferium RP2350. Pico 2 W dostarcza realną pamięć SRAM, w której obserwujemy ten sam algorytm. \subsection*{Minimalny widok GDB} \begin{lstlisting} p g_fragmented_stats p g_final_stats p xFreeBytesRemaining p xMinimumEverFreeBytesRemaining p xStart p pxEnd x/96bx ucHeap \end{lstlisting} \subsection*{Tabela obserwacji} \begin{tabularx}{\textwidth}{@{}p{4.2cm}p{3.0cm}X@{}} \toprule Wielkość & Odczyt & Warunek \\ \midrule \texttt{initial\_free} & \blank{2.3cm} & punkt odniesienia \\ \texttt{after\_allocations} & \blank{2.3cm} & mniejsze niż initial \\ STOP 1: free blocks & \blank{2.3cm} & równe 2 \\ STOP 1: total / largest & \blank{2.3cm} & largest mniejsze niż total \\ STOP 2: final free & \blank{2.3cm} & równe initial \\ STOP 2: free blocks & \blank{2.3cm} & równe 1 \\ minimum-ever & \blank{2.3cm} & nie rośnie po free \\ OOM / hook / asserts / pass & \blank{2.3cm} & \texttt{1 / 1 / 0 / 1} \\ \bottomrule \end{tabularx} \section*{Wyjście — trzy minuty} \begin{enumerate} \item Dlaczego jeden \texttt{allocp} nie wystarcza do zwolnienia obszaru spod offsetu 0 przy zachowaniu obszaru spod offsetu 5?\\[.35em] \blank{.96\linewidth} \item Jak \texttt{vPortFree()} znajduje rozmiar payloadu, skoro dostaje tylko jego adres?\\[.35em] \blank{.96\linewidth} \item Dlaczego 96 wolnych bajtów w dwóch blokach nie pozwala przydzielić jednego bloku 80 B?\\[.35em] \blank{.96\linewidth} \end{enumerate} \subsection*{Zaliczenie} \begin{itemize} \item \checkline wymieniam: header, free list, coalescing; \item \checkline przewiduję \texttt{2 bloki -> 1 blok}; \item \checkline odróżniam payload, metadata allocatora i stos \texttt{sp}; \item \checkline odróżniam current free, largest free i minimum-ever; \item \checkline Task 3 kończy się \texttt{aligned=1 oom=1 hooks=1 asserts=0 pass=1}. \end{itemize} \vfill \noindent\textbf{Poza lekcją:} pełne czytanie upstream \texttt{heap\_4.c}, osobne uruchomienie Task 1--2 na AMD64/Hazard3, samodzielny deploy RP2350 oraz porównanie z \texttt{heap\_1}, \texttt{heap\_2}, \texttt{heap\_3} i \texttt{heap\_5}. \end{document}