238 lines
8.7 KiB
TeX
238 lines
8.7 KiB
TeX
\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}{09}
|
|
\newcommand{\CardCount}{16}
|
|
\newcommand{\CardSlug}{kernel-facade}
|
|
\newcommand{\CardVersion}{v00.01}
|
|
\newcommand{\DocumentUUID}{6a2ab068-17e7-4bfa-b726-2df37787ed69}
|
|
\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{K09 · FreeRTOS C++ · kernel facade}}\rhead{\small L08 · paired kernel state}
|
|
\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 Scheduler facade i scoped guards}\par
|
|
\vspace{.25em}{\large statyczna usługa; RAII tylko dla prawdziwych par}\par
|
|
\end{center}
|
|
\noindent\begin{tabularx}{\textwidth}{@{}p{1.65cm}Xp{1.55cm}X@{}}
|
|
\toprule
|
|
Karta & K09 / \CardCount & Czas & 30 minut \\
|
|
Platforma & Hazard3 / RV32I & Język & freestanding C++17 \\
|
|
Dowód & nesting + \texttt{mstatus.MIE} & API & task context \\
|
|
Wersja & \CardVersion & UUID karty & \texttt{6a2ab068-...} \\
|
|
\bottomrule
|
|
\end{tabularx}
|
|
|
|
\section*{Najpierw klasyfikacja lifetime}
|
|
\begin{center}
|
|
\texttt{Scheduler::start/state} $\rightarrow$ usługa procesu, brak scoped lifetime\\[.35em]
|
|
\texttt{enter/exit}, \texttt{suspend/resume} $\rightarrow$ pary, więc guard RAII
|
|
\end{center}
|
|
|
|
\noindent\fcolorbox{accent}{accentlight}{\begin{minipage}{.94\textwidth}
|
|
\textbf{Kontrakt K09.} Destruktor guarda ma odtworzyć stan również przy każdym
|
|
\texttt{return}. Nie wolno jednak blokować przy zawieszonym schedulerze, a
|
|
\texttt{CriticalSection} nie jest mutexem ani API dla ISR.
|
|
\end{minipage}}
|
|
|
|
\section*{Plan 30 minut}
|
|
\noindent\begin{tabularx}{\textwidth}{@{}p{1.35cm}p{3.15cm}X@{}}
|
|
\toprule
|
|
Czas & Tryb & Dowód \\
|
|
\midrule
|
|
0--5 & raw API & rozdziel usługi od par \\
|
|
5--10 & critical & nesting oraz MIE przed/wewnątrz/po \\
|
|
10--15 & guard & trzy wczesne wyjścia wracają do stanu bazowego \\
|
|
15--20 & suspend & scheduler suspended, lecz MIE pozostaje 1 \\
|
|
20--25 & nesting & inner exit nie wznawia jeszcze outer suspension \\
|
|
25--30 & ABI & inline do raw API; brak vtable/runtime \\
|
|
\bottomrule
|
|
\end{tabularx}
|
|
|
|
\section*{Dwie różne blokady}
|
|
\noindent\begin{tabularx}{\textwidth}{@{}p{4.1cm}p{3.3cm}p{3.2cm}X@{}}
|
|
\toprule
|
|
Operacja & Context switch & Przerwania & Para \\
|
|
\midrule
|
|
\texttt{vTaskSuspendAll} & odroczony & działają & \texttt{xTaskResumeAll} \\
|
|
\texttt{taskENTER\_CRITICAL} & niemożliwy & MIE=0 & \texttt{taskEXIT\_CRITICAL} \\
|
|
\bottomrule
|
|
\end{tabularx}
|
|
|
|
Predykcja MIE podczas suspension: \blank{2cm}. Predykcja critical depth po
|
|
dwóch enter i jednym exit: \blank{2cm}.
|
|
|
|
\newpage
|
|
\section{Raw critical: zmierz dokładną sekwencję}
|
|
|
|
\begin{lstlisting}
|
|
taskENTER_CRITICAL(); // depth + 1, MIE=0
|
|
taskENTER_CRITICAL(); // depth + 1, MIE=0
|
|
taskEXIT_CRITICAL(); // depth - 1, nadal MIE=0
|
|
taskEXIT_CRITICAL(); // depth == 0, MIE=1
|
|
\end{lstlisting}
|
|
|
|
\noindent\begin{tabularx}{\textwidth}{@{}p{2.0cm}p{3.0cm}p{2.5cm}X@{}}
|
|
\toprule
|
|
Checkpoint & Operacja & Predykcja depth/MIE & Odczyt depth/MIE \\
|
|
\midrule
|
|
C0 & przed & 0 / 1 & \blank{2.5cm} \\
|
|
C1 & enter & 1 / 0 & \blank{2.5cm} \\
|
|
C2 & drugi enter & 2 / 0 & \blank{2.5cm} \\
|
|
C3 & pierwszy exit & 1 / 0 & \blank{2.5cm} \\
|
|
C4 & drugi exit & 0 / 1 & \blank{2.5cm} \\
|
|
\bottomrule
|
|
\end{tabularx}
|
|
|
|
\section{RAII zamienia trzy krawędzie w jedną parę}
|
|
\begin{lstlisting}
|
|
uint32_t repair(uint32_t path) {
|
|
freertos::CriticalSection guard;
|
|
if (path == 0) return 0x10;
|
|
if (path == 1) return 0x20;
|
|
return 0x30;
|
|
} // destructor runs on every return
|
|
\end{lstlisting}
|
|
|
|
\noindent\begin{tabularx}{\textwidth}{@{}p{1.5cm}p{2.6cm}p{2.7cm}p{2.5cm}X@{}}
|
|
\toprule
|
|
Path & Wynik & inside depth/MIE & after depth/MIE & PASS \\
|
|
\midrule
|
|
0 & \texttt{0x10} & \blank{2cm} & \blank{2cm} & $\square$ \\
|
|
1 & \texttt{0x20} & \blank{2cm} & \blank{2cm} & $\square$ \\
|
|
2 & \texttt{0x30} & \blank{2cm} & \blank{2cm} & $\square$ \\
|
|
\bottomrule
|
|
\end{tabularx}
|
|
|
|
\section*{Co dokładnie gwarantuje guard?}
|
|
\begin{itemize}
|
|
\item paruje wywołania w normalnym przepływie i przy wcześniejszym return;
|
|
\item nie jest kopiowalny ani przenoszalny, więc nie powiela exit;
|
|
\item zachowuje zagnieżdżenie: inner exit nie kończy outer critical section.
|
|
\end{itemize}
|
|
|
|
Czy RAII pozwala wywołać blokujące API wewnątrz critical section?
|
|
\blank{2cm}. Uzasadnienie:\\[.5em]\blank{16cm}
|
|
|
|
\newpage
|
|
\section{SchedulerSuspendGuard: context switch bez maskowania IRQ}
|
|
|
|
\begin{lstlisting}
|
|
{
|
|
freertos::SchedulerSuspendGuard guard;
|
|
// scheduler state == suspended; mstatus.MIE == 1
|
|
// no delay, queue wait, semaphore wait or other blocking API
|
|
} // xTaskResumeAll exactly once
|
|
\end{lstlisting}
|
|
|
|
\noindent\begin{tabularx}{\textwidth}{@{}p{2cm}p{3.3cm}p{3.1cm}X@{}}
|
|
\toprule
|
|
Path & Inside state/MIE & After state/MIE & Wynik \\
|
|
\midrule
|
|
0 & suspended / \blank{1cm} & running / \blank{1cm} & \texttt{0x40} \\
|
|
1 & suspended / \blank{1cm} & running / \blank{1cm} & \texttt{0x50} \\
|
|
2 & suspended / \blank{1cm} & running / \blank{1cm} & \texttt{0x60} \\
|
|
\bottomrule
|
|
\end{tabularx}
|
|
|
|
\section*{Zagnieżdżenie}
|
|
\begin{center}
|
|
\texttt{running}\\
|
|
$\downarrow$ outer ctor: \texttt{suspended}\\
|
|
$\downarrow$ inner ctor: \texttt{suspended}\\
|
|
$\downarrow$ inner dtor: \texttt{suspended}\\
|
|
$\downarrow$ outer dtor: \texttt{running}
|
|
\end{center}
|
|
|
|
Dlaczego inner destructor nie może jeszcze zwrócić stanu running?\\[.5em]
|
|
\blank{16cm}
|
|
|
|
\section{Statyczna fasada i bieżący task}
|
|
\begin{lstlisting}
|
|
freertos::Scheduler::start();
|
|
auto state = freertos::Scheduler::state();
|
|
auto handle = freertos::this_task::current();
|
|
auto tick = freertos::this_task::tick_count();
|
|
freertos::this_task::yield();
|
|
\end{lstlisting}
|
|
|
|
\noindent\begin{tabularx}{\textwidth}{@{}p{5cm}p{4cm}X@{}}
|
|
\toprule
|
|
Relacja & Predykcja & Odczyt \\
|
|
\midrule
|
|
state przed start & not started & \blank{2.5cm} \\
|
|
state w workerze & running & \blank{2.5cm} \\
|
|
this task handle & worker handle & \blank{2.5cm} \\
|
|
tick after yield & $\geq$ tick before & \blank{2.5cm} \\
|
|
\bottomrule
|
|
\end{tabularx}
|
|
|
|
\section*{Granice}
|
|
Suspension nie wyłącza ISR; tylko odracza przełączenie taska. Critical section
|
|
maskuje przerwania i musi być krótka. Żaden z guardów nie jest zamiennikiem
|
|
mutexa chroniącego zasób przez długą operację.
|
|
|
|
\newpage
|
|
\section{Hazard3/GDB i ABI}
|
|
\begin{lstlisting}[language=bash]
|
|
make check
|
|
riscv64-unknown-elf-gdb build/task01_kernel_facade/prog.elf
|
|
b kernel_facade_debug_checkpoint
|
|
\end{lstlisting}
|
|
\begin{lstlisting}
|
|
p g_raw_critical_depth
|
|
p g_raw_critical_mie
|
|
p g_guard_critical_after_depth
|
|
p g_guard_critical_after_mie
|
|
p g_raw_scheduler_state
|
|
p g_guard_scheduler_after_state
|
|
p g_nested_scheduler_state
|
|
p g_this_task_handle
|
|
p g_worker_handle
|
|
p g_kernel_facade_pass
|
|
\end{lstlisting}
|
|
|
|
\section*{Co ma zostać w assembly?}
|
|
\noindent\begin{tabularx}{\textwidth}{@{}p{5.3cm}p{3.3cm}X@{}}
|
|
\toprule
|
|
Dowód & Oczekiwane & Odczyt \\
|
|
\midrule
|
|
disable IRQ & \texttt{csrci mstatus,8} & \blank{2.5cm} \\
|
|
enable IRQ & \texttt{csrsi mstatus,8} & \blank{2.5cm} \\
|
|
symbole wrappera & brak & \blank{2.5cm} \\
|
|
vtable/RTTI/exception runtime & brak & \blank{2.5cm} \\
|
|
\bottomrule
|
|
\end{tabularx}
|
|
|
|
\section*{Zaliczenie}
|
|
\begin{itemize}
|
|
\item $\square$ rozdzielam statyczną fasadę od scoped ownera;
|
|
\item $\square$ odtwarzam critical depth 0--1--2--1--0 i MIE 1--0--0--0--1;
|
|
\item $\square$ pokazuję restore po trzech wczesnych return;
|
|
\item $\square$ pokazuję, że suspension zachowuje MIE=1;
|
|
\item $\square$ wyjaśniam nesting outer/inner i zakaz blokowania;
|
|
\item $\square$ dowodzę inline facade/guards oraz końcowy PASS.
|
|
\end{itemize}
|
|
|
|
\section*{Wyjście}
|
|
Kiedy wybrać \texttt{CriticalSection}, a kiedy \texttt{SchedulerSuspendGuard}?\\[.5em]
|
|
\blank{16cm}\\[1em]
|
|
Dlaczego \texttt{Scheduler} nie jest obiektem RAII?\\[.5em]
|
|
\blank{16cm}
|
|
|
|
\vfill
|
|
\noindent\textbf{Następna karta K10:} typed \texttt{Queue<T>} i
|
|
\texttt{StaticQueue<T,N>} z jawnym kontraktem copyable message type.
|
|
\end{document}
|