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