Add process scheduler, pipes, and separate terminal from shell

This commit is contained in:
2026-06-19 21:49:29 +03:00
parent 452125782e
commit 415f78fd40
17 changed files with 524 additions and 120 deletions
+36 -24
View File
@@ -5,8 +5,10 @@
#include "src/kernel/panic.h"
#include "src/kernel/path.h"
#include "src/kernel/pic.h"
#include "src/kernel/pipe.h"
#include "src/kernel/process.h"
#include "src/kernel/syscall.h"
#include "src/kernel/timer.h"
#include "src/kernel/tss.h"
#include "src/kernel/util.h"
#include "src/kernel/vga.h"
@@ -44,46 +46,56 @@ void kernel_main() {
idt_set_entry(0x0E, isr_page_fault, 0x8E);
idt_set_entry(0x0D, isr_general_violation, 0x8E);
idt_set_entry(46, isr_ata_primary, 0x8E);
__asm__ volatile("sti");
gdt_init();
syscall_init();
process_t *kernel = memory_allocate(sizeof(process_t));
kernel->pml4 = (void *)KERNEL_VIRTUAL_PML4;
kernel->root = fs_mount();
kernel->pml4 = (uint64_t *)KERNEL_VIRTUAL_PML4;
kernel->pid = 0;
kernel->state = PROCESS_RUNNING;
kernel->kernel_stack = kernel->kernel_rsp = (uint8_t *)memory_allocate(PAGE_SIZE) + PAGE_SIZE;
kernel->user_stack = kernel->user_rsp = (uint8_t *)memory_allocate(PAGE_SIZE) + PAGE_SIZE;
kernel->cwd = memory_allocate(sizeof(path_t));
kernel->fds[0] = keyboard_init();
kernel->fds[1] = vga_init();
kernel->free_fd = 2;
kernel->root = fs_mount();
kernel->fds[kernel->free_fd++] = keyboard_init();
kernel->fds[kernel->free_fd++] = vga_init();
kernel->code = EXIT_CODE_OK;
fs_node_t *bin = path_open_file(kernel->root, kernel->cwd, "bin/terminal");
current_process = kernel;
tss.rsp0 = (uint64_t)kernel->kernel_stack;
ASSERT(bin, "kernel: terminal not found");
fs_node_t *terminal_node = path_open_file(kernel->root, kernel->cwd, "bin/terminal");
fs_node_t *shell_node = path_open_file(kernel->root, kernel->cwd, "bin/shell");
uint8_t *code = memory_allocate(bin->size);
fs_read(bin, 0, bin->size, code);
ASSERT(terminal_node, "kernel: terminal not found");
ASSERT(shell_node, "kernel: shell not found");
process_t *terminal = process_create(kernel, code, bin->size);
uint8_t *terminal_code = memory_allocate(terminal_node->size);
fs_read(terminal_node, 0, terminal_node->size, terminal_code);
fs_close(terminal_node);
memory_free(code);
fs_close(bin);
uint8_t *shell_code = memory_allocate(shell_node->size);
fs_read(shell_node, 0, shell_node->size, shell_code);
fs_close(shell_node);
__asm__ volatile("mov %0, %%cr3" : : "r"((uint64_t)VIRT_TO_PHYS((void *)KERNEL_VIRTUAL_PML4)) : "memory");
process_t *terminal = process_create(kernel, terminal_code, terminal_node->size);
memory_free(terminal_code);
uint8_t *stack = (uint8_t *)terminal->user_stack;
process_t *shell = process_create(kernel, shell_code, shell_node->size);
memory_free(shell_code);
stack -= sizeof(uint64_t);
*(uint64_t *)stack = 0;
uint64_t *terminal_stack = (uint64_t *)terminal->user_stack;
*(--terminal_stack) = 0;
terminal->user_rsp = (void *)(USER_VIRTUAL_STACK_TOP - ((uint64_t)terminal->user_stack - (uint64_t)terminal_stack));
terminal->user_rsp = (void *)(USER_VIRTUAL_STACK_TOP - ((uint64_t)terminal->user_stack - (uint64_t)stack));
uint64_t *shell_stack = (uint64_t *)shell->user_stack;
*(--shell_stack) = 0;
shell->user_rsp = (void *)(USER_VIRTUAL_STACK_TOP - ((uint64_t)shell->user_stack - (uint64_t)shell_stack));
current_process = terminal;
tss.rsp0 = (uint64_t)current_process->kernel_stack;
process_switch_to(&kernel->kernel_rsp, terminal->kernel_rsp, VIRT_TO_PHYS(terminal->pml4));
pipe_init(&(terminal->fds[terminal->free_fd++]), &(shell->fds[0]));
fs_unmount((fs_node_t *)kernel->root);
timer_init();
outw(0x604, 0x2000); // TODO: parse ACPI tables for real hardware
__asm__ volatile("cli; hlt");
__asm__ volatile("sti; hlt");
}
+126
View File
@@ -0,0 +1,126 @@
#include "src/kernel/pipe.h"
#include "src/kernel/stream.h"
#include "src/lib/memory.h"
#define PIPE_BUFFER_SIZE 65536
typedef struct {
char buffer[PIPE_BUFFER_SIZE];
uint64_t begin;
uint64_t end;
uint8_t write_closed;
uint8_t read_closed;
} pipe_t;
typedef struct {
stream_t stream;
pipe_t *pipe;
} pipe_read_stream_t;
typedef struct {
stream_t stream;
pipe_t *pipe;
} pipe_write_stream_t;
static uint64_t null_read(__attribute__((unused)) const stream_t *self, __attribute__((unused)) uint64_t max,
__attribute__((unused)) char *to) {
return 0;
}
static uint64_t null_write(__attribute__((unused)) const stream_t *self, __attribute__((unused)) const char *from,
__attribute__((unused)) uint64_t bytes) {
return 0;
}
static uint64_t pipe_read(const stream_t *self, uint64_t max, char *to) {
pipe_t *pipe = ((pipe_read_stream_t *)self)->pipe;
uint64_t available = (pipe->end + PIPE_BUFFER_SIZE - pipe->begin) % PIPE_BUFFER_SIZE;
uint64_t to_read = available < max ? available : max;
if (pipe->write_closed && !to_read) {
return (uint64_t)-1;
}
if (!to_read) {
return 0;
}
if (pipe->begin + to_read <= PIPE_BUFFER_SIZE) {
memory_copy(pipe->buffer + pipe->begin, to_read, to);
} else {
uint64_t chunk = PIPE_BUFFER_SIZE - pipe->begin;
memory_copy(pipe->buffer + pipe->begin, chunk, to);
memory_copy(pipe->buffer, to_read - chunk, to + chunk);
}
pipe->begin = (pipe->begin + to_read) % PIPE_BUFFER_SIZE;
return to_read;
}
static uint64_t pipe_write(const stream_t *self, const char *from, uint64_t bytes) {
pipe_t *pipe = ((pipe_read_stream_t *)self)->pipe;
if (pipe->read_closed) {
return (uint64_t)-1;
}
uint64_t available = PIPE_BUFFER_SIZE - (pipe->end + PIPE_BUFFER_SIZE - pipe->begin) % PIPE_BUFFER_SIZE;
uint64_t to_write = available < bytes ? available : bytes;
if (to_write == 0) {
return 0;
}
if (pipe->end + to_write <= PIPE_BUFFER_SIZE) {
memory_copy(from, to_write, pipe->buffer + pipe->end);
} else {
uint64_t chunk = PIPE_BUFFER_SIZE - pipe->end;
memory_copy(from, chunk, pipe->buffer + pipe->end);
memory_copy(from + chunk, to_write - chunk, pipe->buffer);
}
pipe->end = (pipe->end + to_write) % PIPE_BUFFER_SIZE;
return to_write;
}
static void read_close(stream_t *self) {
pipe_t *pipe = ((pipe_read_stream_t *)self)->pipe;
pipe->read_closed = 1;
if (pipe->read_closed && pipe->write_closed) {
memory_free(pipe);
}
memory_free(self);
}
static void write_close(stream_t *self) {
pipe_t *pipe = ((pipe_write_stream_t *)self)->pipe;
pipe->write_closed = 1;
if (pipe->read_closed && pipe->write_closed) {
memory_free(pipe);
}
memory_free(self);
}
void pipe_init(stream_t **write, stream_t **read) {
pipe_t *pipe = memory_allocate(sizeof(pipe_t));
pipe_write_stream_t *ws = memory_allocate(sizeof(pipe_write_stream_t));
ws->stream.read = null_read;
ws->stream.write = pipe_write;
ws->stream.close = write_close;
ws->pipe = pipe;
pipe_read_stream_t *rs = memory_allocate(sizeof(pipe_read_stream_t));
rs->stream.read = pipe_read;
rs->stream.write = null_write;
rs->stream.close = read_close;
rs->pipe = pipe;
*write = (stream_t *)ws;
*read = (stream_t *)rs;
}
+5
View File
@@ -0,0 +1,5 @@
#pragma once
#include "src/kernel/stream.h"
void pipe_init(stream_t **write, stream_t **read);
+73 -3
View File
@@ -1,17 +1,35 @@
#include "src/kernel/process.h"
#include "src/kernel/memory.h"
#include "src/kernel/panic.h"
#include "src/kernel/path.h"
#include "src/kernel/tss.h"
#include "src/kernel/util.h"
#include "src/lib/layout.h"
#include "src/lib/memory.h"
#include "src/lib/util.h"
#define MAX_PROCESSES 256
static process_t *processes[MAX_PROCESSES];
static uint64_t free_pid = 1;
process_t *current_process;
process_t *process_create(const process_t *parent, const uint8_t *code, uint64_t size) {
ASSERT(size <= USER_VIRTUAL_HEAP - USER_VIRTUAL_CODE, "process_create: binary too big");
if (size >= USER_VIRTUAL_HEAP - USER_VIRTUAL_CODE) {
return NUL;
}
process_t *proc = memory_allocate(sizeof(process_t));
uint64_t free_pi;
for (free_pi = 0; free_pi <= MAX_PROCESSES; free_pi++) {
if (!processes[free_pi]) {
break;
}
}
if (free_pi == MAX_PROCESSES) {
return NUL;
}
process_t *proc = processes[free_pi] = memory_allocate(sizeof(process_t));
proc->parent = parent;
@@ -56,6 +74,8 @@ process_t *process_create(const process_t *parent, const uint8_t *code, uint64_t
cwd->stack[i] = fs_open_again(parent->cwd->stack[i]);
}
proc->pid = free_pid++;
proc->state = PROCESS_RUNNING;
proc->cwd = cwd;
proc->root = parent->root;
proc->fds[0] = parent->fds[0];
@@ -66,6 +86,49 @@ process_t *process_create(const process_t *parent, const uint8_t *code, uint64_t
return proc;
}
process_t *process_get(uint64_t pid) {
for (uint64_t i = 0; i < MAX_PROCESSES; i++) {
if (processes[i] && processes[i]->pid == pid) {
return processes[i];
}
}
return NUL;
}
void process_next() {
process_t *next = NUL;
uint64_t cpi = 0;
for (uint64_t i = 0; i < MAX_PROCESSES; i++) {
if (processes[i] == current_process) {
cpi = i;
break;
}
}
for (uint64_t i = 1; i <= MAX_PROCESSES; i++) {
process_t *candidate = processes[(cpi + i) % MAX_PROCESSES];
if (candidate && candidate->state == PROCESS_RUNNING) {
next = candidate;
break;
}
}
if (!next) {
outw(0x604, 0x2000);
__asm__ volatile("cli; hlt");
}
if (next == current_process) {
return;
}
process_t *prev = current_process;
tss.rsp0 = (uint64_t)next->kernel_stack;
current_process = next;
process_switch_to(&prev->kernel_rsp, next->kernel_rsp, VIRT_TO_PHYS(next->pml4));
}
void process_destroy(process_t *proc) {
for (uint64_t i = 2; i < MAX_FDS; i++) {
if (proc->fds[i]) {
@@ -117,5 +180,12 @@ void process_destroy(process_t *proc) {
memory_free(proc->kernel_stack - PAGE_SIZE);
for (uint64_t i = 0; i < MAX_PROCESSES; i++) {
if (processes[i] == proc) {
processes[i] = NUL;
break;
}
}
memory_free(proc);
}
+13
View File
@@ -9,6 +9,12 @@
#define MAX_FDS 16
typedef enum {
PROCESS_RUNNING,
PROCESS_WAITING,
PROCESS_ZOMBIE,
} process_state_t;
typedef struct process {
const struct process *parent;
uint64_t *pml4;
@@ -16,6 +22,9 @@ typedef struct process {
void *kernel_rsp;
void *user_stack;
void *user_rsp;
uint64_t pid;
process_state_t state;
uint64_t waiting_for;
const fs_node_t *root;
const path_t *cwd;
stream_t *fds[MAX_FDS];
@@ -31,6 +40,10 @@ extern void process_trampoline();
process_t *process_create(const process_t *parent, const uint8_t *code, uint64_t size);
process_t *process_get(uint64_t pid);
extern void process_switch_to(void **save_rsp, void *new_rsp, void *new_pml4_phys);
void process_next();
void process_destroy(process_t *proc);
+29 -8
View File
@@ -94,7 +94,7 @@ static uint64_t chdir(const char *path) {
return current_process->cwd->depth;
}
static exit_code_t spawn(const char *path, uint64_t argc, const char **argv) {
static uint64_t spawn(const char *path, uint64_t argc, const char **argv) {
fs_node_t *node = path_open_file(current_process->root, current_process->cwd, path);
if (!node) {
return EXIT_CODE_NOT_FOUND;
@@ -138,15 +138,25 @@ static exit_code_t spawn(const char *path, uint64_t argc, const char **argv) {
child->user_rsp = (void *)(USER_VIRTUAL_STACK_TOP - ((uint64_t)child->user_stack - (uint64_t)stack));
process_t *parent = current_process;
current_process = child;
tss.rsp0 = (uint64_t)child->kernel_stack;
__asm__ volatile("mov %0, %%cr3" : : "r"((uint64_t)VIRT_TO_PHYS((void *)current_process->pml4)) : "memory");
process_switch_to(&parent->kernel_rsp, child->kernel_rsp, VIRT_TO_PHYS(child->pml4));
return child->pid;
}
static exit_code_t wait(uint64_t pid) {
process_t *child = process_get(pid);
if (!child) {
return EXIT_CODE_GENERAL_FAILURE;
}
current_process->state = PROCESS_WAITING;
current_process->waiting_for = pid;
while (child->state != PROCESS_ZOMBIE) {
process_next();
}
current_process->state = PROCESS_RUNNING;
exit_code_t code = child->code;
tss.rsp0 = (uint64_t)parent->kernel_stack;
current_process = parent;
process_destroy(child);
return code;
@@ -174,8 +184,17 @@ static uint64_t close(uint64_t fd) {
}
static void exit(exit_code_t code) {
current_process->state = PROCESS_ZOMBIE;
current_process->code = code;
process_switch_to(&current_process->kernel_rsp, current_process->parent->kernel_rsp, VIRT_TO_PHYS(current_process->parent->pml4));
if (current_process->parent) {
process_t *parent = (process_t *)current_process->parent;
if (parent->state == PROCESS_WAITING && parent->waiting_for == current_process->pid) {
parent->state = PROCESS_RUNNING;
}
}
process_next();
}
uint64_t syscall_dispatch(uint64_t func, uint64_t arg1, uint64_t arg2, uint64_t arg3, __attribute__((unused)) uint64_t arg4) {
@@ -190,6 +209,8 @@ uint64_t syscall_dispatch(uint64_t func, uint64_t arg1, uint64_t arg2, uint64_t
return chdir((const char *)arg1);
case SYSCALL_SPAWN:
return spawn((const char *)arg1, arg2, (const char **)arg3);
case SYSCALL_WAIT:
return wait(arg1);
case SYSCALL_OPEN:
return open((const char *)arg1);
case SYSCALL_CLOSE:
+1
View File
@@ -7,6 +7,7 @@
#define SYSCALL_GETCWD 2
#define SYSCALL_CHDIR 3
#define SYSCALL_SPAWN 4
#define SYSCALL_WAIT 5
#define SYSCALL_OPEN 6
#define SYSCALL_CLOSE 7
#define SYSCALL_EXIT 60
+28
View File
@@ -0,0 +1,28 @@
bits 64
extern timer_handler
global timer_entry
timer_entry:
push rax
push rcx
push rdx
push rsi
push rdi
push r8
push r9
push r10
push r11
call timer_handler
pop r11
pop r10
pop r9
pop r8
pop rdi
pop rsi
pop rdx
pop rcx
pop rax
iretq
+20
View File
@@ -0,0 +1,20 @@
#include "src/kernel/timer.h"
#include "src/kernel/idt.h"
#include "src/kernel/process.h"
#include "src/kernel/util.h"
extern void timer_entry();
void timer_init() {
// PIT channel 0, rate generator, ~100Hz
outb(0x43, 0x36);
outb(0x40, 0xA9); // divisor low byte (11932 for ~100Hz)
outb(0x40, 0x2E); // divisor high byte
outb(0x21, inb(0x21) & ~0x01); // unmask IRQ0
idt_set_entry(32, timer_entry, 0x8E);
}
void timer_handler() {
outb(0x20, 0x20);
process_next();
}
+3
View File
@@ -0,0 +1,3 @@
#pragma once
void timer_init();