\documentclass[10pt]{article} \usepackage[T1]{fontenc}\usepackage[utf8]{inputenc}\usepackage[polish]{babel} \usepackage[a4paper,margin=1.55cm]{geometry} \usepackage{array,tabularx,booktabs,amsmath,amssymb,xcolor,listings,hyperref,fancyhdr,lastpage,enumitem} \definecolor{accent}{HTML}{16324A}\definecolor{accentlight}{HTML}{EEF3F7}\definecolor{rulegray}{HTML}{D7DEE5} \hypersetup{colorlinks=true,linkcolor=accent,urlcolor=blue} \IfFileExists{build-meta.tex}{\input{build-meta.tex}}{\newcommand{\BuildCommit}{local}} \newcommand{\PublisherDomain}{mpabi} \newcommand{\CardArea}{inf} \newcommand{\CardSeries}{freertos-cpp} \newcommand{\CardNumber}{08} \newcommand{\CardCount}{16} \newcommand{\CardSlug}{static-task} \newcommand{\CardVersion}{v00.01} \newcommand{\DocumentUUID}{d9e45417-a37f-487c-813f-fc7a44d4b4f3} \newcommand{\blank}[1]{\rule{#1}{.2pt}} \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{K08 · FreeRTOS C++ · storage taska}}\rhead{\small L07 · dynamic/static i delete} \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} \begin{document}\sloppy \begin{center} {\LARGE\bfseries \texttt{DynamicTask}, \texttt{StaticTask} i lifetime}\par \vspace{.25em}{\large delete logiczny teraz, reclaim dynamiczny przez idle}\par \end{center} \noindent\begin{tabularx}{\textwidth}{@{}p{1.65cm}Xp{1.55cm}X@{}} \toprule Karta & K08 / \CardCount & Czas & 30 minut \\ Platforma & Hazard3 / RV32I & Język & freestanding C++17 \\ Stack & 256 słów & Polityki & heap kontra caller storage \\ Wersja & \CardVersion & UUID karty & \texttt{d9e45417-...} \\ \bottomrule \end{tabularx} \section*{To samo zachowanie, dwa źródła TCB i stacka} \begin{center} \texttt{xTaskCreate -> TCB + stack from heap\_4}\\[.4em] \texttt{xTaskCreateStatic -> caller StaticTask\_t + StackType\_t[]} \end{center} Oba workery liczą 1..1000 i kończą wynikiem 500500. Różni je miejsce, właściciel oraz moment odzyskania pamięci. \noindent\fcolorbox{accent}{accentlight}{\begin{minipage}{.94\textwidth} \textbf{Kontrakt K08.} Null handle i stan completed oznaczają zakończenie wrappera, lecz nie dowodzą jeszcze zwolnienia dynamicznego TCB/stacka. To robi idle. Statyczne bufory nigdy nie stają się własnością kernela. \end{minipage}} \section*{Plan 30 minut} \noindent\begin{tabularx}{\textwidth}{@{}p{1.35cm}p{3.1cm}X@{}} \toprule Czas & Tryb & Dowód \\ \midrule 0--5 & jednostki & 256 słów, liczba bajtów i lokalizacje \\ 5--10 & dynamic & heap delta, TCB i stack w ucHeap \\ 10--15 & static & caller TCB/stack w .bss, heap delta 0 \\ 15--20 & wykonanie & oba wyniki 500500, completed/null \\ 20--25 & delete & dynamic heap nadal zmniejszony przed idle \\ 25--30 & cleanup & verifier blokuje; idle odzyskuje dokładny delta \\ \bottomrule \end{tabularx} \section*{Predykcja} 256 słów RV32I $\times$ \blank{1cm} B/słowo = \blank{2cm} B. Czy \texttt{vTaskDelete(nullptr)} samo natychmiast wywoła dwa free dynamicznego taska? \blank{3cm} \newpage \section{Słowa, bajty i właściciel storage} \noindent\begin{tabularx}{\textwidth}{@{}p{4.1cm}p{5.0cm}X@{}} \toprule Właściwość & DynamicTask & StaticTask \\ \midrule API & \texttt{xTaskCreate} & \texttt{xTaskCreateStatic} \\ TCB & przydziela kernel z heapu & caller przekazuje \texttt{StaticTask\_t} \\ stack & kernel: depth w słowach & caller: \texttt{StackType\_t[N]} \\ heap delta przy create & \blank{3cm} & \blank{3cm} \\ adres TCB & \blank{3cm} & \blank{3cm} \\ adres stack base & \blank{3cm} & \blank{3cm} \\ właściciel po delete & idle odzyskuje dynamiczny & caller nadal posiada statyczny \\ \bottomrule \end{tabularx} \section*{Oblicz przed kompilacją} \begin{align*} \text{stack bytes} &= \text{stack words}\times\texttt{sizeof(StackType\_t)}\\ &=256\times\blank{1cm}=\blank{2cm}\ \text{B} \end{align*} Dynamiczny koszt będzie większy od samych stack bytes, bo obejmuje także TCB oraz narzut bloków allocatora. Predykcja kosztu: \blank{3cm} B. Pomiar: \blank{3cm} B. Różnica: \blank{3cm} B. \section{Caller-supplied storage musi naprawdę być osobne} \begin{lstlisting} StaticTaskStorage<256> worker_storage; // .bss StaticWorker worker{worker_storage}; xTaskCreateStatic(entry, "static", 256, &worker, priority, worker_storage.stack, &worker_storage.tcb); \end{lstlisting} W ELF symbol \texttt{g\_static\_worker\_storage} ma typ sekcji \blank{2cm}. Odczyty muszą spełnić: \begin{center} \texttt{observed TCB == \&worker\_storage.tcb}\\ \texttt{observed stack base == worker\_storage.stack} \end{center} \section*{Dlaczego storage jest oddzielone od handle?} Handle może zostać wyzerowany po completion. Bufor statyczny nadal istnieje i ma własny czas życia. Nie wolno go jednak ponownie użyć, dopóki usunięcie taska nie jest ustalone; sam „request stop” nie wystarcza. \newpage \section{Oś czasu delete i idle cleanup} \begin{center} \texttt{baseline}\\ $\downarrow$ \texttt{xTaskCreate(dynamic): free -= cost}\\ $\downarrow$ \texttt{xTaskCreateStatic(static): delta 0}\\ $\downarrow$ \texttt{workers run -> completed -> handle=null -> self-delete}\\ $\downarrow$ \texttt{verifier runs before idle: dynamic cost still absent}\\ $\downarrow$ \texttt{verifier vTaskDelay(2): idle cleans termination list}\\ $\downarrow$ \texttt{verifier wakes: free == baseline} \end{center} \noindent\begin{tabularx}{\textwidth}{@{}p{5.7cm}p{4cm}X@{}} \toprule Pomiar & Predykcja & Odczyt \\ \midrule baseline free & \blank{3cm} & \blank{3cm} \\ free po dynamic start & mniejsze & \blank{3cm} \\ free po static start & bez zmiany & \blank{3cm} \\ free po static verifier create & bez zmiany & \blank{3cm} \\ free przed idle cleanup & nadal zmniejszone & \blank{3cm} \\ free po idle cleanup & baseline & \blank{3cm} \\ recovered by idle & dynamic cost & \blank{3cm} \\ \bottomrule \end{tabularx} \section*{Co robi idle?} Self-delete nie może zwolnić własnego stosu, gdy kod nadal na nim wykonuje instrukcje. Kernel odkłada dynamiczny TCB na listę terminacji. Idle działa już na innym stosie i może bezpiecznie wywołać cleanup/free. Dla statycznego taska rozpoznaje caller-owned storage i go nie zwalnia. \section*{Dwa różne zdania o zakończeniu} \begin{enumerate} \item \textbf{Logiczne}: task nie jest schedulowany, wrapper ma completed/null. \item \textbf{Pamięciowe}: idle odzyskał dynamiczny TCB/stack; current free wróciło o dokładny koszt. \end{enumerate} Czy verifier o priorytecie 1 może sprawdzić reclaim bez blokowania się? \blank{3cm}. Dlaczego? \blank{11cm}\\[1em]\blank{16cm} \section*{Granica destruktorów aplikacyjnych} Wymuszone usunięcie innego taska nie odwija automatycznie jego ramek C++. Workery K08 kończą member body normalnie, a dopiero trampoline self-delete. \newpage \section{Hazard3/GDB i zaliczenie} \begin{lstlisting}[language=bash] make check riscv64-unknown-elf-gdb build/task01_static_task/prog.elf b task_storage_debug_checkpoint \end{lstlisting} \begin{lstlisting} p g_heap_baseline p g_heap_after_dynamic_start p g_heap_after_static_start p g_heap_before_idle_cleanup p g_heap_after_idle_cleanup p/x g_dynamic_tcb p/x g_dynamic_stack_low p/x g_static_tcb p/x g_static_tcb_storage p/x g_static_stack_low p/x g_static_stack_storage p g_static_stack_words p g_static_stack_bytes p g_task_storage_pass \end{lstlisting} \noindent\begin{tabularx}{\textwidth}{@{}p{1cm}p{4.1cm}X@{}} \toprule STOP & Stan & Dowód \\ \midrule 1 & baseline & current free zapisane \\ 2 & dynamic created & heap spada; handle/TCB i stack w ucHeap \\ 3--4 & static created & caller addresses; heap bez zmiany \\ 5--8 & workers & oba wyniki 500500; completion/self-delete \\ 9 & before idle & handles null, lecz dynamic cost nadal zajęty \\ 10 & after idle & odzyskany dokładny cost; free=baseline \\ 11 & final & static storage zachowane; pass=1 \\ \bottomrule \end{tabularx} \section*{Zaliczenie} \begin{itemize} \item $\square$ przeliczam 256 słów na 1024 B; \item $\square$ pokazuję dodatni dynamic cost i zerowy static delta; \item $\square$ klasyfikuję dynamic addresses w heapie i static w .bss; \item $\square$ rozróżniam completion/null od idle memory reclaim; \item $\square$ pokazuję before-idle, after-idle i exact recovered cost; \item $\square$ nie przypisuję kernelowi własności static storage. \end{itemize} \section*{Wyjście} Dlaczego self-delete nie może od razu zwolnić własnego stacka?\\[.5em] \blank{16cm}\\[1em] Kiedy wolno ponownie użyć statycznego TCB/stacka?\\[.5em] \blank{16cm} \vfill \noindent\textbf{Następna karta K09:} statyczna fasada schedulera, \texttt{this\_task}, critical section i guard zawieszenia z poprawnym restore. \end{document}