307 lines
11 KiB
TeX
307 lines
11 KiB
TeX
\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}
|