-
Notifications
You must be signed in to change notification settings - Fork 0
/
part1.txt
163 lines (126 loc) · 7.65 KB
/
part1.txt
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
Part 1:
Enter your answer in the designated location. Do NOT remove lines that start
with '=' signs and keep lines to a max of 80 chars long.
================================== Q1 start ===================================
1. What actions does a kernel perform to context switch between processes?
During boot time, the kernel initially sets up the trap table for syscalls
and preemptive timer interrupts. Then when a user process performs a syscall
or the hardware sends an interrupt, it saves the user-mode process's registers
to the kernel stack, enters kernel mode, and runs the kernel's interrupt
handler. If the kernel's scheduler then decides to perform a context switch
to another thread of execution, it has to save the user process's registers
(that are on the kernel stack) to it's in-memory data structure for that
process (so that it can be re-loaded next time), restore the saved registers
for the new process it wants to switch to back to the kernel stack, execute
the return-from-trap instructure, and then let the hardware switch back to
user mode and restore the user's registers from the kernel stack,
which will then have the CPU resume execution from the same program counter,
stack pointer, and other registers that this process was previously in.
=================================== Q1 end ====================================
================================== Q2 start ===================================
2. What process state does a process enter immediately after it terminates?
What is this state for, and when does it leave this state?
When a process terminates, it initially enters a post-termination or "zombie"
state, where it is no longer running but some of its important data structures
are still maintained in the kernel's process list. Thus, this allows the
parent process to retrieve things about the now-terminated child process,
like it's pid and exit status code. Once the parent process wait()s on its
child, it will be repead and destroyed for good (no more zombie state). If,
however, the parent is already killed as well, then its reaper is not its
parent but the init process or another registered subrepear process,
and that repear (usually init) is the one that performs the wait to reap
the zombie process.
=================================== Q2 end ====================================
================================== Q3 start ===================================
3. What is typeof? Is it a C language keyword, function, or macro? What does
it do?
`typeof` is a GNU extension to the C language.
It is a keyword similar to C++'s standardized `decltype`,
but in C and a non-standard GNU extension.
If the argument of typeof is a type, then it just returns that type.
Normally, though, the argument is an expression, in which case
`typeof` returns the type of that expression.
Usually the expression is not actually evaluated while getting its type,
but if the expression is of a variably modified type
like a variable length array, then it will also be evaluated.
=================================== Q3 end ====================================
================================== Q4 start ===================================
4. What is offsetof? Is it a keyword, function, or macro? What does it do?
`offsetof` is a macro defined in `<stddef.h>` as part of the C specification.
Given a type and the name of a member field of that field,
it returns (as a `size_t`) the offset of the member field
from the start of the given type.
It is normally implemented using a compiler builtin,
like `__builtin_offsetof` in gcc,
(because in C++ it must be due to operator overloading).
In C, like in the linux kernel and when the builtin is not available,
it can also be implemented as
```C
#define offsetof(type, member) ((size_t) &((type *) 0)->member)
```
which basically takes a null pointer to the type,
takes a pointer to the member field,
and then converts it to a `size_t`,
which since the original pointer was a null pointer, 0,
is the distance between the start of the type
and the start of the member field.
=================================== Q4 end ====================================
================================== Q5 start ===================================
5. What does the container_of() macro do?
The `container_of` macro allows you to retrieve a pointer to the containing
struct (container) of a given member field, given a pointer to the member field,
the name of the member field, and the containing type. It does this by
calculating the offset of the member field from the start of the container
(using `offsetof`) and then subtracting that offset (in bytes) from the pointer,
and then casting that pointer to a pointer to the container type.
This is super useful for implementing intrusive linked lists,
where the linked list head (or multiple) is embedded in the container,
because it allows you to retrieve the container entry for a list element
from just the list head, which itself doesn't contain the container.
It is unsafe, though, (like an unchecked cast) because you have to statically
know that the correct container type is behind the list head pointer.
=================================== Q5 end ====================================
================================== Q6 start ===================================
6. The container_of() macro, in the way it’s currently written, is not
portable across C compilers because it uses GNU-specific language
extensions. Can you identify what they are?
There seems to be a couple of versions of `container_of` in the linux kernel.
They do the same things, but are implemented slightly differently.
For example, there is this one in `scripts/kconfig/list.h:19-21`
and `tools/include/linux/kernel.h:34-36`:
```c
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
```
And this one in `include/linux/kernel.h:851-856`:
```c
#define container_of(ptr, type, member) ({ \
void *__mptr = (void *)(ptr); \
BUILD_BUG_ON_MSG(!__same_type(*(ptr), ((type *)0)->member) && \
!__same_type(*(ptr), void), \
"pointer type mismatch in container_of()"); \
((type *)(__mptr - offsetof(type, member))); })
```
The latter one is the one used when I compile my module,
so I will refer to that one in this answer and other answers
that refer to `container_of`. The former version, though, uses `typeof`,
which is a GNU extension (described in Q3).
In the latter version, the GNU extension used is statement expressions.
By surrounding a block in parentheses, it turns into an expression evaluating
to the last statement in the block. This is similar to features in
other languages like Rust, where the last statement (without a semicolon)
are the value of the block. This is very useful for running code in macros
safely within a block while still evaluating to an expression,
which lets you execute multiple statements or run a loop or similar things.
=================================== Q6 end ====================================
================================== Q7 start ===================================
7. The container_of() macro has two lines. Is the 1st line (the declaration of
__mptr) really necessary? Is it possible to rewrite the macro without it?
What is the purpose of that 1st line?
Both versions of `container_of` contain the `__mptr` declaration.
The former is the one with two lines, though that one is not the main one
in `include/linux/kernel.h`. The main one is also two lines if you ignore
the type-checking second line containing the `BUILD_BUG_ON_MSG` macro.
They both use `__mptr` similarly, though.
=================================== Q7 end ====================================