forked from Bananymous/banan-os
Kernel: Add bareboness fork() function
This commit is contained in:
parent
c19f4c019a
commit
6fdbe6f9c2
|
@ -48,6 +48,7 @@ set(KERNEL_SOURCES
|
||||||
kernel/Storage/ATADevice.cpp
|
kernel/Storage/ATADevice.cpp
|
||||||
kernel/Storage/StorageDevice.cpp
|
kernel/Storage/StorageDevice.cpp
|
||||||
kernel/Syscall.cpp
|
kernel/Syscall.cpp
|
||||||
|
kernel/Syscall.S
|
||||||
kernel/Thread.cpp
|
kernel/Thread.cpp
|
||||||
kernel/Terminal/TTY.cpp
|
kernel/Terminal/TTY.cpp
|
||||||
kernel/Terminal/VesaTerminalDriver.cpp
|
kernel/Terminal/VesaTerminalDriver.cpp
|
||||||
|
|
|
@ -17,4 +17,8 @@
|
||||||
#define read_rsp(rsp) asm volatile("movl %%esp, %0" : "=r"(rsp))
|
#define read_rsp(rsp) asm volatile("movl %%esp, %0" : "=r"(rsp))
|
||||||
#define push_callee_saved() asm volatile("pushal")
|
#define push_callee_saved() asm volatile("pushal")
|
||||||
#define pop_callee_saved() asm volatile("popal")
|
#define pop_callee_saved() asm volatile("popal")
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
extern "C" uintptr_t read_rip();
|
||||||
|
|
|
@ -41,6 +41,8 @@ namespace Kernel
|
||||||
|
|
||||||
pid_t pid() const { return m_pid; }
|
pid_t pid() const { return m_pid; }
|
||||||
|
|
||||||
|
BAN::ErrorOr<Process*> fork(uintptr_t rsp, uintptr_t rip);
|
||||||
|
|
||||||
BAN::ErrorOr<int> open(BAN::StringView, int);
|
BAN::ErrorOr<int> open(BAN::StringView, int);
|
||||||
BAN::ErrorOr<void> close(int fd);
|
BAN::ErrorOr<void> close(int fd);
|
||||||
BAN::ErrorOr<size_t> read(int fd, void* buffer, size_t count);
|
BAN::ErrorOr<size_t> read(int fd, void* buffer, size_t count);
|
||||||
|
|
|
@ -31,6 +31,8 @@ namespace Kernel
|
||||||
static BAN::ErrorOr<Thread*> create_userspace(uintptr_t, Process*, int, char**);
|
static BAN::ErrorOr<Thread*> create_userspace(uintptr_t, Process*, int, char**);
|
||||||
~Thread();
|
~Thread();
|
||||||
|
|
||||||
|
BAN::ErrorOr<Thread*> clone(Process*, uintptr_t rsp, uintptr_t rip);
|
||||||
|
|
||||||
pid_t tid() const { return m_tid; }
|
pid_t tid() const { return m_tid; }
|
||||||
|
|
||||||
void set_rsp(uintptr_t rsp) { m_rsp = rsp; validate_stack(); }
|
void set_rsp(uintptr_t rsp) { m_rsp = rsp; validate_stack(); }
|
||||||
|
|
|
@ -83,9 +83,11 @@ namespace Kernel
|
||||||
for (size_t i = 0; i < result->m_physical_pages.size(); i++)
|
for (size_t i = 0; i < result->m_physical_pages.size(); i++)
|
||||||
{
|
{
|
||||||
m_mmu.map_page_at(result->m_physical_pages[i], 0, MMU::Flags::ReadWrite | MMU::Flags::Present);
|
m_mmu.map_page_at(result->m_physical_pages[i], 0, MMU::Flags::ReadWrite | MMU::Flags::Present);
|
||||||
|
m_mmu.invalidate(0);
|
||||||
memcpy((void*)0, (void*)(vaddr() + i * PAGE_SIZE), PAGE_SIZE);
|
memcpy((void*)0, (void*)(vaddr() + i * PAGE_SIZE), PAGE_SIZE);
|
||||||
}
|
}
|
||||||
m_mmu.unmap_page(0);
|
m_mmu.unmap_page(0);
|
||||||
|
m_mmu.invalidate(0);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,6 +129,8 @@ namespace Kernel
|
||||||
MMU::kernel().load();
|
MMU::kernel().load();
|
||||||
delete m_mmu;
|
delete m_mmu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dprintln("process {} exit", pid());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Process::add_thread(Thread* thread)
|
void Process::add_thread(Thread* thread)
|
||||||
|
@ -167,8 +169,6 @@ namespace Kernel
|
||||||
m_general_allocator = nullptr;
|
m_general_allocator = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
dprintln("process {} exit", pid());
|
|
||||||
|
|
||||||
s_process_lock.lock();
|
s_process_lock.lock();
|
||||||
for (size_t i = 0; i < s_processes.size(); i++)
|
for (size_t i = 0; i < s_processes.size(); i++)
|
||||||
if (s_processes[i] == this)
|
if (s_processes[i] == this)
|
||||||
|
@ -197,6 +197,40 @@ namespace Kernel
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<Process*> Process::fork(uintptr_t rsp, uintptr_t rip)
|
||||||
|
{
|
||||||
|
LockGuard _(m_lock);
|
||||||
|
|
||||||
|
Process* forked = create_process();
|
||||||
|
|
||||||
|
forked->m_tty = m_tty;
|
||||||
|
forked->m_working_directory = m_working_directory;
|
||||||
|
|
||||||
|
forked->m_open_files = m_open_files;
|
||||||
|
|
||||||
|
forked->m_mmu = new MMU();
|
||||||
|
ASSERT(forked->m_mmu);
|
||||||
|
|
||||||
|
for (auto* mapped_range : m_mapped_ranges)
|
||||||
|
MUST(forked->m_mapped_ranges.push_back(mapped_range->clone(forked->mmu())));
|
||||||
|
|
||||||
|
ASSERT(m_threads.size() == 1);
|
||||||
|
ASSERT(m_threads.front() == &Thread::current());
|
||||||
|
|
||||||
|
//for (auto& allocator : m_fixed_width_allocators)
|
||||||
|
// MUST(forked->m_fixed_width_allocators.push_back(allocator.clone()));
|
||||||
|
|
||||||
|
//if (m_general_allocator)
|
||||||
|
// forked->m_general_allocator = m_general_allocator->clone();
|
||||||
|
|
||||||
|
Thread* thread = MUST(m_threads.front()->clone(forked, rsp, rip));
|
||||||
|
forked->add_thread(thread);
|
||||||
|
|
||||||
|
register_process(forked);
|
||||||
|
|
||||||
|
return forked;
|
||||||
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<int> Process::open(BAN::StringView path, int flags)
|
BAN::ErrorOr<int> Process::open(BAN::StringView path, int flags)
|
||||||
{
|
{
|
||||||
if (flags & ~O_RDWR)
|
if (flags & ~O_RDWR)
|
||||||
|
|
|
@ -19,7 +19,6 @@ namespace Kernel
|
||||||
|
|
||||||
extern "C" [[noreturn]] void start_thread(uintptr_t rsp, uintptr_t rip);
|
extern "C" [[noreturn]] void start_thread(uintptr_t rsp, uintptr_t rip);
|
||||||
extern "C" [[noreturn]] void continue_thread(uintptr_t rsp, uintptr_t rip);
|
extern "C" [[noreturn]] void continue_thread(uintptr_t rsp, uintptr_t rip);
|
||||||
extern "C" uintptr_t read_rip();
|
|
||||||
|
|
||||||
static Scheduler* s_instance = nullptr;
|
static Scheduler* s_instance = nullptr;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
.global sys_fork_trampoline
|
||||||
|
sys_fork_trampoline:
|
||||||
|
pushq %rbx
|
||||||
|
pushq %rbp
|
||||||
|
pushq %r12
|
||||||
|
pushq %r13
|
||||||
|
pushq %r14
|
||||||
|
pushq %r15
|
||||||
|
call read_rip
|
||||||
|
testq %rax, %rax
|
||||||
|
je .done
|
||||||
|
movq %rax, %rsi
|
||||||
|
movq %rsp, %rdi
|
||||||
|
call sys_fork
|
||||||
|
.done:
|
||||||
|
popq %r15
|
||||||
|
popq %r14
|
||||||
|
popq %r13
|
||||||
|
popq %r12
|
||||||
|
popq %rbp
|
||||||
|
popq %rbx
|
||||||
|
ret
|
|
@ -98,6 +98,16 @@ namespace Kernel
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" long sys_fork(uintptr_t rsp, uintptr_t rip)
|
||||||
|
{
|
||||||
|
auto ret = Process::current().fork(rsp, rip);
|
||||||
|
if (ret.is_error())
|
||||||
|
return -ret.error().get_error_code();
|
||||||
|
return ret.value()->pid();
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" long sys_fork_trampoline();
|
||||||
|
|
||||||
extern "C" long cpp_syscall_handler(int syscall, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, uintptr_t arg5)
|
extern "C" long cpp_syscall_handler(int syscall, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, uintptr_t arg5)
|
||||||
{
|
{
|
||||||
Thread::current().set_in_syscall(true);
|
Thread::current().set_in_syscall(true);
|
||||||
|
@ -149,6 +159,9 @@ namespace Kernel
|
||||||
case SYS_SET_TERMIOS:
|
case SYS_SET_TERMIOS:
|
||||||
ret = sys_set_termios((const ::termios*)arg1);
|
ret = sys_set_termios((const ::termios*)arg1);
|
||||||
break;
|
break;
|
||||||
|
case SYS_FORK:
|
||||||
|
ret = sys_fork_trampoline();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
Kernel::panic("Unknown syscall {}", syscall);
|
Kernel::panic("Unknown syscall {}", syscall);
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,13 +111,38 @@ namespace Kernel
|
||||||
|
|
||||||
Thread::~Thread()
|
Thread::~Thread()
|
||||||
{
|
{
|
||||||
dprintln("thread {} ({}) exit", tid(), m_process->pid());
|
|
||||||
if (m_stack)
|
if (m_stack)
|
||||||
delete m_stack;
|
delete m_stack;
|
||||||
m_stack = nullptr;
|
m_stack = nullptr;
|
||||||
if (m_interrupt_stack)
|
if (m_interrupt_stack)
|
||||||
delete m_interrupt_stack;
|
delete m_interrupt_stack;
|
||||||
m_interrupt_stack = nullptr;
|
m_interrupt_stack = nullptr;
|
||||||
|
|
||||||
|
dprintln("thread {} ({}) exit", tid(), m_process->pid());
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<Thread*> Thread::clone(Process* new_process, uintptr_t rsp, uintptr_t rip)
|
||||||
|
{
|
||||||
|
ASSERT(m_is_userspace);
|
||||||
|
ASSERT(m_state == State::Executing);
|
||||||
|
|
||||||
|
Thread* thread = new Thread(s_next_tid++, new_process);
|
||||||
|
if (thread == nullptr)
|
||||||
|
return BAN::Error::from_errno(ENOMEM);
|
||||||
|
thread->m_is_userspace = true;
|
||||||
|
|
||||||
|
thread->m_interrupt_stack = m_interrupt_stack->clone(new_process->mmu());
|
||||||
|
thread->m_stack = m_stack->clone(new_process->mmu());
|
||||||
|
|
||||||
|
thread->m_state = State::Executing;
|
||||||
|
thread->m_in_syscall = true;
|
||||||
|
|
||||||
|
thread->m_rip = rip;
|
||||||
|
thread->m_rsp = rsp;
|
||||||
|
|
||||||
|
thread->m_userspace_entry = {};
|
||||||
|
|
||||||
|
return thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Thread::validate_stack() const
|
void Thread::validate_stack() const
|
||||||
|
|
|
@ -126,7 +126,7 @@ extern "C" void kernel_main()
|
||||||
dprintln("GDT initialized");
|
dprintln("GDT initialized");
|
||||||
|
|
||||||
IDT::initialize();
|
IDT::initialize();
|
||||||
dprintln("IDT initialized");
|
dprintln("IDT initialized");
|
||||||
|
|
||||||
MMU::initialize();
|
MMU::initialize();
|
||||||
dprintln("MMU initialized");
|
dprintln("MMU initialized");
|
||||||
|
@ -180,7 +180,7 @@ static void init2(void* tty1)
|
||||||
|
|
||||||
((TTY*)tty1)->initialize_device();
|
((TTY*)tty1)->initialize_device();
|
||||||
|
|
||||||
MUST(Process::create_userspace("/usr/bin/Shell"sv));
|
MUST(Process::create_userspace("/usr/bin/test"sv));
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Process::create_kernel(
|
Process::create_kernel(
|
||||||
|
|
|
@ -17,6 +17,7 @@ __BEGIN_DECLS
|
||||||
#define SYS_TELL 10
|
#define SYS_TELL 10
|
||||||
#define SYS_GET_TERMIOS 11
|
#define SYS_GET_TERMIOS 11
|
||||||
#define SYS_SET_TERMIOS 12
|
#define SYS_SET_TERMIOS 12
|
||||||
|
#define SYS_FORK 13
|
||||||
|
|
||||||
__END_DECLS
|
__END_DECLS
|
||||||
|
|
||||||
|
|
|
@ -103,6 +103,11 @@ long syscall(long syscall, ...)
|
||||||
ret = Kernel::syscall(SYS_SET_TERMIOS, (uintptr_t)termios);
|
ret = Kernel::syscall(SYS_SET_TERMIOS, (uintptr_t)termios);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case SYS_FORK:
|
||||||
|
{
|
||||||
|
ret = Kernel::syscall(SYS_FORK);
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
puts("LibC: Unhandeled syscall");
|
puts("LibC: Unhandeled syscall");
|
||||||
ret = -ENOSYS;
|
ret = -ENOSYS;
|
||||||
|
@ -122,25 +127,5 @@ long syscall(long syscall, ...)
|
||||||
|
|
||||||
pid_t fork(void)
|
pid_t fork(void)
|
||||||
{
|
{
|
||||||
return -1;
|
return syscall(SYS_FORK);
|
||||||
}
|
|
||||||
|
|
||||||
int execv(const char*, char* const[])
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int execve(const char*, char* const[], char* const[])
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int execvp(const char*, char* const[])
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pid_t getpid(void)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,31 +1,22 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#define ERROR(msg) { perror(msg); return 1; }
|
#define ERROR(msg) { perror(msg); return 1; }
|
||||||
#define BUF_SIZE 1024
|
#define BUF_SIZE 1024
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
FILE* fp = fopen("/usr/include/stdio.h", "r");
|
printf("forking\n");
|
||||||
if (fp == NULL)
|
|
||||||
ERROR("fopen");
|
|
||||||
|
|
||||||
char* buffer = (char*)malloc(BUF_SIZE);
|
pid_t pid = fork();
|
||||||
if (buffer == NULL)
|
if (pid == 0)
|
||||||
ERROR("malloc");
|
|
||||||
|
|
||||||
for (;;)
|
|
||||||
{
|
{
|
||||||
size_t n_read = fread(buffer, 1, BUF_SIZE - 1, fp);
|
printf("child\n");
|
||||||
if (n_read == 0)
|
return 0;
|
||||||
break;
|
|
||||||
buffer[n_read] = '\0';
|
|
||||||
printf("%s", buffer);
|
|
||||||
}
|
}
|
||||||
|
printf("parent\n");
|
||||||
free(buffer);
|
|
||||||
fclose(fp);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue