-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathprocess.h
More file actions
461 lines (391 loc) · 14.7 KB
/
process.h
File metadata and controls
461 lines (391 loc) · 14.7 KB
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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
#pragma once
/**
* @brief Process management
* @file kernel/process.h
*
* This file defines the data structures and functions for managing processes
* and threads in a multithreading model.
*
* A process is an instance of a program that is being executed by one or more
* threads. Each process has its own unique address space, which means that it
* has its own virtual memory space that is separate from other processes. This
* allows each process to have its own private memory space, which is not
* accessible to other processes. A process also has its own set of system
* resources, such as open files descriptors.
*
* A thread is an execution context that is created within a process.
* Threads within the same process share the same address space. They do not
* share the same execution context (registers, stack).
*
* Both processes and threads are identified using a unique id: PID and TID.
* Threads do not exist without a containing process, and processes are forced
* to always contain at least one alive thread.
*
* The file provides functions for creating and killing processes, creating and
* killing threads, as well as switching between them.
*
* @defgroup process Processes
* @ingroup scheduling
*
* @{
*/
#include <kernel/file.h>
#include <kernel/types.h>
#include <kernel/user.h>
#include <kernel/vmm.h>
#include <kernel/signal.h>
#include <libalgo/linked_list.h>
#include <utils/compiler.h>
#include <string.h>
#if ARCH == i686
#include <kernel/arch/i686/process.h>
#else
#error Unsupported arcihtecture
#endif
/** The max length of a process's name */
#define PROCESS_NAME_MAX_LEN 32U
/** Maximum number of files that one process can have open at any one time. */
#define PROCESS_FD_COUNT 32
struct address_space;
/** A function used as an entry point when creating a new thread
*
* @param data Generic data passed on to the function when starting
* the thread
* @note We never return from this function
*/
typedef void (*thread_entry_t)(void *data);
/** The different states a thread can be in
* @enum thread_state
*/
typedef enum thread_state {
SCHED_RUNNING, ///< Currently running (or ready to run)
SCHED_WAITING, ///< Currently waiting for a resource (timer, lock ...)
SCHED_ZOMBIE, ///< Thread waiting to be collected by its parent process.
SCHED_KILLED, ///< The thread has been killed waiting to be destroyed
} thread_state_t;
/**
* @brief A single process
*
* The struct contains information about the process, including its name,
* unique ID and resources context (address space, file descriptors, ...).
* It also contains a list of all the process's active threads.
*
* @struct process
*/
struct process {
char name[PROCESS_NAME_MAX_LEN]; /*!< The thread's name */
pid_t pid; /*!< Process' unique ID */
unsigned int flags;
struct address_space *as; /*!< The process's address space */
struct process *parent;
llist_t threads; /*!< Linked list of the process' active threads */
llist_t children; /*!< Linked list of the process' active children */
node_t this; /*!< Node inside the parent's list of children */
node_t this_global; /*!< Used by the global list of alive processes */
size_t refcount; /*!< Reference count to this process.
We only kill a process once all of its threads have
been released. */
/** Open file descriptors table.
*
* This table is lock protected by @ref files_lock, one must **always**
* take this lock when accessing a process' open files (read AND write).
*/
struct file *files[PROCESS_FD_COUNT];
spinlock_t files_lock; /*!< Lock for @ref open_files */
/*
* Signal handling.
*/
struct signal_set *sig_set; /* Registered signal handlers. */
struct signal_queue sig_pending;
sig_sa_sigaction_t sig_handler; /* stub handler set by sigsethandler(). */
thread_state_t state;
uint16_t exit_status; /** Transmitted to the parent process during wait() */
struct user_creds *creds; /** Process credentials. */
spinlock_t lock;
};
/*
* Possible values for (struct process)->flags.
*/
enum process_flags {
PROC_SA_NOCLDWAIT = BIT(0), /* Do not generate SIGCHLD when children stop */
};
/* Union of all the flags that should be inherited when forking. */
#define PROC_FLAGS_INHERITED (PROC_SA_NOCLDWAIT)
/**
* @brief A single thread.
*
* The struct contains information about the thread's execution state.
* This state is architecture dependant, so its actual definition is present
* inside the arch specific process header file.
*
* Generic information about a thread include its current state (used by the
* scheduler), information about its containing process and feature flags.
*
* @struct thread
*/
typedef struct thread {
/**
* Arch specific thread context
*
* This includes registers, and information that must be kept for when
* switching back into the thread.
*/
thread_context_t context;
thread_state_t state; /*!< Thread's current state, used by the scheduler */
pid_t tid; /*!< Thread ID */
u32 flags; /*!< Combination of \ref thread_flags values */
struct process *process; /*!< Containing process */
node_t this_proc; /*!< Used by a process to list threads */
node_t this_sched; /*!< Used by the scheduler */
node_t this_global; /*!< Used by the global list of alive processes */
/** Information relative to the current state of the thread */
union {
/** For sleeping threads only */
struct {
clock_t wakeup; /*!< Time when it should wakeup (in ticks) */
} sleep;
};
/*
* Signal handling.
*/
struct signal_queue sig_pending;
sigset_t sig_blocked; /* mask of currently blocked signals. */
} thread_t;
/*
* List of all processes and threads currently alive.
*
* These list only contain live threads/processes so that lookups
* do not return objects that have already been killed.
*/
extern llist_t processes_list;
extern spinlock_t processes_list_lock;
extern llist_t threads_list;
extern spinlock_t threads_list_lock;
/** @enum thread_flags */
typedef enum thread_flags {
THREAD_KERNEL = BIT(0), ///< This is a kernel thread
THREAD_RESCHED = BIT(1), ///< Reschedule when exiting interrupt
} process_flags_t;
/***/
static ALWAYS_INLINE bool thread_is_kernel(thread_t *thread)
{
return thread->flags & THREAD_KERNEL;
}
/** The initial thread is the thread created along with the process.
* The TID of the initial thread is the same as its process's PID.
*/
static ALWAYS_INLINE bool thread_is_initial(thread_t *thread)
{
return thread->tid == thread->process->pid;
}
/** Set the thread's current stack pointer */
static inline void thread_set_stack_pointer(struct thread *thread, void *stack)
{
arch_thread_set_stack_pointer(&thread->context, stack);
}
/** Get the thread's current stack pointer */
static inline void *thread_get_stack_pointer(struct thread *thread)
{
return arch_thread_get_stack_pointer(&thread->context);
}
/** Set the thread's current base pointer */
static inline void thread_set_base_pointer(struct thread *thread, void *ptr)
{
arch_thread_set_base_pointer(&thread->context, ptr);
}
/** Get the thread's current base pointer */
static inline void *thread_get_base_pointer(struct thread *thread)
{
return arch_thread_get_base_pointer(&thread->context);
}
/** Set a thread's curent interrupt frame. */
static inline void
thread_set_interrupt_frame(thread_t *thread,
const struct interrupt_frame *frame)
{
arch_thread_set_interrupt_frame(&thread->context, frame);
}
/** Get a thread's current interrupt frame. */
static inline struct interrupt_frame *
thread_get_interrupt_frame(struct thread *thread)
{
return arch_thread_get_interrupt_frame(&thread->context);
}
/** Set the thread's kernel stack bottom address */
static inline void thread_set_kernel_stack(struct thread *thread, void *stack)
{
arch_thread_set_kernel_stack_top(&thread->context,
stack + KERNEL_STACK_SIZE);
}
/** Get the thread's kernel stack top address */
static inline void *thread_get_kernel_stack_top(const struct thread *thread)
{
return arch_thread_get_kernel_stack_top(&thread->context);
}
/** Get the thread's kernel stack bottom address */
static inline void *thread_get_kernel_stack(const struct thread *thread)
{
void *top = thread_get_kernel_stack_top(thread);
if (!top)
return NULL;
return top - KERNEL_STACK_SIZE;
}
/** Set the thread's user stack bottom address */
static inline void thread_set_user_stack(struct thread *thread, void *stack)
{
arch_thread_set_user_stack_top(&thread->context, stack + USER_STACK_SIZE);
}
/** Get the thread's user stack top address */
static inline void *thread_get_user_stack_top(const struct thread *thread)
{
return arch_thread_get_user_stack_top(&thread->context);
}
/** Get the thread's user stack bottom address */
static inline void *thread_get_user_stack(const struct thread *thread)
{
void *top = thread_get_user_stack_top(thread);
if (!top)
return NULL;
return top - USER_STACK_SIZE;
}
/**
* Read the thread's return address when exiting the current interrupt context.
*/
static inline void *
thread_get_interrupt_return_address(const struct thread *thread)
{
return arch_thread_get_interrupt_return_address(&thread->context);
}
/** Process used when starting up the kernel.
*
* It is necesary to define it statically, since memory management functions are
* not yet set up when first starting up.
*
* We should not reference this process anymore once we have an up and running
* scheduler.
*/
extern struct process kernel_process;
extern struct thread kernel_process_initial_thread;
/** The init process is the very first executed userland process.
*
* It is the parent of all other userland processes, and all zombie processes
* are attached to it when their parent dies.
*
* The init process uses the reserved PID 1.
*/
extern struct process *init_process;
/** Initialize the kernel process's address space.
*
* The kernel process's instance being defined satically, it is required to
* be explicitely initialize during the startup phase so that it can be used
* just like any other process later on.
*/
void process_init_kernel_process(void);
/** Kill a process. */
void process_kill(struct process *process, uint16_t status);
/** Register an open file inside the process's open file descriptor table.
* @return The registered file's index inside the open file descriptor table.
*/
int process_register_file(struct process *, struct file *);
/** Remove an open file from the process's open file descriptor table. */
error_t process_unregister_file(struct process *, int fd);
/**
* @return A reference to the file description at index @ref fd
* inside @ref process 's file descriptor table.
*/
struct file *process_file_get(struct process *, int fd);
/** Release a file description retreived using @ref process_file_get(). */
static inline void process_file_put(struct process *process, struct file *file)
{
UNUSED(process);
file_put(file);
}
/** Run an executable.
*
* As the kernel should never run external executables, this function instead
* creates a new userland process that will in turn be used to execute the
* executable.
*
* @param exec_path Absolute path to the executable file
*/
struct thread *process_execute_in_userland(const char *exec_path);
/***/
static inline void process_set_name(struct process *process, const char *name,
size_t size)
{
strlcpy(process->name, name, MIN(size + 1, PROCESS_NAME_MAX_LEN));
}
/** Find an **alive** process by its PID. */
struct process *process_find_by_pid(pid_t pid);
/** The currently running thread */
extern thread_t *current;
/** Switch the currently running thread
* @param process The new thread to switch into
* @return \c false if the new thread was previously killed
*/
bool thread_switch(thread_t *);
/** Create and initialize a new thread
*
* When starting a userland thread, the \c data field is not passed
* to the entrypoint function.
*
* @param process The process the newly created thread belongs to
* @param entrypoint The function called when starting the thread
* @param data Data passed to the entry function (can be NULL)
* @param esp The value to place inside the stack pointer register before
* first kicking off the thread. This is mainly useful when forking
* an existing thread. Ignored if NULL.
* @param ebp The value placed inside the stack base pointer register before
* first kicking off the thread. Ignored if NULL.
* @param flags Feature flags: a combination of \ref thread_flags enum values
*/
thread_t *thread_spawn(struct process *, thread_entry_t, void *data,
void *esp, void *ebp, u32 flags);
/** Start executing code in userland
*
* This function resets the user execution context (eip, stack content).
* It then changes the privilege level to be that of userland, and jumps
* onto the given address.
*
* @info This serves as a temporary equivalent to the execve syscall for testing
* purposes.
*
* @param stack_pointer The stack pointer used when jumping to userland
* @param base_pointer The base pointer used when jumping to userland
* @param entrypoint The entrypoint address to jump onto
* @param data The data passed as an argument to the 'entrypoint' function
* (ignored)
*/
NO_RETURN void thread_jump_to_userland(void *stack_pointer, void *base_pointer,
thread_entry_t, void *);
/** Set the MMU address saved inside the thread's structure.
* @note This function does not change the MMU currently in use,
* see @ref mmu_load for this instead.
*/
void thread_set_mmu(struct thread *thread, paddr_t mmu);
/** Effectively kill a thread
*
* * Free all private memory used by the thread
* * Synchronize resources (write to files, ...)
*/
void thread_kill(thread_t *);
/** Create a new fork of the given thread.
*
* A new process is created to execute the forked thread.
* The address space of the original thread's process is duplicated
* inside the newly created process.
*
* If the duplicated process had multiple threads, only the one that
* called this function is replicated.
*
* @return The newly created thread
*/
struct thread *thread_fork(struct thread *, thread_entry_t, void *);
/** Try and deliver the first non-blocked pending signal.
*
* This function should be called before returning to userland.
*/
void thread_deliver_pending_signal(struct thread *thread);
/** Find an **alive** thread by its TID. */
struct thread *thread_find_by_tid(pid_t tid);
/** @} */