267 lines
15 KiB
TeX
267 lines
15 KiB
TeX
\documentclass[12pt]{article}
|
|
\usepackage[T1]{fontenc}
|
|
\usepackage[utf8]{inputenc}
|
|
\usepackage[polish]{babel}
|
|
\usepackage[a4paper,margin=2.0cm]{geometry}
|
|
\usepackage{array}
|
|
\usepackage{tabularx}
|
|
\usepackage{xcolor}
|
|
\usepackage{listings}
|
|
\usepackage{hyperref}
|
|
\usepackage{fancyhdr}
|
|
\usepackage{lastpage}
|
|
\usepackage{enumitem}
|
|
|
|
\definecolor{accent}{HTML}{16324A}
|
|
\definecolor{accentlight}{HTML}{EEF3F7}
|
|
\definecolor{rulegray}{HTML}{D7DEE5}
|
|
\hypersetup{colorlinks=true,linkcolor=blue,urlcolor=blue,citecolor=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}{??}
|
|
\newcommand{\CardSlug}{heap4}
|
|
\newcommand{\CardVersion}{v0.1}
|
|
\newcommand{\DocumentKey}{\PublisherDomain/\CardArea/\CardSeries/\CardNumber/\CardSlug/\CardVersion}
|
|
\newcommand{\DocumentUUID}{4654524c-73f2-46df-83a3-d448ba6d6c5a}
|
|
\renewcommand{\lstlistingname}{Listing}
|
|
\renewcommand{\lstlistlistingname}{Lista listingów}
|
|
|
|
\lstset{basicstyle=\ttfamily\small,columns=fullflexible,keepspaces=true,frame=single,breaklines=true,showstringspaces=false,numbers=left,numberstyle=\tiny\color{accent},stepnumber=1,numbersep=8pt,backgroundcolor=\color{accentlight},rulecolor=\color{rulegray},captionpos=b}
|
|
\newcommand{\TaskSection}[5]{\section{Task #1: #3}\subsection*{Algorytm i zakresy linii}#4\lstinputlisting[language=C,caption={Task #1: #3},label={lst:task#1-c}]{../src/tasks/#2.c}\subsection*{Co sprawdzić w ASM}#5\lstinputlisting[basicstyle=\ttfamily\scriptsize,caption={ASM dla task #1},label={lst:task#1-asm}]{../build/#2/#2.s}}
|
|
|
|
\pagestyle{fancy}
|
|
\fancyhf{}
|
|
\lhead{\textbf{FreeRTOS: heap\_4}}
|
|
\rhead{\small \DocumentAuthor, \DocumentYear}
|
|
\lfoot{\scriptsize commit \BuildCommit}
|
|
\cfoot{\scriptsize \thepage/\pageref{LastPage}}
|
|
\rfoot{\scriptsize uuid \DocumentUUID}
|
|
\renewcommand{\headrulewidth}{0.4pt}
|
|
\setlength{\headheight}{18pt}
|
|
\setlength{\footskip}{24pt}
|
|
|
|
\begin{document}
|
|
\sloppy
|
|
\noindent
|
|
\begin{tabular}{@{}lp{0.72\textwidth}@{}}
|
|
Project: & FreeRTOS: \texttt{heap\_4} \\
|
|
Subject: & Informatyka \\
|
|
Level: & 1, Zakres Podstawowy, PP \\
|
|
Topic: & dynamiczna alokacja pamięci, lista wolnych bloków, scalanie bloków \\
|
|
Revision date: & 02.05.2026 \\
|
|
Status: & Draft \\
|
|
Card in series: & \small\texttt{\CardNumber/\CardCount} \\
|
|
Card version: & \small\texttt{\CardVersion} \\
|
|
Card key: & \small\texttt{\DocumentKey} \\
|
|
UUID: & \small\texttt{\DocumentUUID} \\
|
|
Commit: & \small\texttt{\BuildCommit} \\
|
|
Scope: & pierwsza karta serii FreeRTOS; model dydaktyczny mechaniki \texttt{heap\_4.c} \\
|
|
Files used: & \small
|
|
\begin{tabular}[t]{@{}l@{}}
|
|
Task01: \texttt{task01\_heap\_map.c} \\
|
|
Task02: \texttt{task02\_free\_list.c} \\
|
|
Task03: \texttt{task03\_heap\_init.c} \\
|
|
Task04: \texttt{task04\_alignment.c} \\
|
|
Task05: \texttt{task05\_malloc\_first\_fit.c} \\
|
|
Task06: \texttt{task06\_split\_block.c} \\
|
|
Task07: \texttt{task07\_free\_insert.c} \\
|
|
Task08: \texttt{task08\_coalescing.c} \\
|
|
Task09: \texttt{task09\_config\_macros.c} \\
|
|
Task10: \texttt{task10\_heap4\_demo.c} \\
|
|
Common: \texttt{heap4\_common.h} \\
|
|
Runtime: \texttt{memops.c} \\
|
|
Startup: \texttt{crt0.S}
|
|
\end{tabular}
|
|
\end{tabular}
|
|
|
|
\vspace{0.8em}
|
|
\noindent\rule{\textwidth}{0.5pt}
|
|
|
|
\section{Cel}
|
|
Ta karta zaczyna serię FreeRTOS od \texttt{heap\_4}, ponieważ ten plik jest
|
|
naturalnym pomostem po kartach z języka C. Wymaga wskaźników, tablic,
|
|
struktur, listy jednokierunkowej, \texttt{static}, \texttt{typedef}, makr oraz
|
|
arytmetyki adresów, ale nie wymaga jeszcze schedulera, przerwań ani
|
|
uruchamiania tasków.
|
|
|
|
Przykłady są małym modelem edukacyjnym mechaniki \texttt{heap\_4.c}. Nie są
|
|
kopią pliku upstream FreeRTOS. Na RV32I wyniki są zapisywane w globalnych
|
|
zmiennych \texttt{volatile}; \texttt{printf} jest dostępny wyłącznie w wariancie
|
|
hostowym przez flagę \texttt{-DHOST\_PRINTF=1}.
|
|
|
|
W tej karcie słowo „sterta” oznacza obszar pamięci, którym zarządza alokator.
|
|
W \texttt{heap\_4} taki obszar jest zwykle statyczną tablicą bajtów, więc linker
|
|
może umieścić go w sekcji \texttt{.bss}. Sama sekcja \texttt{.bss} nie jest jednak
|
|
alokacją dynamiczną: dopiero kod \texttt{pvPortMalloc}/\texttt{vPortFree} albo
|
|
model z tej karty dzieli bajty tego bufora na bloki, prowadzi listę wolnych
|
|
bloków i wykonuje scalanie. W Task 03 widać ten moment przejścia od zwykłego
|
|
statycznego bufora do sterty zarządzanej przez algorytm.
|
|
|
|
Listingi C i ASM mają numerowane linie. Opisy w sekcjach tasków odnoszą się
|
|
do numerów widocznych po lewej stronie listingów C.
|
|
|
|
\lstinputlisting[language=C,caption={Wspólny nagłówek dla karty \texttt{heap\_4}},label={lst:common-h}]{../src/tasks/heap4_common.h}
|
|
|
|
\subsection{\texttt{static inline} w nagłówku}
|
|
Helpery z Listingu~\ref{lst:common-h} są zapisane jako \texttt{static inline},
|
|
bo każdy task jest osobnym programem i dołącza ten sam nagłówek. \texttt{static}
|
|
daje prywatną kopię funkcji w każdym pliku \texttt{.c}, a \texttt{inline}
|
|
pozwala kompilatorowi wstawić krótkie obliczenia, takie jak wyrównanie rozmiaru
|
|
lub sprawdzenie bitu alokacji, bez kosztu zwykłego wywołania funkcji.
|
|
|
|
\section{Mapa karty}
|
|
\begin{tabularx}{\textwidth}{|p{2.1cm}|X|}
|
|
\hline
|
|
Task & Temat \\
|
|
\hline
|
|
1 & Mapa pojęć: obszar sterty, nagłówek bloku i bit alokacji. \\
|
|
\hline
|
|
2 & Lista wolnych bloków jako lista jednokierunkowa. \\
|
|
\hline
|
|
3 & Inicjalizacja sterty, pierwszy wolny blok i wartownik końca listy. \\
|
|
\hline
|
|
4 & Wyrównanie rozmiarów żądań i adresu początku sterty. \\
|
|
\hline
|
|
5 & Pierwszy pasujący blok, czyli zasada \texttt{first fit}. \\
|
|
\hline
|
|
6 & Podział dużego bloku na część przydzieloną i pozostały blok wolny. \\
|
|
\hline
|
|
7 & Zwracanie bloku i wstawianie go do listy według adresu. \\
|
|
\hline
|
|
8 & Scalanie sąsiednich wolnych bloków. \\
|
|
\hline
|
|
9 & Makra konfiguracyjne oraz hostowy \texttt{TRACE\_PRINTF}. \\
|
|
\hline
|
|
10 & Mini-demonstracja sekwencji \texttt{malloc/free}. \\
|
|
\hline
|
|
\end{tabularx}
|
|
|
|
\TaskSection{01}{task01_heap_map}{mapa \texttt{heap\_4}}{
|
|
\begin{itemize}[leftmargin=1.6em]
|
|
\item Linie 3--7 definiują zmienne \texttt{volatile}, czyli obserwowalne wyniki taska.
|
|
\item Linie 11--16 tworzą pojedynczy blok i zapisują w jego polu \texttt{size} rozmiar z bitem alokacji.
|
|
\item Linie 18--22 rozbijają nagłówek bloku na wartości pomocnicze: rozmiar nagłówka, rozmiar wyrównany, bit zajętości, rozmiar całego bloku i część użytkownika.
|
|
\item Linie 24--27 wypisują te same wartości tylko w wariancie hostowym; na RV32I makro \texttt{TRACE\_PRINTF} znika.
|
|
\item Wynik sprawdzaj w \texttt{g\_header\_size}, \texttt{g\_allocated\_bit\_set}, \texttt{g\_total\_block\_bytes} i \texttt{g\_user\_bytes}.
|
|
\end{itemize}
|
|
}{
|
|
W ASM szukaj operacji ustawiania najwyższego bitu rozmiaru oraz zapisów do symboli globalnych \texttt{g\_*}. Dostęp do pól \texttt{block.next} i \texttt{block.size} powinien być zwykłym zapisem pod adres bazowy stosu z przesunięciem.
|
|
}
|
|
\TaskSection{02}{task02_free_list}{lista wolnych bloków}{
|
|
\begin{itemize}[leftmargin=1.6em]
|
|
\item Linie 7--16 implementują sumowanie rozmiarów bloków na liście.
|
|
\item Linie 18--28 przechodzą po tej samej liście i zapamiętują największy blok.
|
|
\item Linie 30--39 liczą liczbę węzłów listy.
|
|
\item Linie 43--55 budują ręcznie listę \texttt{start -> a -> b -> c -> NULL}.
|
|
\item Linie 57--59 uruchamiają trzy funkcje pomiarowe i zapisują wyniki w \texttt{volatile}.
|
|
\end{itemize}
|
|
}{
|
|
W ASM zwróć uwagę na pętle po wskaźniku \texttt{block = block->next}. To jest podstawowy idiom przechodzenia po liście jednokierunkowej, widoczny jako ładowanie pola \texttt{next} i skok warunkowy.
|
|
}
|
|
\TaskSection{03}{task03_heap_init}{inicjalizacja sterty}{
|
|
\begin{itemize}[leftmargin=1.6em]
|
|
\item Linie 3--7 definiują bufor sterty, węzeł startowy listy i wskaźnik końcowego wartownika.
|
|
\item Linie 20--23 wyrównują początek sterty i obcinają dostępną długość do wielokrotności wyrównania.
|
|
\item Linie 25--28 ustawiają adres i pola końcowego wartownika.
|
|
\item Linie 30--34 tworzą pierwszy wolny blok i podpinają go za \texttt{start}.
|
|
\item Linie 39--43 wykonują inicjalizację i zapisują trzy wartości kontrolne.
|
|
\end{itemize}
|
|
}{
|
|
W ASM szukaj maskowania dolnych bitów adresu i rozmiaru. Warto też porównać zapisy do \texttt{first->next}, \texttt{first->size}, \texttt{start.next} i \texttt{start.size}; każdy z nich jest dostępem do pola struktury.
|
|
}
|
|
\TaskSection{04}{task04_alignment}{wyrównanie}{
|
|
\begin{itemize}[leftmargin=1.6em]
|
|
\item Linie 3--6 definiują wyniki pokazujące wpływ wyrównania.
|
|
\item Linie 14--16 liczą rzeczywiste rozmiary bloków dla żądań 1, 9 i 17 bajtów, po dodaniu nagłówka.
|
|
\item Linie 18--20 biorą celowo nie-wyrównany adres \texttt{\&bytes[1]} i wyliczają przesunięcie do kolejnej granicy wyrównania.
|
|
\item Linie 22--24 są hostowym wypisem kontrolnym.
|
|
\end{itemize}
|
|
}{
|
|
W ASM znajdź operacje \texttt{and} używane do testowania dolnych bitów oraz dodawanie brakującej liczby bajtów do wyrównania. To jest ten sam mechanizm, którego używa alokator dla rozmiaru bloku i początku sterty.
|
|
}
|
|
\TaskSection{05}{task05_malloc_first_fit}{\texttt{first fit}}{
|
|
\begin{itemize}[leftmargin=1.6em]
|
|
\item Linie 13--30 inicjalizują stertę z jednym wolnym blokiem oraz wartownikiem końca.
|
|
\item Linie 38--40 zamieniają żądanie użytkownika na całkowity rozmiar bloku i ustawiają początek przeszukiwania listy.
|
|
\item Linie 42--51 realizują algorytm \texttt{first fit}: idą po liście do pierwszego bloku o wystarczającym rozmiarze.
|
|
\item Linie 44--47 zdejmują znaleziony blok z listy wolnych, oznaczają go jako zajęty i zwracają adres danych użytkownika.
|
|
\item Linie 61--67 wykonują alokację i zapisują, czy się udała, jaki rozmiar dostał blok i ile wolnego miejsca zostało na liście.
|
|
\end{itemize}
|
|
}{
|
|
W ASM obserwuj porównanie \texttt{block->size >= total}, zmianę \texttt{previous->next} oraz dodanie \texttt{sizeof(HeapBlock)} do adresu bloku przed zwróceniem wskaźnika użytkownika.
|
|
}
|
|
\TaskSection{06}{task06_split_block}{podział bloku}{
|
|
\begin{itemize}[leftmargin=1.6em]
|
|
\item Linie 3--12 definiują rozmiar sterty, minimalny sensowny rozmiar reszty i zmienne wynikowe.
|
|
\item Linie 14--28 tworzą jeden duży wolny blok.
|
|
\item Linie 38--44 szukają bloku pasującego do żądania i zapamiętują jego pierwotny rozmiar.
|
|
\item Linie 45--50 wykonują podział: nowy blok \texttt{remainder} zaczyna się pod adresem \texttt{block + total}.
|
|
\item Linie 51--56 obsługują przypadek bez podziału, oznaczają blok jako zajęty i zwracają adres użytkownika.
|
|
\item Linie 65--73 liczą pozostałe wolne bloki, a linie 81--87 zapisują wynik demonstracji.
|
|
\end{itemize}
|
|
}{
|
|
W ASM szukaj arytmetyki adresów dla \texttt{remainder}: adres bloku plus \texttt{total}. To najważniejszy fragment pokazujący, że dzielenie bloku jest tylko przesunięciem wskaźnika i zapisaniem nowego nagłówka.
|
|
}
|
|
\TaskSection{07}{task07_free_insert}{\texttt{free} i wstawianie}{
|
|
\begin{itemize}[leftmargin=1.6em]
|
|
\item Linie 11--20 wstawiają blok do listy w miejscu wynikającym z porządku adresów.
|
|
\item Linie 22--32 modelują \texttt{free}: z adresu użytkownika wracają do nagłówka bloku, czyszczą bit alokacji i wywołują wstawianie.
|
|
\item Linie 34--54 zawierają funkcje kontrolne liczące węzły oraz łączny rozmiar wolnej pamięci.
|
|
\item Linie 63--76 budują układ \texttt{a}, \texttt{b}, \texttt{c}, gdzie \texttt{b} jest zajętym blokiem między dwoma wolnymi.
|
|
\item Linie 78--83 zwalniają \texttt{b} i sprawdzają, czy lista ma kolejność \texttt{a -> b -> c -> end}.
|
|
\end{itemize}
|
|
}{
|
|
W ASM zwróć uwagę na porównywanie adresów w pętli z linii 15. To porządkowanie po adresie jest przygotowaniem do scalania bloków w następnym tasku.
|
|
}
|
|
\TaskSection{08}{task08_coalescing}{scalanie bloków}{
|
|
\begin{itemize}[leftmargin=1.6em]
|
|
\item Linie 11--17 znajdują miejsce w liście, w którym powinien znaleźć się zwracany blok.
|
|
\item Linie 19--25 sprawdzają sąsiedztwo z prawym blokiem; jeśli adres końca zwracanego bloku jest adresem następnego, rozmiary są łączone.
|
|
\item Linie 27--32 sprawdzają sąsiedztwo z lewym blokiem i ewentualnie dołączają scalony wynik do lewej strony.
|
|
\item Linie 35--44 liczą końcową liczbę wolnych bloków.
|
|
\item Linie 52--65 budują trzy sąsiednie bloki w pamięci, a linia 67 uruchamia scalanie środkowego bloku.
|
|
\item Linie 69--71 zapisują, czy po scaleniu został jeden blok i jaki ma rozmiar.
|
|
\end{itemize}
|
|
}{
|
|
W ASM szukaj testu \texttt{(uint8\_t *)block + block->size == iterator->next}. To jest sedno scalania: alokator nie porównuje indeksów, tylko rzeczywiste adresy końca i początku bloków.
|
|
}
|
|
\TaskSection{09}{task09_config_macros}{makra konfiguracyjne}{
|
|
\begin{itemize}[leftmargin=1.6em]
|
|
\item Linie 3--9 definiują wartości domyślne tylko wtedy, gdy nie zostały podane z zewnątrz przez kompilator.
|
|
\item Linia 11 tworzy wartość pochodną od konfiguracji i wyrównania.
|
|
\item Linie 20--22 zapisują wartości makr do zmiennych \texttt{volatile}, żeby były widoczne w debugerze.
|
|
\item Linie 24--28 pokazują kompilację warunkową: host ustawia \texttt{g\_trace\_is\_host\_only} na 1, RV32I na 0.
|
|
\item Linie 30--32 wypisują wynik tylko w wariancie hostowym.
|
|
\end{itemize}
|
|
}{
|
|
W ASM dla RV32I nie powinno być wywołania \texttt{printf}. Sprawdź też, że makra konfiguracyjne nie są zmiennymi w pamięci, tylko stałymi wstawionymi do kodu przez preprocesor.
|
|
}
|
|
\TaskSection{10}{task10_heap4_demo}{mini \texttt{heap\_4}}{
|
|
\begin{itemize}[leftmargin=1.6em]
|
|
\item Linie 18--38 to funkcje diagnostyczne przechodzące po liście wolnych bloków.
|
|
\item Linie 40--62 wstawiają blok i scalają go z prawym oraz lewym sąsiadem.
|
|
\item Linie 64--88 inicjalizują stertę: wyrównują początek, ustawiają wartownika, pierwszy wolny blok i liczniki wolnej pamięci.
|
|
\item Linie 90--131 implementują alokację: sprawdzają rozmiar żądania, szukają bloku, dzielą go lub zdejmują w całości z listy, oznaczają jako zajęty i aktualizują minimum wolnej pamięci.
|
|
\item Linie 133--149 implementują zwalnianie: wracają z adresu użytkownika do nagłówka, sprawdzają bit alokacji, przywracają rozmiar i scalają blok.
|
|
\item Linie 157--169 wykonują sekwencję \texttt{malloc/free} i zapisują stan końcowy alokatora.
|
|
\end{itemize}
|
|
}{
|
|
W ASM porównaj trzy fragmenty: inicjalizację listy, pętlę szukania wolnego bloku oraz wywołanie \texttt{insert\_block\_merge}. To pokazuje, że pełny model składa się z prostych operacji wskaźnikowych poznanych w poprzednich taskach.
|
|
}
|
|
|
|
\section{Polecenia}
|
|
\begin{enumerate}[leftmargin=1.6em]
|
|
\item Zbuduj kartę dla RV32I poleceniem \texttt{make tasks}.
|
|
\item Zbuduj wariant hostowy poleceniem \texttt{make host}; wypisywanie działa tylko przez \texttt{HOST\_PRINTF}.
|
|
\item W tasku 6 zmień próg \texttt{MIN\_SPLIT\_SIZE} i sprawdź, kiedy blok nie jest dzielony.
|
|
\item W tasku 8 zmień rozmiary bloków tak, aby scalanie działało tylko z prawym sąsiadem.
|
|
\item W tasku 10 dodaj większą alokację, która ma się nie udać, i zapisz status w nowej zmiennej \texttt{volatile}.
|
|
\end{enumerate}
|
|
|
|
\end{document}
|