PatchworkOS  19e446b
A non-POSIX operating system.
Loading...
Searching...
No Matches
process.c
Go to the documentation of this file.
1#include <kernel/cpu/cpu.h>
2#include <kernel/cpu/gdt.h>
3#include <kernel/fs/ctl.h>
4#include <kernel/fs/dentry.h>
5#include <kernel/fs/devfs.h>
6#include <kernel/fs/file.h>
8#include <kernel/fs/path.h>
9#include <kernel/fs/vfs.h>
10#include <kernel/io/io.h>
11#include <kernel/log/log.h>
12#include <kernel/log/panic.h>
13#include <kernel/mem/cache.h>
14#include <kernel/mem/vmm.h>
15#include <kernel/proc/group.h>
16#include <kernel/proc/process.h>
17#include <kernel/proc/reaper.h>
18#include <kernel/sched/clock.h>
19#include <kernel/sched/sched.h>
20#include <kernel/sched/thread.h>
21#include <kernel/sched/timer.h>
22#include <kernel/sched/wait.h>
23#include <kernel/sync/lock.h>
24#include <kernel/sync/rcu.h>
25
26#include <assert.h>
27#include <errno.h>
28#include <kernel/sync/seqlock.h>
29#include <kernel/utils/map.h>
30#include <kernel/utils/ref.h>
31#include <stdatomic.h>
32#include <stdint.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <sys/fs.h>
36#include <sys/list.h>
37#include <sys/math.h>
38#include <sys/proc.h>
39
41
42static _Atomic(pid_t) newPid = ATOMIC_VAR_INIT(0);
43
44static map_t pidMap = MAP_CREATE();
45
47static lock_t processesLock = LOCK_CREATE();
48
49static void process_ctor(void* ptr)
50{
51 process_t* process = (process_t*)ptr;
52
53 process->ref = (ref_t){0};
54 list_entry_init(&process->entry);
55 map_entry_init(&process->mapEntry);
57 process->id = 0;
58 atomic_init(&process->priority, 0);
60 lock_init(&process->status.lock);
61 process->space = (space_t){0};
62 process->nspace = NULL;
63 lock_init(&process->nspaceLock);
64 process->cwd = (cwd_t){0};
65 process->fileTable = (file_table_t){0};
66 process->futexCtx = (futex_ctx_t){0};
67 process->perf = (perf_process_ctx_t){0};
68 process->noteHandler = (note_handler_t){0};
69 process->suspendQueue = (wait_queue_t){0};
70 process->dyingQueue = (wait_queue_t){0};
71 atomic_init(&process->flags, PROCESS_NONE);
72 atomic_init(&process->threads.newTid, 0);
73 list_init(&process->threads.list);
74 process->threads.count = 0;
75 lock_init(&process->threads.lock);
76 env_init(&process->env);
77 process->argv = NULL;
78 process->argc = 0;
79 process->group = (group_member_t){0};
80 process->rcu = (rcu_entry_t){0};
81}
82
83static cache_t cache = CACHE_CREATE(cache, "process", sizeof(process_t), CACHE_LINE, process_ctor, NULL);
84
85static void process_free(process_t* process)
86{
87 LOG_DEBUG("freeing process pid=%d\n", process->id);
88
89 assert(list_is_empty(&process->threads.list));
90
91 if (process->argv != NULL)
92 {
93 for (uint64_t i = 0; i < process->argc; i++)
94 {
95 if (process->argv[i] != NULL)
96 {
97 free(process->argv[i]);
98 }
99 }
100 free((void*)process->argv);
101 process->argv = NULL;
102 process->argc = 0;
103 }
104
105 group_member_deinit(&process->group);
106 cwd_deinit(&process->cwd);
107 file_table_deinit(&process->fileTable);
108 if (process->nspace != NULL)
109 {
110 UNREF(process->nspace);
111 }
112 space_deinit(&process->space);
113 futex_ctx_deinit(&process->futexCtx);
114 for (uint64_t i = 0; i < ARRAY_SIZE(process->rings); i++)
115 {
116 ioring_ctx_deinit(&process->rings[i]);
117 }
118 wait_queue_deinit(&process->dyingQueue);
120 env_deinit(&process->env);
121
122 rcu_call(&process->rcu, rcu_call_cache_free, process);
123}
124
126{
127 if (ns == NULL)
128 {
129 errno = EINVAL;
130 return NULL;
131 }
132
133 process_t* process = cache_alloc(&cache);
134 if (process == NULL)
135 {
136 errno = ENOMEM;
137 return NULL;
138 }
139
140 ref_init(&process->ref, process_free);
141 process->id = atomic_fetch_add_explicit(&newPid, 1, memory_order_relaxed);
142 atomic_store(&process->priority, priority);
143 process->status.buffer[0] = '\0';
144
147 {
148 cache_free(process);
149 return NULL;
150 }
151
152 process->nspace = REF(ns);
153 cwd_init(&process->cwd);
154 file_table_init(&process->fileTable);
155 futex_ctx_init(&process->futexCtx);
156 perf_process_ctx_init(&process->perf);
157 for (uint64_t i = 0; i < ARRAY_SIZE(process->rings); i++)
158 {
159 ioring_ctx_init(&process->rings[i]);
160 }
162 wait_queue_init(&process->suspendQueue);
163 wait_queue_init(&process->dyingQueue);
164 atomic_store(&process->flags, PROCESS_NONE);
165 atomic_store(&process->threads.newTid, 0);
166 lock_init(&process->threads.lock);
167 env_init(&process->env);
168
169 if (group_member_init(&process->group, group) == ERR)
170 {
171 process_free(process);
172 return NULL;
173 }
174
175 lock_acquire(&processesLock);
176
177 map_key_t mapKey = map_key_uint64(process->id);
178 if (map_insert(&pidMap, &mapKey, &process->mapEntry) == ERR)
179 {
180 lock_release(&processesLock);
181 process_free(process);
182 return NULL;
183 }
184
185 LOG_DEBUG("created process pid=%d\n", process->id);
186
188 lock_release(&processesLock);
189 return REF(process);
190}
191
193{
195
196 map_key_t mapKey = map_key_uint64(id);
197 map_entry_t* entry = map_get(&pidMap, &mapKey);
198 if (entry == NULL)
199 {
200 return NULL;
201 }
202
203 return REF_TRY(CONTAINER_OF(entry, process_t, mapEntry));
204}
205
207{
208 if (process == NULL)
209 {
210 errno = EINVAL;
211 return NULL;
212 }
213
214 lock_acquire(&process->nspaceLock);
215 namespace_t* ns = process->nspace != NULL ? REF(process->nspace) : NULL;
216 lock_release(&process->nspaceLock);
217
218 if (ns == NULL)
219 {
220 errno = EINVAL;
221 return NULL;
222 }
223
224 return ns;
225}
226
228{
229 if (process == NULL || ns == NULL)
230 {
231 return;
232 }
233
234 lock_acquire(&process->nspaceLock);
235 UNREF(process->nspace);
236 process->nspace = REF(ns);
237 lock_release(&process->nspaceLock);
238}
239
240void process_kill(process_t* process, const char* status)
241{
242 if (atomic_fetch_or(&process->flags, PROCESS_DYING) & PROCESS_DYING)
243 {
244 return;
245 }
246
247 lock_acquire(&process->status.lock);
248 strncpy(process->status.buffer, status, PROCESS_STATUS_MAX - 1);
249 process->status.buffer[PROCESS_STATUS_MAX - 1] = '\0';
250 lock_release(&process->status.lock);
251
253
254 uint64_t killCount = 0;
255 thread_t* thread;
256 PROCESS_RCU_THREAD_FOR_EACH(thread, process)
257 {
258 thread_send_note(thread, "kill");
259 killCount++;
260 }
261
262 if (killCount > 0)
263 {
264 LOG_DEBUG("sent kill note to %llu threads in process pid=%d\n", killCount, process->id);
265 }
266
267 // Anything that another process could be waiting on must be cleaned up here.
268
269 cwd_clear(&process->cwd);
271
272 lock_acquire(&process->nspaceLock);
273 UNREF(process->nspace);
274 process->nspace = NULL;
275 lock_release(&process->nspaceLock);
276
277 group_remove(&process->group);
278
279 wait_unblock(&process->dyingQueue, WAIT_ALL, EOK);
280
281 reaper_push(process);
282}
283
285{
286 lock_acquire(&processesLock);
287 map_remove(&pidMap, &process->mapEntry);
288 list_remove_rcu(&process->entry);
289 lock_release(&processesLock);
290
291 UNREF(process);
292}
293
294uint64_t process_set_cmdline(process_t* process, char** argv, uint64_t argc)
295{
296 if (process == NULL)
297 {
298 errno = EINVAL;
299 return ERR;
300 }
301
302 if (argv == NULL || argc == 0)
303 {
304 process->argv = NULL;
305 process->argc = 0;
306 return 0;
307 }
308
309 char** newArgv = malloc(sizeof(char*) * (argc + 1));
310 if (newArgv == NULL)
311 {
312 errno = ENOMEM;
313 return ERR;
314 }
315
316 uint64_t i = 0;
317 for (; i < argc; i++)
318 {
319 if (argv[i] == NULL)
320 {
321 break;
322 }
323 size_t len = strlen(argv[i]) + 1;
324 newArgv[i] = malloc(len);
325 if (newArgv[i] == NULL)
326 {
327 for (uint64_t j = 0; j < i; j++)
328 {
329 free(newArgv[j]);
330 }
331 free(newArgv);
332 errno = ENOMEM;
333 return ERR;
334 }
335 memcpy(newArgv[i], argv[i], len);
336 }
337 newArgv[i] = NULL;
338
339 uint64_t newArgc = i;
340 if (process->argv != NULL)
341 {
342 for (uint64_t j = 0; j < process->argc; j++)
343 {
344 if (process->argv[j] != NULL)
345 {
346 free(process->argv[j]);
347 }
348 }
349 free(process->argv);
350 }
351
352 process->argv = newArgv;
353 process->argc = newArgc;
354
355 return 0;
356}
357
359{
361
362 thread_t* thread;
363 PROCESS_RCU_THREAD_FOR_EACH(thread, process)
364 {
365 if (thread->id == tid)
366 {
367 return true;
368 }
369 }
370
371 return false;
372}
373
375{
376 if (kernelProcess == NULL)
377 {
379 if (ns == NULL)
380 {
381 panic(NULL, "Failed to create kernel namespace");
382 }
383 UNREF_DEFER(ns);
384
386 if (kernelProcess == NULL)
387 {
388 panic(NULL, "Failed to create kernel process");
389 }
390 LOG_INFO("kernel process initialized with pid=%d\n", kernelProcess->id);
391 }
392
393 return kernelProcess;
394}
395
#define assert(expression)
Definition assert.h:29
#define SYSCALL_DEFINE(num, returnType,...)
Macro to define a syscall.
Definition syscall.h:172
@ SYS_GETPID
Definition syscall.h:70
void perf_process_ctx_init(perf_process_ctx_t *ctx)
Initializes a per-process performance context.
Definition perf.c:137
void cwd_init(cwd_t *cwd)
Initialize a CWD structure.
Definition cwd.c:6
void cwd_clear(cwd_t *cwd)
Clear the current working directory.
Definition cwd.c:46
void cwd_deinit(cwd_t *cwd)
Deinitialize a CWD structure.
Definition cwd.c:12
void file_table_close_all(file_table_t *table)
Close all files in the file table.
Definition file_table.c:96
void file_table_init(file_table_t *table)
Initialize a file table.
Definition file_table.c:9
void file_table_deinit(file_table_t *table)
Deinitialize a file table.
Definition file_table.c:19
namespace_t * namespace_new(namespace_t *parent)
Create a new namespace.
Definition namespace.c:253
void ioring_ctx_deinit(ioring_ctx_t *ctx)
Deinitialize a I/O context.
Definition ring.c:162
void ioring_ctx_init(ioring_ctx_t *ctx)
Initialize a I/O context.
Definition ring.c:146
void note_handler_init(note_handler_t *handler)
Initialize a note handler.
Definition note.c:19
NORETURN void panic(const interrupt_frame_t *frame, const char *format,...)
Panic the kernel, printing a message and halting.
Definition panic.c:292
#define LOG_INFO(format,...)
Definition log.h:91
#define LOG_DEBUG(format,...)
Definition log.h:85
void cache_free(void *obj)
Free an object back to its cache.
Definition cache.c:205
#define CACHE_LINE
Cache line size in bytes.
Definition cache.h:75
void * cache_alloc(cache_t *cache)
Allocate an object from the cache.
Definition cache.c:109
#define CACHE_CREATE(_cache, _name, _size, _alignment, _ctor, _dtor)
Macro to create a cache initializer.
Definition cache.h:148
void space_deinit(space_t *space)
Deinitializes a virtual address space.
Definition space.c:121
uint64_t space_init(space_t *space, uintptr_t startAddress, uintptr_t endAddress, space_flags_t flags)
Initializes a virtual address space.
Definition space.c:64
@ SPACE_MAP_KERNEL_HEAP
Map the kernel heap into the address space.
Definition space.h:35
@ SPACE_MAP_IDENTITY
Map the identity mapped physical memory into the address space.
Definition space.h:36
@ SPACE_MAP_KERNEL_BINARY
Map the kernel binary into the address space.
Definition space.h:34
#define VMM_USER_SPACE_MAX
The maximum address for user space.
Definition vmm.h:74
#define VMM_USER_SPACE_MIN
The minimum address for user space.
Definition vmm.h:75
void env_init(env_t *env)
Initialize the environment.
Definition env.c:8
void env_deinit(env_t *env)
Deinitialize the environment.
Definition env.c:15
void group_member_deinit(group_member_t *member)
Deinitializes a group member.
Definition group.c:74
uint64_t group_member_init(group_member_t *member, group_member_t *group)
Initializes a group member.
Definition group.c:42
void group_remove(group_member_t *member)
Removes a process from its group.
Definition group.c:120
void reaper_push(process_t *process)
Pushes a process to be reaped later.
Definition reaper.c:66
void process_kill(process_t *process, const char *status)
Kills a process, pushing it to the reaper.
Definition process.c:240
void process_set_ns(process_t *process, namespace_t *ns)
Sets the namespace of a process.
Definition process.c:227
#define PROCESS_RCU_THREAD_FOR_EACH(thread, process)
Macro to iterate over all threads in a process.
Definition process.h:232
process_t * process_get_kernel(void)
Gets the kernel process.
Definition process.c:374
process_t * process_new(priority_t priority, group_member_t *group, namespace_t *ns)
Allocates and initializes a new process.
Definition process.c:125
bool process_has_thread(process_t *process, tid_t tid)
Checks if a process has a thread with the specified thread ID.
Definition process.c:358
#define PROCESS_STATUS_MAX
Maximum length of a process exit status.
Definition process.h:59
void process_remove(process_t *process)
Removes a process from the system.
Definition process.c:284
uint64_t process_set_cmdline(process_t *process, char **argv, uint64_t argc)
Sets the command line arguments for a process.
Definition process.c:294
static process_t * process_current(void)
Retrieves the process of the currently running thread.
Definition process.h:131
list_t _processes
Global list of all processes.
namespace_t * process_get_ns(process_t *process)
Gets the namespace of a process.
Definition process.c:206
process_t * process_get(pid_t id)
Gets a process by its ID.
Definition process.c:192
@ PROCESS_NONE
Definition process.h:39
@ PROCESS_DYING
Definition process.h:40
uint64_t thread_send_note(thread_t *thread, const char *string)
Send a note to a thread.
Definition thread.c:176
uint64_t wait_unblock(wait_queue_t *queue, uint64_t amount, errno_t err)
Unblock threads waiting on a wait queue.
Definition wait.c:307
void wait_queue_deinit(wait_queue_t *queue)
Deinitialize wait queue.
Definition wait.c:57
#define WAIT_ALL
Used to indicate that the wait should unblock all waiting threads.
Definition wait.h:43
void wait_queue_init(wait_queue_t *queue)
Initialize wait queue.
Definition wait.c:51
void futex_ctx_deinit(futex_ctx_t *ctx)
Deinitialize a per-process futex context. *.
Definition futex.c:22
void futex_ctx_init(futex_ctx_t *ctx)
Initialize a per-process futex context.
Definition futex.c:16
static void lock_init(lock_t *lock)
Initializes a lock.
Definition lock.h:79
#define LOCK_CREATE()
Create a lock initializer.
Definition lock.h:69
static void lock_release(lock_t *lock)
Releases a lock.
Definition lock.h:175
static void lock_acquire(lock_t *lock)
Acquires a lock, blocking until it is available.
Definition lock.h:96
#define RCU_READ_SCOPE()
RCU read-side critical section for the current scope.
Definition rcu.h:94
void rcu_call_cache_free(void *arg)
Helper callback to free a cache object.
Definition rcu.c:193
void rcu_call(rcu_entry_t *entry, rcu_callback_t func, void *arg)
Add a callback to be executed after a grace period.
Definition rcu.c:72
void map_entry_init(map_entry_t *entry)
Initialize a map entry.
Definition map.c:37
static map_key_t map_key_uint64(uint64_t uint64)
Create a map key from a uint64_t.
Definition map.h:129
uint64_t map_insert(map_t *map, const map_key_t *key, map_entry_t *value)
Insert a key-value pair into the map.
Definition map.c:170
void map_remove(map_t *map, map_entry_t *entry)
Remove a entry from the map.
Definition map.c:319
map_entry_t * map_get(map_t *map, const map_key_t *key)
Get a value from the map by key.
Definition map.c:253
#define MAP_CREATE()
Create a map initializer.
Definition map.h:161
#define REF_TRY(ptr)
Increment reference count, but only if the current count is not zero.
Definition ref.h:95
#define UNREF_DEFER(ptr)
RAII-style cleanup for scoped references.
Definition ref.h:122
#define REF(ptr)
Increment reference count.
Definition ref.h:82
static void ref_init(ref_t *ref, void *callback)
Initialize a reference counter.
Definition ref.h:130
#define UNREF(ptr)
Decrement reference count.
Definition ref.h:109
#define EINVAL
Invalid argument.
Definition errno.h:142
#define ENOMEM
Out of memory.
Definition errno.h:92
#define errno
Error number variable.
Definition errno.h:27
#define EOK
No error.
Definition errno.h:32
#define ARRAY_SIZE(x)
Get the number of elements in a static array.
Definition defs.h:111
#define LIST_CREATE(name)
Creates a list initializer.
Definition list.h:163
static void list_remove_rcu(list_entry_t *entry)
Removes a list entry from its current list in a RCU-safe manner.
Definition list.h:307
static bool list_is_empty(list_t *list)
Checks if a list is empty.
Definition list.h:210
static void list_entry_init(list_entry_t *entry)
Initializes a list entry.
Definition list.h:173
static void list_push_back_rcu(list_t *list, list_entry_t *entry)
Pushes an entry to the end of the list in a RCU-safe manner.
Definition list.h:337
static void list_init(list_t *list)
Initializes a list.
Definition list.h:185
#define PRIORITY_MAX
The maximum priority value, inclusive.
Definition proc.h:43
uint8_t priority_t
Priority type.
Definition proc.h:41
#define NULL
Pointer error value.
Definition NULL.h:25
#define ERR
Integer error value.
Definition ERR.h:17
#define CONTAINER_OF(ptr, type, member)
Container of macro.
__UINT64_TYPE__ tid_t
Thread Identifier.
Definition tid_t.h:12
__UINT64_TYPE__ pid_t
Process Identifier.
Definition pid_t.h:11
errno_t memset_s(void *s, rsize_t smax, int c, rsize_t n)
Definition memset_s.c:9
static process_t * kernelProcess
Definition process.c:40
static cache_t cache
Definition process.c:83
static void process_free(process_t *process)
Definition process.c:85
#define atomic_store(object, desired)
Definition stdatomic.h:289
@ memory_order_relaxed
Definition stdatomic.h:116
#define atomic_fetch_add_explicit(object, operand, order)
Definition stdatomic.h:259
#define atomic_fetch_or(object, operand)
Definition stdatomic.h:285
#define _Atomic(T)
Definition stdatomic.h:59
#define ATOMIC_VAR_INIT(value)
Definition stdatomic.h:74
#define atomic_init(obj, value)
Definition stdatomic.h:75
__UINT64_TYPE__ uint64_t
Definition stdint.h:17
_PUBLIC void * malloc(size_t size)
Definition malloc.c:5
_PUBLIC void free(void *ptr)
Definition free.c:11
_PUBLIC char * strncpy(char *_RESTRICT s1, const char *_RESTRICT s2, size_t n)
Definition strncpy.c:3
_PUBLIC void * memcpy(void *_RESTRICT s1, const void *_RESTRICT s2, size_t n)
Definition memcpy.c:61
_PUBLIC size_t strlen(const char *s)
Definition strlen.c:3
Cache structure.
Definition cache.h:123
Definition cwd.h:18
File table structure.
Definition file_table.h:24
Per-process futex context.
Definition futex.h:34
Group member structure.
Definition group.h:35
A doubly linked list.
Definition list.h:46
A simple ticket lock implementation.
Definition lock.h:44
Map entry structure.
Definition map.h:69
Map key stucture.
Definition map.h:57
Hash map structure.
Definition map.h:90
Namespace structure.
Definition namespace.h:57
Per-process note handler.
Definition note.h:131
char buffer[PROCESS_STATUS_MAX]
Definition process.h:67
Process structure.
Definition process.h:76
file_table_t fileTable
Definition process.h:88
rcu_entry_t rcu
Definition process.h:101
group_member_t group
Definition process.h:100
uint64_t argc
Definition process.h:99
env_t env
Definition process.h:97
lock_t nspaceLock
Definition process.h:86
note_handler_t noteHandler
Definition process.h:92
char ** argv
Definition process.h:98
futex_ctx_t futexCtx
Definition process.h:89
map_entry_t mapEntry
Definition process.h:79
ref_t ref
Definition process.h:77
wait_queue_t suspendQueue
Definition process.h:93
perf_process_ctx_t perf
Definition process.h:90
ioring_ctx_t rings[CONFIG_MAX_RINGS]
Definition process.h:91
space_t space
Definition process.h:84
list_entry_t zombieEntry
Definition process.h:80
process_status_t status
Definition process.h:83
namespace_t * nspace
Definition process.h:85
pid_t id
Definition process.h:81
cwd_t cwd
Definition process.h:87
process_threads_t threads
Definition process.h:96
list_entry_t entry
Definition process.h:78
wait_queue_t dyingQueue
Definition process.h:94
uint64_t count
Definition process.h:52
list_t list
Reads are RCU protected, writes require the lock.
Definition process.h:51
Intrusive RCU head structure.
Definition rcu.h:65
Reference counting structure.
Definition ref.h:52
Virtual address space structure.
Definition space.h:78
Thread of execution structure.
Definition thread.h:61
tid_t id
The thread id, unique within a process_t.
Definition thread.h:64
The primitive that threads block on.
Definition wait.h:185