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