1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
|
\documentclass{article}
\usepackage{graphicx}
\usepackage{listings}
\lstset { %
language=C++
}
\title{WG01: C++ for Kernel Development}
\author{Amlal El Mahrouss\\NeKernel.org}
\date{\today}
\begin{document}
\maketitle
\section{Abstract}
{
Many and most kernels have been shipped using the C programming language.\\
And some of them like EKA2 uses the C++ programming language. Although notoriously difficult, one may still adapt to those constraints in order to deliver one such operating system kernel. \\
That is the reason that most production-grade kernels (Linux, XNU, and NT) are mostly written in C. With a higher-level subset in C++. \\
However, when correctly applying C++ principles to kernel development, one makes the development much more easier to pull off.
}
\section{The Three Principles of Kernel C++}
\subsection{Part One: The C++ Runtime}
{
The problem mostly lies in the C++ runtime. Which assumes an existing host. A host is the contrary of a freestanding target, that is a program which expects a runtime to be present and linked to the program. \\
A C++ Kernel may instead make use of compile-time features of C++ alongside a tiny C++ runtime to make sure that no issues arise because of this host/freestanding difference.
}
\subsection{Part Two: Constexpr and Friends}
{
One may avoid v-tables and runtime dependent features as much as possible. \\
While focusing instead on meta-programming and compile-time features offered by C++. For example one may use templates to implement a scheduling policy algorithm. \\
One example of such implementation may be: \\
(Keep in mind that the code has not been tested on production systems, use it at your own risk!)
\begin{lstlisting}
/// Reference Implementation: https://github.com
/// /nekernel-org/nekernel/blob/stable/src/kernel/
/// KernelKit/CoreProcessScheduler.h#L51-L76
/// AND: https://github.com/nekernel-org/nekernel/blob/stable/src/kernel
/// /KernelKit/CoreProcessScheduler.h#L78-L105
struct FileTree final {
static constexpr bool is_virtual_memory = false;
static constexpr bool is_memory = false;
static constexpr bool is_file = true;
/// ...
};
struct MemoryTree final {
static constexpr bool is_virtual_memory = false;
static constexpr bool is_memory = true;
static constexpr bool is_file = false;
/// ...
};
\end{lstlisting}
As you can see these two structures
leverages the `constexpr' keyword to make sure
no bugs or panic occur at runtime because of a misuse of a system resource. \\
Which is why the constexpr keyword is very powerful here, we avoid the many pitfalls of writing (and hoping) that the C version will be well-thought enough so that we can catch such bugs later.
\subsection{Bonus: Using Concepts and the DDK.}
{
This bonus subsection intends to show how properly applied C++\\
Can solve issues related to driver validation.
The following example shows how powerful C++ can be when combining it with Device Driver development as well.
\begin{lstlisting}
/// Reference implementation:
/// https://github.com/nekernel-org
/// /nekernel/blob/stable/src/libDDK/DriverKit
/// /c%2B%2B/driver_base.h
/// @brief This concept requires the driver
/// to be IDriverBase compliant.
template <typename T>
concept IsValidDriver = requires(T a) {
{ a.IsActive() && a.Type() > 0 };
};
/// @brief Consteval helper to detect
/// whether a template is truly based on IDriverBase.
/// @note This helper is consteval only.
template<IsValidDriver T>
consteval void ce_ddk_is_valid(T) {}
\end{lstlisting}
}
}
\subsection{Part Three: Memory and C++ Classes}
{
This last part treats about the final and most important part of this paper so far. Memory. As you may already have known, the C++ language uses a class lookup system (also called v-table) in order to refer to a base method in case if the instance has it missing.
\begin{lstlisting}
/// Link: https://godbolt.org/z/q3Wceannr
/// /std:c++20 /Wall
#include <iostream>
class A
{
public:
explicit A() = default;
virtual ~A() = default;
virtual void doImpl()
{
std::cout << "doImpl\n";
}
};
class B : public A
{
public:
explicit B() = default;
~B() override = default;
};
int main() {
B* callImpl = new B();
callImpl->doImpl();
}
\end{lstlisting}
\subsection{Addressing V-Tables in a C++ Kernel.}
Now the problem with kernel development is that we want to avoid such feature as much as possible, and we'd do that by following the Prong on Inheritance:
\subsection{The Three Prongs on Inheritance:}
Consider the following questions:
\begin{itemize}
\item[A:] Is this a protocol/concept that can be extended to other similar protocols/concepts?
\item[B:] Can I do this without too much trade-off costs?
\item[C:] Can I do this without using V-Tables?
\end{itemize}
}
When 2/3 of those questions fail, you should consider finding another solution to your problem, as it surely has an equivalent without V-Tables.
\section{Final Words and Conclusion}
{
We can now conclude that C++ in Kernel Development is indeed possible under strict conditions of C++ Development. \\
Breaking those conditions would lead to system quirks and instability.
A reference implementation of this paper exists, It's called NeKernel.org, available under the same internet address. \\
I am looking forward to any questions or inquiries at: amlal@nekernel.org \\\\Yours truly, \\
Amlal El Mahrouss
}
\section{References}
{
\begin{itemize}
\item[EKA2:] https://en.wikipedia.org/wiki/EKA2
\item[NeKernel.org:] https://nekernel.org
\end{itemize}
}
\end{document}
|