MOS Source Code
Loading...
Searching...
No Matches
process.c
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-3.0-or-later
2
3#include "mos/tasks/process.h"
4
7#include "mos/io/io.h"
8#include "mos/mm/mm.h"
9#include "mos/tasks/signal.h"
10
11#include <abi-bits/wait.h>
12#include <errno.h>
13#include <limits.h>
16#include <mos/filesystem/vfs.h>
21#include <mos/misc/panic.h>
22#include <mos/mm/cow.h>
25#include <mos/syslog/printk.h>
26#include <mos/tasks/schedule.h>
28#include <mos/tasks/thread.h>
29#include <mos/tasks/wait.h>
30#include <mos_stdio.h>
31#include <mos_stdlib.h>
32#include <mos_string.h>
33
34hashmap_t process_table = { 0 }; // pid_t -> process_t
35
36static const char *vmap_content_str[] = {
37 [VMAP_UNKNOWN] = "unknown", //
38 [VMAP_STACK] = "stack", //
39 [VMAP_FILE] = "file", //
40 [VMAP_MMAP] = "mmap", //
41 [VMAP_DMA] = "DMA", //
42};
43
44const char *vmap_type_str[] = {
45 [VMAP_TYPE_SHARED] = "shared",
46 [VMAP_TYPE_PRIVATE] = "private",
47};
48
50{
51 static pid_t next = 1;
52 return (pid_t){ next++ };
53}
54
56{
57 process_t *proc = kmalloc(process_cache);
58
60 proc->pid = new_process_id();
63
65
66 proc->name = strdup(name ? name : "<unknown>");
67
68 if (likely(parent))
69 {
70 proc->parent = parent;
71 }
72 else if (unlikely(proc->pid == 1) || unlikely(proc->pid == 2))
73 {
74 proc->parent = proc;
75 pr_demph(process, "special process %pp created", (void *) proc);
76 }
77 else
78 {
79 pr_emerg("process %pp has no parent", (void *) proc);
80 kfree(proc->name);
81 kfree(proc);
82 return NULL;
83 }
84
86
87 if (unlikely(proc->pid == 2))
88 {
89 proc->mm = platform_info->kernel_mm; // ! Special case: PID 2 (kthreadd) uses the kernel page table
90 }
91 else
92 {
93 proc->mm = mm_create_context();
94 }
95
96 if (unlikely(!proc->mm))
97 {
98 pr_emerg("failed to create page table for process %pp", (void *) proc);
99 kfree(proc->name);
100 kfree(proc);
101 return NULL;
102 }
103
104 return proc;
105}
106
108{
109 if (!process_is_valid(process))
110 return;
111
112 hashmap_remove(&process_table, process->pid);
113
114 MOS_ASSERT(process != current_process);
115 pr_dinfo2(process, "destroying process %pp", (void *) process);
116
117 MOS_ASSERT_X(process->main_thread != NULL, "main thread must be dead before destroying process");
118
120 thread_destroy(process->main_thread);
121 process->main_thread = NULL;
122
123 if (process->name != NULL)
124 kfree(process->name);
125
126 if (process->mm != NULL)
127 {
128 spinlock_acquire(&process->mm->mm_lock);
129 list_foreach(vmap_t, vmap, process->mm->mmaps)
130 {
131 spinlock_acquire(&vmap->lock);
132 vmap_destroy(vmap);
133 }
134
135 // free page table
136 MOS_ASSERT(process->mm != current_mm);
137 mm_destroy_context(process->mm);
138 }
139
140 kfree(process);
141}
142
143process_t *process_new(process_t *parent, const char *name, const stdio_t *ios)
144{
145 process_t *proc = process_allocate(parent, name);
146 if (unlikely(!proc))
147 return NULL;
148 pr_dinfo2(process, "creating process %pp", (void *) proc);
149
150 process_attach_ref_fd(proc, ios && ios->in ? ios->in : io_null, FD_FLAGS_NONE);
151 process_attach_ref_fd(proc, ios && ios->out ? ios->out : io_null, FD_FLAGS_NONE);
152 process_attach_ref_fd(proc, ios && ios->err ? ios->err : io_null, FD_FLAGS_NONE);
153
154 proc->main_thread = thread_new(proc, THREAD_MODE_USER, proc->name, 0, NULL);
156
157 void *old_proc = hashmap_put(&process_table, proc->pid, proc);
158 MOS_ASSERT_X(old_proc == NULL, "process already exists, go and buy yourself a lottery :)");
159 return proc;
160}
161
163{
165 if (process_is_valid(p))
166 return p;
167
168 return NULL;
169}
170
172{
174
175 // find a free fd
176 fd_t fd = 0;
177 while (process->files[fd].io)
178 {
179 fd++;
181 {
182 mos_warn("process %pp has too many open files", (void *) process);
183 return -EMFILE;
184 }
185 }
186
187 process->files[fd].io = io_ref(file);
188 process->files[fd].flags = flags;
189 return fd;
190}
191
193{
195 if (fd < 0 || fd >= MOS_PROCESS_MAX_OPEN_FILES)
196 return NULL;
197 return process->files[fd].io;
198}
199
201{
203 if (fd < 0 || fd >= MOS_PROCESS_MAX_OPEN_FILES)
204 return false;
205 io_t *io = process->files[fd].io;
206
207 if (unlikely(!io_valid(io)))
208 return false;
209
210 io_unref(process->files[fd].io);
211 process->files[fd] = nullfd;
212 return true;
213}
214
215pid_t process_wait_for_pid(pid_t pid, u32 *exit_code, u32 flags)
216{
217 if (pid == -1)
218 {
219 if (list_is_empty(&current_process->children))
220 return -ECHILD; // no children to wait for at all
221
222 find_dead_child:;
223 // first find if there are any dead children
225 {
226 if (child->exited)
227 {
228 pid = child->pid;
229 goto child_dead;
230 }
231 }
232
233 if (flags & WNOHANG)
234 return 0; // no dead children, and we don't want to wait
235
236 // we have to wait for a child to die
237 MOS_ASSERT_X(!current_process->signal_info.sigchild_waitlist.closed, "waitlist is in use");
238
239 const bool ok = reschedule_for_waitlist(&current_process->signal_info.sigchild_waitlist);
240 MOS_ASSERT(ok); // we just created the waitlist, it should be empty
241
242 // we are woken up by a signal, or a child dying
243 if (signal_has_pending())
244 {
245 pr_dinfo2(process, "woken up by signal");
246 waitlist_remove_me(&current_process->signal_info.sigchild_waitlist);
247 return -ERESTARTSYS;
248 }
249
250 goto find_dead_child;
251 }
252
253child_dead:;
254 process_t *target_proc = process_get(pid);
255 if (target_proc == NULL)
256 {
257 pr_warn("process %d does not exist", pid);
258 return -ECHILD;
259 }
260
261 while (true)
262 {
263 // child is already dead
264 if (target_proc->exited)
265 break;
266
267 // wait for the child to die
268 bool ok = reschedule_for_waitlist(&current_process->signal_info.sigchild_waitlist);
269 MOS_ASSERT(ok);
270 }
271
272 list_remove(target_proc); // remove from parent's children list
273 pid = target_proc->pid;
274 if (exit_code)
275 *exit_code = target_proc->exit_status;
276 process_destroy(target_proc);
277
278 return pid;
279}
280
281void process_exit(process_t *process, u8 exit_code, signal_t signal)
282{
284 pr_dinfo2(process, "process %pp exited with code %d, signal %d", (void *) process, exit_code, signal);
285
286 if (unlikely(process->pid == 1))
287 mos_panic("init process terminated with code %d, signal %d", exit_code, signal);
288
289 list_foreach(thread_t, thread, process->threads)
290 {
291 spinlock_acquire(&thread->state_lock);
292 if (thread->state == THREAD_STATE_DEAD)
293 {
294 pr_dinfo2(process, "cleanup thread %pt", (void *) thread);
295 MOS_ASSERT(thread != current_thread);
296 hashmap_remove(&thread_table, thread->tid);
297 list_remove(thread);
298 thread_destroy(thread);
299 }
300 else
301 {
302 // send termination signal to all threads, except the current one
303 if (thread != current_thread)
304 {
305 pr_dinfo2(signal, "sending SIGKILL to thread %pt", (void *) thread);
306 spinlock_release(&thread->state_lock);
307 signal_send_to_thread(thread, SIGKILL);
308 thread_wait_for_tid(thread->tid);
309 spinlock_acquire(&thread->state_lock);
310 pr_dinfo2(process, "thread %pt terminated", (void *) thread);
311 MOS_ASSERT_X(thread->state == THREAD_STATE_DEAD, "thread %pt is not dead", (void *) thread);
312 hashmap_remove(&thread_table, thread->tid);
313 thread_destroy(thread);
314 }
315 else
316 {
317 spinlock_release(&thread->state_lock);
318 process->main_thread = thread; // make sure we properly destroy the main thread at the end
319 pr_dinfo2(process, "thread %pt is current thread, making it main thread", (void *) thread);
320 }
321 }
322 }
323
324 size_t files_total = 0;
325 size_t files_closed = 0;
326 for (int i = 0; i < MOS_PROCESS_MAX_OPEN_FILES; i++)
327 {
328 fd_type file = process->files[i];
329 process->files[i] = nullfd;
330
331 if (io_valid(file.io))
332 {
333 files_total++;
334 if (io_unref(file.io) == NULL)
335 files_closed++;
336 }
337 }
338
339 // re-parent all children to parent of this process
340 list_foreach(process_t, child, process->children)
341 {
342 child->parent = process->parent;
344 list_node_append(&process->parent->children, list_node(child));
345 }
346
348
349 pr_dinfo2(process, "closed %zu/%zu files owned by %pp", files_closed, files_total, (void *) process);
350 process->exited = true;
351 process->exit_status = W_EXITCODE(exit_code, signal);
352
353 // let the parent wait for our exit
354 spinlock_acquire(&current_thread->state_lock);
355
356 // wake up parent
357 pr_dinfo2(process, "waking up parent %pp", (void *) process->parent);
358 signal_send_to_process(process->parent, SIGCHLD);
360
363}
364
365void process_dump_mmaps(const process_t *process)
366{
367 pr_info("process %pp:", (void *) process);
368 size_t i = 0;
369 list_foreach(vmap_t, map, process->mm->mmaps)
370 {
371 i++;
372 const char *typestr = vmap_content_str[map->content];
373 const char *forkmode = vmap_type_str[map->type];
374 pr_info2(" %3zd: %pvm, %s, %s", i, (void *) map, typestr, forkmode);
375 }
376
377 pr_info("total: %zd memory regions", i);
378}
379
381{
382 pr_dinfo2(signal, "registering signal handler for process %pp, signal %d", (void *) process, sig);
383 if (!sigaction)
384 {
385 process->signal_info.handlers[sig] = (sigaction_t){ .handler = SIG_DFL };
386 return true;
387 }
388 process->signal_info.handlers[sig] = *sigaction;
389 return true;
390}
391
392// ! sysfs support
393
394#define do_print(fmt, name, item) sysfs_printf(f, "%-10s: " fmt "\n", name, item);
395
397{
398 do_print("%d", "pid", current_process->pid);
399 do_print("%s", "name", current_process->name);
400
401 do_print("%d", "parent", current_process->parent->pid);
402
403 list_foreach(thread_t, thread, current_process->threads)
404 {
405 do_print("%pt", "thread", (void *) thread);
406 }
407 return true;
408}
409
411{
412 do_print("%d", "tid", current_thread->tid);
413 do_print("%s", "name", current_thread->name);
414 return true;
415}
416
418{
419 list_foreach(vmap_t, vmap, current_mm->mmaps)
420 {
421#define stat_line(fmt) " %9s: " fmt "\n"
422 sysfs_printf(f, PTR_RANGE "\n", vmap->vaddr, vmap->vaddr + vmap->npages * MOS_PAGE_SIZE);
423 sysfs_printf(f, stat_line("%pvf,%s%s"), "Perms", //
424 (void *) &vmap->vmflags, //
425 vmap->vmflags & VM_USER ? "user" : "kernel", //
426 vmap->vmflags & VM_GLOBAL ? " global" : "" //
427 );
428 sysfs_printf(f, stat_line("%s"), "Type", vmap_type_str[vmap->type]);
429 sysfs_printf(f, stat_line("%s"), "Content", vmap_content_str[vmap->content]);
430 if (vmap->content == VMAP_FILE)
431 {
432 char filepath[MOS_PATH_MAX_LENGTH];
433 io_get_name(vmap->io, filepath, sizeof(filepath));
434 sysfs_printf(f, stat_line("%s"), " File", filepath);
435 sysfs_printf(f, stat_line("%zu bytes"), " Offset", vmap->io_offset);
436 }
437 sysfs_printf(f, stat_line("%zu pages"), "Total", vmap->npages);
438 sysfs_printf(f, stat_line("%zu pages"), "Regular", vmap->stat.regular);
439 sysfs_printf(f, stat_line("%zu pages"), "PageCache", vmap->stat.pagecache);
440 sysfs_printf(f, stat_line("%zu pages"), "CoW", vmap->stat.cow);
441#undef stat_line
442 sysfs_printf(f, "\n");
443 }
444 return true;
445}
446
452
#define MOS_ASSERT_X(cond, msg,...)
Definition assert.h:15
#define MOS_ASSERT(cond)
Definition assert.h:14
#define MOS_UNREACHABLE()
Definition assert.h:11
#define mos_warn(fmt,...)
Definition assert.h:23
#define MOS_PROCESS_MAX_OPEN_FILES
Definition autoconf.h:26
#define MOS_PATH_MAX_LENGTH
Definition autoconf.h:27
#define MOS_PAGE_SIZE
Definition autoconf.h:6
fd_flags_t
Definition fs_types.h:47
@ FD_FLAGS_NONE
Definition fs_types.h:48
dentry_t * dentry_ref_up_to(dentry_t *dentry, dentry_t *root)
Increment the reference count of a dentry up to a given dentry.
void dentry_unref(dentry_t *dentry)
Decrement the reference count of a dentry.
long signal_send_to_process(process_t *target, signal_t signal)
Send a signal to a process, an arbitrary thread will be chosen to receive the signal.
Definition signal.c:126
long signal_send_to_thread(thread_t *target, signal_t signal)
Send a signal to a thread.
Definition signal.c:92
bool signal_has_pending(void)
Return true if there's a pending signal.
Definition signal.c:301
MOSAPI void * hashmap_get(hashmap_t *map, uintn key)
Definition hashmap.c:96
MOSAPI void * hashmap_put(hashmap_t *map, uintn key, void *value)
Definition hashmap.c:68
MOSAPI void * hashmap_remove(hashmap_t *map, uintn key)
Definition hashmap.c:117
MOSAPI char * strdup(const char *src)
MOSAPI void linked_list_init(list_node_t *head_node)
Initialise a circular double linked list.
Definition list.c:15
MOSAPI void list_node_append(list_node_t *head, list_node_t *item)
Definition list.c:68
#define list_foreach(t, v, h)
Iterate over a list.
Definition list.h:83
#define list_node(element)
Get the ‘list_node’ of a list element. This is exactly the reverse of ‘list_entry’ above.
Definition list.h:68
MOSAPI bool list_is_empty(const list_node_t *head)
Definition list.c:21
#define list_remove(element)
Definition list.h:74
void mm_destroy_context(mm_context_t *table)
Destroy a user-mode platform-dependent page table.
Definition mm.c:83
mm_context_t * mm_create_context(void)
Create a user-mode platform-dependent page table.
Definition mm.c:67
void vmap_destroy(vmap_t *vmap)
Destroy a vmap object, and unmmap the region.
Definition mm.c:171
@ VMAP_TYPE_PRIVATE
Definition mm.h:31
@ VMAP_TYPE_SHARED
Definition mm.h:32
@ VMAP_MMAP
Definition mm.h:25
@ VMAP_DMA
Definition mm.h:26
@ VMAP_UNKNOWN
Definition mm.h:22
@ VMAP_FILE
Definition mm.h:24
@ VMAP_STACK
Definition mm.h:23
slab_t * process_cache
Definition tasks.c:22
#define nullfd
Definition task_types.h:40
@ THREAD_MODE_USER
Definition task_types.h:22
dentry_t * root_dentry
Definition vfs.c:36
io_t * io_ref(io_t *io)
Definition io.c:73
__nodiscard bool io_valid(const io_t *io)
Definition io.c:127
io_t * io_unref(io_t *io)
Definition io.c:92
io_t *const io_null
Definition io.c:42
void io_get_name(const io_t *io, char *buf, size_t size)
Definition io.c:307
#define likely(x)
Definition mos_global.h:39
#define unlikely(x)
Definition mos_global.h:40
#define current
#define mos_panic(fmt,...)
Definition panic.h:55
#define NULL
Definition pb_syshdr.h:46
#define current_thread
Definition platform.h:30
@ VM_GLOBAL
Definition platform.h:48
@ VM_USER
Definition platform.h:45
@ THREAD_STATE_DEAD
thread is dead, and will be cleaned up soon by the scheduler
Definition platform.h:67
#define current_mm
Definition platform.h:32
#define current_process
Definition platform.h:31
#define pr_info2(fmt,...)
Definition printk.h:36
#define pr_warn(fmt,...)
Definition printk.h:38
#define pr_emerg(fmt,...)
Definition printk.h:39
#define pr_info(fmt,...)
Definition printk.h:35
#define pr_demph(feat, fmt,...)
Definition printk.h:29
#define pr_dinfo2(feat, fmt,...)
Definition printk.h:27
fd_t process_attach_ref_fd(process_t *process, io_t *file, fd_flags_t flags)
Definition process.c:171
static const char * vmap_content_str[]
Definition process.c:36
pid_t process_wait_for_pid(pid_t pid, u32 *exit_code, u32 flags)
Definition process.c:215
#define stat_line(fmt)
static sysfs_item_t process_sysfs_items[]
Definition process.c:447
bool process_register_signal_handler(process_t *process, signal_t sig, const sigaction_t *sigaction)
Definition process.c:380
#define do_print(fmt, name, item)
Definition process.c:394
void process_destroy(process_t *process)
Definition process.c:107
void process_dump_mmaps(const process_t *process)
Definition process.c:365
hashmap_t process_table
Definition process.c:34
process_t * process_get(pid_t pid)
Definition process.c:162
void process_exit(process_t *process, u8 exit_code, signal_t signal)
Definition process.c:281
static bool process_sysfs_thread_stat(sysfs_file_t *f)
Definition process.c:410
io_t * process_get_fd(process_t *process, fd_t fd)
Definition process.c:192
const char * vmap_type_str[]
Definition process.c:44
static bool process_sysfs_process_stat(sysfs_file_t *f)
Definition process.c:396
static pid_t new_process_id(void)
Definition process.c:49
process_t * process_allocate(process_t *parent, const char *name)
Definition process.c:55
static bool process_sysfs_vmap_stat(sysfs_file_t *f)
Definition process.c:417
process_t * process_new(process_t *parent, const char *name, const stdio_t *ios)
Definition process.c:143
bool process_detach_fd(process_t *process, fd_t fd)
Definition process.c:200
should_inline bool process_is_valid(const process_t *process)
Definition process.h:23
#define PROCESS_MAGIC_PROC
Definition process.h:9
mos_platform_info_t *const platform_info
__nodiscard bool reschedule_for_waitlist(waitlist_t *waitlist)
Definition schedule.c:174
#define ERESTARTSYS
Definition signal.h:10
int signal_t
Definition signal_types.h:9
const char * name
Definition slab.c:31
#define spinlock_acquire(lock)
Definition spinlock.h:61
#define spinlock_release(lock)
Definition spinlock.h:62
fd_flags_t flags
Definition task_types.h:37
io_t * io
Definition task_types.h:36
Definition io.h:46
A node in a linked list.
Definition list.h:27
spinlock_t mm_lock
protects [pgd] and the [mmaps] list (the list itself, not the vmap_t objects)
Definition platform.h:81
list_head mmaps
Definition platform.h:83
mm_context_t * kernel_mm
Definition platform.h:118
sigaction_t handlers[SIGNAL_MAX_N]
signal handlers
Definition task_types.h:30
waitlist_t sigchild_waitlist
the parent is waiting for a child to exit, if not empty
Definition task_types.h:31
mm_context_t * mm
Definition task_types.h:59
bool exited
true if the process has exited
Definition task_types.h:51
process_t * parent
Definition task_types.h:47
process_signal_info_t signal_info
signal handling info
Definition task_types.h:64
dentry_t * working_directory
Definition task_types.h:60
const char * name
Definition task_types.h:46
list_head threads
Definition task_types.h:57
thread_t * main_thread
Definition task_types.h:56
list_head children
list of children processes
Definition task_types.h:48
fd_type files[MOS_PROCESS_MAX_OPEN_FILES]
Definition task_types.h:54
pid_t pid
Definition task_types.h:45
u32 exit_status
exit status
Definition task_types.h:52
A wrapper type for the standard I/O streams.
Definition process.h:17
io_t * err
Definition process.h:18
io_t * out
Definition process.h:18
io_t * in
Definition process.h:18
spinlock_t state_lock
protects the thread state
Definition task_types.h:82
Definition mm.h:58
ssize_t sysfs_printf(sysfs_file_t *file, const char *fmt,...)
Definition sysfs.c:73
#define SYSFS_RO_ITEM(_name, _show_fn)
Definition sysfs.h:48
#define SYSFS_AUTOREGISTER(sysfs_name, sysfs_items)
void thread_destroy(thread_t *thread)
Definition thread.c:46
hashmap_t thread_table
Definition thread.c:23
bool thread_wait_for_tid(tid_t tid)
Definition thread.c:175
void thread_exit_locked(thread_t *t)
Definition thread.c:203
thread_t * thread_new(process_t *owner, thread_mode mode, const char *name, size_t stack_size, void *stack)
Definition thread.c:77
unsigned int u32
Definition types.h:21
#define PTR_RANGE
Definition types.h:36
s32 fd_t
Definition types.h:81
s32 pid_t
Definition types.h:78
unsigned char u8
Definition types.h:19
size_t waitlist_wake(waitlist_t *list, size_t max_wakeups)
Definition wait.c:45
__BEGIN_DECLS void waitlist_init(waitlist_t *list)
Definition wait.c:23
void waitlist_remove_me(waitlist_t *waitlist)
Definition wait.c:86