diff options
author | Anton Kling <anton@kling.gg> | 2024-12-11 14:56:58 +0100 |
---|---|---|
committer | Anton Kling <anton@kling.gg> | 2024-12-12 15:48:19 +0100 |
commit | bc828883c51c3c0f35872019f4db632e4ce82dc5 (patch) | |
tree | c4217493e69e4e8078aa403ca23f71fdf598f826 | |
parent | b343b0dae5aa51b5bd9b195936358341a943b3b2 (diff) |
procfs: Add a procfs
Userland can now interface with processes by writing/reading from
`/proc/<pid>/<entry>`
It can send signals, for example `echo 15 > /proc/1/signal`
-rw-r--r-- | kernel/Makefile | 2 | ||||
-rw-r--r-- | kernel/cpu/syscall.c | 6 | ||||
-rw-r--r-- | kernel/fs/procfs.c | 185 | ||||
-rw-r--r-- | kernel/fs/procfs.h | 3 | ||||
-rw-r--r-- | kernel/init/kernel.c | 2 | ||||
-rw-r--r-- | kernel/sched/scheduler.c | 12 | ||||
-rw-r--r-- | kernel/sched/scheduler.h | 3 |
7 files changed, 212 insertions, 1 deletions
diff --git a/kernel/Makefile b/kernel/Makefile index 34b9b30..3b25dbc 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -1,6 +1,6 @@ CC="i686-sb-gcc" AS="i686-sb-as" -OBJ = arch/i386/boot.o init/kernel.o cpu/gdt.o cpu/reload_gdt.o cpu/idt.o cpu/io.o libc/stdio/print.o drivers/keyboard.o log.o drivers/pit.o libc/string/memcpy.o libc/string/strlen.o libc/string/memcmp.o drivers/ata.o libc/string/memset.o cpu/syscall.o read_eip.o libc/exit/assert.o process.o libc/string/strcpy.o arch/i386/mmu.o kmalloc.o fs/ext2.o fs/vfs.o fs/devfs.o cpu/spinlock.o random.o libc/string/strcmp.o crypto/ChaCha20/chacha20.o crypto/SHA1/sha1.o fs/tmpfs.o libc/string/isequal.o drivers/pst.o kubsan.o drivers/serial.o socket.o poll.o fs/fifo.o hashmap/hashmap.o fs/shm.o elf.o sched/scheduler.o libc/string/copy.o drivers/mouse.o libc/string/strlcpy.o libc/string/strcat.o drivers/vbe.o drivers/pci.o drivers/rtl8139.o network/ethernet.o network/arp.o network/bytes.o network/ipv4.o network/udp.o signal.o network/tcp.o drivers/ahci.o crypto/xoshiro256plusplus/xoshiro256plusplus.o arch/i386/interrupts.o cpu/isr.o lib/stack.o lib/buffered_write.o lib/list.o cpu/arch_inst.o cpu/int_syscall.o lib/ringbuffer.o lib/relist.o arch/i386/tsc.o arch/i386/asm_tsc.o drivers/cmos.o timer.o queue.o fonts.o drivers/ac97.o audio.o libc/ctype/isdigit.o libc/stdlib/parsenum.o libc/ctype/tolower.o lib/sv.o lib/sb.o libc/string/memmove.o libc/ctype/isspace.o +OBJ = arch/i386/boot.o init/kernel.o cpu/gdt.o cpu/reload_gdt.o cpu/idt.o cpu/io.o libc/stdio/print.o drivers/keyboard.o log.o drivers/pit.o libc/string/memcpy.o libc/string/strlen.o libc/string/memcmp.o drivers/ata.o libc/string/memset.o cpu/syscall.o read_eip.o libc/exit/assert.o process.o libc/string/strcpy.o arch/i386/mmu.o kmalloc.o fs/ext2.o fs/vfs.o fs/devfs.o cpu/spinlock.o random.o libc/string/strcmp.o crypto/ChaCha20/chacha20.o crypto/SHA1/sha1.o fs/tmpfs.o libc/string/isequal.o drivers/pst.o kubsan.o drivers/serial.o socket.o poll.o fs/fifo.o hashmap/hashmap.o fs/shm.o elf.o sched/scheduler.o libc/string/copy.o drivers/mouse.o libc/string/strlcpy.o libc/string/strcat.o drivers/vbe.o drivers/pci.o drivers/rtl8139.o network/ethernet.o network/arp.o network/bytes.o network/ipv4.o network/udp.o signal.o network/tcp.o drivers/ahci.o crypto/xoshiro256plusplus/xoshiro256plusplus.o arch/i386/interrupts.o cpu/isr.o lib/stack.o lib/buffered_write.o lib/list.o cpu/arch_inst.o cpu/int_syscall.o lib/ringbuffer.o lib/relist.o arch/i386/tsc.o arch/i386/asm_tsc.o drivers/cmos.o timer.o queue.o fonts.o drivers/ac97.o audio.o libc/ctype/isdigit.o libc/stdlib/parsenum.o libc/ctype/tolower.o lib/sv.o lib/sb.o libc/string/memmove.o libc/ctype/isspace.o fs/procfs.o CFLAGS = -std=c99 -O0 -fsanitize=vla-bound,shift-exponent,pointer-overflow,shift,signed-integer-overflow,bounds -ggdb -ffreestanding -Wall -Wextra -Wno-int-conversion -Wno-unused-parameter -Werror -mgeneral-regs-only -Wimplicit-fallthrough -I./libc/include/ -I. -Wno-pointer-sign -DKERNEL LDFLAGS= #CFLAGS = -std=c99 -O3 -flto -ggdb -ffreestanding -Wall -Wextra -Wno-int-conversion -Wno-unused-parameter -Werror -mgeneral-regs-only -Wimplicit-fallthrough -I./libc/include/ -I. -Wno-pointer-sign -DKERNEL diff --git a/kernel/cpu/syscall.c b/kernel/cpu/syscall.c index a78e63e..31f1fc5 100644 --- a/kernel/cpu/syscall.c +++ b/kernel/cpu/syscall.c @@ -427,6 +427,8 @@ void syscall_wait(int *status) { if (status) { *status = current_task->child_rc; } + process_remove_reference(current_task->child); + current_task->child = NULL; return; } do { @@ -444,6 +446,10 @@ void syscall_wait(int *status) { if (status) { *status = current_task->child_rc; } + if (current_task->child->dead) { + process_remove_reference(current_task->child); + current_task->child = NULL; + } } int syscall_fork(void) { diff --git a/kernel/fs/procfs.c b/kernel/fs/procfs.c new file mode 100644 index 0000000..f6a4fef --- /dev/null +++ b/kernel/fs/procfs.c @@ -0,0 +1,185 @@ +#include <errno.h> +#include <fs/procfs.h> +#include <lib/sb.h> +#include <lib/sv.h> +#include <stdio.h> + +#define ARRAY_LENGTH(a) (sizeof(a) / sizeof((a)[0])) + +vfs_inode_t *procfs_open(const char *p); + +void procfs_close(vfs_fd_t *fd) { + return; +} + +void process_close(vfs_fd_t *fd) { + process_t *p = fd->inode->internal_object; + if (!p) { + return; + } + process_remove_reference(p); +} + +int procfs_read(u8 *buffer, u64 offset, u64 len, vfs_fd_t *fd) { + struct sb builder; + sb_init_buffer(&builder, buffer, len); + + size_t read_amount = 0; + + process_t *p = ready_queue; + for (; p; p = p->next) { + struct dirent entry; + entry.d_ino = p->pid; + ksnprintf(entry.d_name, sizeof(entry.d_name), "%u", p->pid); + + if (read_amount >= offset) { + if (0 == sb_append_buffer(&builder, (u8 *)&entry, sizeof(entry))) { + break; + } + } + read_amount += sizeof(struct dirent); + } + return sv_length(SB_TO_SV(builder)); +} + +#define PROCESS_ROOT 0 +#define PROCESS_SIGNAL 1 +#define PROCESS_NAME 2 + +struct dirent process_entries[] = { + { + .d_ino = PROCESS_ROOT, + .d_name = "", + }, + { + .d_ino = PROCESS_ROOT, + .d_name = ".", + }, + { + .d_ino = PROCESS_SIGNAL, + .d_name = "signal", + }, + { + .d_ino = PROCESS_NAME, + .d_name = "name", + }, +}; + +int process_read(u8 *buffer, u64 offset, u64 len, vfs_fd_t *fd) { + process_t *p = fd->inode->internal_object; + + struct sb builder; + sb_init_buffer(&builder, buffer, len); + + int id = fd->inode->inode_num; + if (PROCESS_ROOT == id) { + size_t read_amount = 0; + for (size_t i = 0; i < ARRAY_LENGTH(process_entries); i++) { + if (0 == strlen(process_entries[i].d_name)) { + continue; + } + if (read_amount >= offset) { + if (0 == sb_append_buffer(&builder, (u8 *)&process_entries[i], + sizeof(struct dirent))) { + break; + } + } + read_amount += sizeof(struct dirent); + } + return sv_length(SB_TO_SV(builder)); + } + if (PROCESS_NAME == id) { + struct sv program_name = C_TO_SV(p->program_name); + sv_take(program_name, &program_name, offset); + sb_append_sv(&builder, program_name); + return sv_length(SB_TO_SV(builder)); + } + return -EBADF; +} + +int process_write(u8 *buffer, u64 offset, u64 len, vfs_fd_t *fd) { + (void)offset; + process_t *p = fd->inode->internal_object; + + int id = fd->inode->inode_num; + if (PROCESS_SIGNAL == id) { + struct sv t = sv_init(buffer, len); + int got_num; + u64 signal = sv_parse_unsigned_number(t, NULL, &got_num); + if (got_num) { + signal_process(p, signal); + } + // TODO: Add the ability to write "SIGTERM", "SIGKILL" etc without + // writing the number. + return 0; + } + return -EBADF; +} + +vfs_inode_t *open_process(u64 pid, int id) { + process_t *process = NULL; + process_t *t = ready_queue; + for (; t; t = t->next) { + if ((u64)t->pid == pid) { + process = t; + break; + } + } + if (!process) { + return NULL; + } + + vfs_inode_t *inode = vfs_create_inode( + id /*inode_num*/, 0 /*type*/, 0 /*has_data*/, 0 /*can_write*/, + 0 /*is_open*/, 0, process /*internal_object*/, 0 /*file_size*/, + procfs_open, NULL /*create_file*/, process_read, process_write, + process_close, NULL /*create_directory*/, NULL /*get_vm_object*/, + NULL /*truncate*/, NULL /*stat*/, NULL /*send_signal*/, NULL /*connect*/); + if (!inode) { + return NULL; + } + process->reference_count++; + return inode; +} + +vfs_inode_t *procfs_open(const char *p) { + struct sv path = C_TO_SV(p); + + if ((sv_try_eat(path, &path, C_TO_SV("/")) && 0 == sv_length(path)) || + 0 == sv_length(path)) { + return vfs_create_inode( + 0 /*inode_num*/, 0 /*type*/, 0 /*has_data*/, 0 /*can_write*/, + 0 /*is_open*/, 0, NULL /*internal_object*/, 0 /*file_size*/, + procfs_open, NULL /*create_file*/, procfs_read, NULL /* write */, + procfs_close, NULL /*create_directory*/, NULL /*get_vm_object*/, + NULL /*truncate*/, NULL /*stat*/, NULL /*send_signal*/, + NULL /*connect*/); + } + + int got_num; + u64 pid = sv_parse_unsigned_number(path, &path, &got_num); + if (!got_num) { + return NULL; + } + + // NOTE: There should only be one '/' since the previous vfs_open will + // call vfs_clean_path first. This is a bit hacky and that code might + // change later and as a result introduce bugs here. + sv_try_eat(path, &path, C_TO_SV("/")); + for (size_t i = 0; i < ARRAY_LENGTH(process_entries); i++) { + if (sv_eq(path, C_TO_SV(process_entries[i].d_name))) { + return open_process(pid, process_entries[i].d_ino); + } + } + return NULL; +} + +vfs_inode_t *procfs_mount(void) { + vfs_inode_t *inode = vfs_create_inode( + 0 /*inode_num*/, 0 /*type*/, 0 /*has_data*/, 0 /*can_write*/, + 0 /*is_open*/, 0, NULL /*internal_object*/, 0 /*file_size*/, procfs_open, + NULL /*create_file*/, procfs_read, NULL /* write */, procfs_close, + NULL /*create_directory*/, NULL /*get_vm_object*/, NULL /*truncate*/, + NULL /*stat*/, NULL /*send_signal*/, NULL /*connect*/); + return inode; +} diff --git a/kernel/fs/procfs.h b/kernel/fs/procfs.h new file mode 100644 index 0000000..8715789 --- /dev/null +++ b/kernel/fs/procfs.h @@ -0,0 +1,3 @@ +#include <fs/vfs.h> + +vfs_inode_t *procfs_mount(void); diff --git a/kernel/init/kernel.c b/kernel/init/kernel.c index 79c6366..5bf959b 100644 --- a/kernel/init/kernel.c +++ b/kernel/init/kernel.c @@ -17,6 +17,7 @@ #include <fcntl.h> #include <fs/devfs.h> #include <fs/ext2.h> +#include <fs/procfs.h> #include <fs/shm.h> #include <fs/vfs.h> #include <interrupts.h> @@ -95,6 +96,7 @@ void kernel_main(u32 kernel_end, unsigned long magic, unsigned long addr, global_socket_init(); + vfs_mount("/proc", procfs_mount()); vfs_mount("/dev", devfs_mount()); assert(ahci_init()); vfs_inode_t *ext2_mount_point = ext2_mount(); diff --git a/kernel/sched/scheduler.c b/kernel/sched/scheduler.c index d0452fd..1b36e8a 100644 --- a/kernel/sched/scheduler.c +++ b/kernel/sched/scheduler.c @@ -98,6 +98,7 @@ process_t *create_process(process_t *p, u32 esp, u32 eip) { if (!r) { return NULL; } + r->reference_count = 1; r->tcb = kcalloc(1, sizeof(struct TCB)); if (!r->tcb) { kfree(r); @@ -147,6 +148,9 @@ process_t *create_process(process_t *p, u32 esp, u32 eip) { r->cr3 = get_active_pagedirectory(); } r->parent = p; + if (p) { + p->reference_count++; + } r->tcb->CR3 = r->cr3->physical_address; @@ -205,6 +209,14 @@ void free_process(process_t *p) { mmu_free_pagedirectory(p->cr3); } +void process_remove_reference(process_t *p) { + assert(0 != p->reference_count); + p->reference_count--; + if (0 == p->reference_count) { + kfree(p); + } +} + void exit_process(process_t *p, int status) { assert(p->pid != 1); disable_interrupts(); diff --git a/kernel/sched/scheduler.h b/kernel/sched/scheduler.h index 9d548a3..d03277e 100644 --- a/kernel/sched/scheduler.h +++ b/kernel/sched/scheduler.h @@ -69,6 +69,8 @@ struct Process { struct list write_list; struct list disconnect_list; + int reference_count; + struct list event_queue; struct stack restore_context_stack; @@ -98,4 +100,5 @@ int get_task_from_pid(pid_t pid, process_t **out); void free_process(process_t *p); void *get_free_virtual_memory(size_t length); void signal_process(process_t *p, int sig); +void process_remove_reference(process_t *p); #endif |