mirror of
https://github.com/wichtounet/thor-os.git
synced 2025-09-09 12:31:06 -04:00
Move thread creation implementation into scheduler
This commit is contained in:
parent
3d68d1eb50
commit
d3a20db821
@ -8,6 +8,8 @@
|
||||
#ifndef SCHEDULER_H
|
||||
#define SCHEDULER_H
|
||||
|
||||
#include "stl/string.hpp"
|
||||
|
||||
#include "process.hpp"
|
||||
|
||||
namespace scheduler {
|
||||
@ -21,16 +23,14 @@ void block_process(pid_t pid);
|
||||
void unblock_process(pid_t pid);
|
||||
|
||||
void init();
|
||||
void start();
|
||||
|
||||
int64_t exec(const std::string& path);
|
||||
|
||||
void kill_current_process();
|
||||
|
||||
void tick();
|
||||
void reschedule();
|
||||
|
||||
process_t& new_process();
|
||||
void queue_process(pid_t p);
|
||||
|
||||
void sleep_ms(pid_t pid, size_t time);
|
||||
|
||||
} //end of namespace scheduler
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "console.hpp"
|
||||
#include "gdt.hpp"
|
||||
#include "terminal.hpp"
|
||||
#include "scheduler.hpp"
|
||||
|
||||
extern "C" {
|
||||
|
||||
@ -67,6 +68,8 @@ void kernel_main(){
|
||||
|
||||
init_console();
|
||||
|
||||
scheduler::init();
|
||||
|
||||
//Launch the shell
|
||||
init_shell();
|
||||
|
||||
|
@ -7,21 +7,26 @@
|
||||
|
||||
#include "stl/array.hpp"
|
||||
#include "stl/vector.hpp"
|
||||
#include "stl/optional.hpp"
|
||||
#include "stl/string.hpp"
|
||||
|
||||
#include "scheduler.hpp"
|
||||
#include "paging.hpp"
|
||||
#include "assert.hpp"
|
||||
#include "gdt.hpp"
|
||||
#include "terminal.hpp"
|
||||
|
||||
#include "disks.hpp"
|
||||
#include "elf.hpp"
|
||||
#include "console.hpp"
|
||||
#include "physical_allocator.hpp"
|
||||
#include "virtual_allocator.hpp"
|
||||
|
||||
constexpr const bool DEBUG_SCHEDULER = true;
|
||||
constexpr const bool DEBUG_SCHEDULER = false;
|
||||
|
||||
//Provided by task_switch.s
|
||||
extern "C" {
|
||||
extern void task_switch(size_t current, size_t next);
|
||||
extern void init_task_switch(size_t current);
|
||||
extern void init_task_switch(size_t current) __attribute__((noreturn));
|
||||
}
|
||||
|
||||
namespace {
|
||||
@ -58,8 +63,36 @@ void idle_task(){
|
||||
char idle_stack[scheduler::user_stack_size];
|
||||
char idle_kernel_stack[scheduler::kernel_stack_size];
|
||||
|
||||
scheduler::process_t& new_process(){
|
||||
//TODO use get_free_pid() that searchs through the PCB
|
||||
auto pid = next_pid++;
|
||||
|
||||
auto& process = pcb[pid];
|
||||
|
||||
process.process.system = false;
|
||||
process.process.pid = pid;
|
||||
process.process.priority = scheduler::DEFAULT_PRIORITY;
|
||||
process.state = scheduler::process_state::NEW;
|
||||
process.process.tty = stdio::get_active_terminal().id;
|
||||
|
||||
return process.process;
|
||||
}
|
||||
|
||||
void queue_process(scheduler::pid_t pid){
|
||||
thor_assert(pid < scheduler::MAX_PROCESS, "pid out of bounds");
|
||||
|
||||
auto& process = pcb[pid];
|
||||
|
||||
thor_assert(process.process.priority <= scheduler::MAX_PRIORITY, "Invalid priority");
|
||||
thor_assert(process.process.priority >= scheduler::MIN_PRIORITY, "Invalid priority");
|
||||
|
||||
process.state = scheduler::process_state::READY;
|
||||
|
||||
run_queues[process.process.priority].push_back(pid);
|
||||
}
|
||||
|
||||
void create_idle_task(){
|
||||
auto& idle_process = scheduler::new_process();
|
||||
auto& idle_process = new_process();
|
||||
|
||||
idle_process.priority = scheduler::MIN_PRIORITY;
|
||||
|
||||
@ -83,7 +116,7 @@ void create_idle_task(){
|
||||
|
||||
idle_process.kernel_rsp = reinterpret_cast<size_t>(&idle_kernel_stack[scheduler::kernel_stack_size - 1]);
|
||||
|
||||
scheduler::queue_process(idle_process.pid);
|
||||
queue_process(idle_process.pid);
|
||||
}
|
||||
|
||||
void switch_to_process(size_t pid){
|
||||
@ -152,23 +185,209 @@ size_t select_next_process(){
|
||||
thor_unreachable("No process is READY");
|
||||
}
|
||||
|
||||
} //end of anonymous namespace
|
||||
void scheduler::init(){ //Create the idle task
|
||||
create_idle_task();
|
||||
std::optional<std::string> read_elf_file(const std::string& file){
|
||||
auto content = disks::read_file(file);
|
||||
|
||||
if(content.empty()){
|
||||
k_print_line("The file does not exist or is empty");
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
if(!elf::is_valid(content)){
|
||||
k_print_line("This file is not an ELF file or not in ELF64 format");
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
return {std::move(content)};
|
||||
}
|
||||
|
||||
void scheduler::start(){
|
||||
bool allocate_user_memory(scheduler::process_t& process, size_t address, size_t size, size_t& ref){
|
||||
//1. Calculate some stuff
|
||||
auto first_page = paging::page_align(address);
|
||||
auto left_padding = address - first_page;
|
||||
auto bytes = left_padding + paging::PAGE_SIZE + size;
|
||||
auto pages = (bytes / paging::PAGE_SIZE) + 1;
|
||||
|
||||
//2. Get enough physical memory
|
||||
auto physical_memory = physical_allocator::allocate(pages);
|
||||
|
||||
if(!physical_memory){
|
||||
k_print_line("Cannot allocate physical memory, probably out of memory");
|
||||
return false;
|
||||
}
|
||||
|
||||
//3. Find a start of a page inside the physical memory
|
||||
|
||||
auto aligned_physical_memory = paging::page_aligned(physical_memory) ? physical_memory :
|
||||
(physical_memory / paging::PAGE_SIZE + 1) * paging::PAGE_SIZE;
|
||||
|
||||
//4. Map physical allocated memory to the necessary virtual memory
|
||||
|
||||
paging::user_map_pages(process, first_page, aligned_physical_memory, pages);
|
||||
|
||||
ref = physical_memory;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void clear_physical_memory(size_t memory, size_t pages){
|
||||
auto virt = virtual_allocator::allocate(pages);
|
||||
|
||||
paging::map_pages(virt, memory, pages);
|
||||
|
||||
auto it = reinterpret_cast<uint64_t*>(virt);
|
||||
std::fill_n(it, (pages * paging::PAGE_SIZE) / sizeof(uint64_t), 0);
|
||||
|
||||
paging::unmap_pages(virt, pages);
|
||||
|
||||
//TODO virt should be deallocated
|
||||
}
|
||||
|
||||
bool create_paging(char* buffer, scheduler::process_t& process){
|
||||
//1. Prepare PML4T
|
||||
|
||||
//Get memory for cr3
|
||||
process.physical_cr3 = physical_allocator::allocate(1);
|
||||
process.paging_size = paging::PAGE_SIZE;
|
||||
|
||||
clear_physical_memory(process.physical_cr3, 1);
|
||||
|
||||
//Map the kernel pages inside the user memory space
|
||||
paging::map_kernel_inside_user(process);
|
||||
|
||||
//2. Create all the other necessary structures
|
||||
|
||||
//2.1 Allocate user stack
|
||||
allocate_user_memory(process, scheduler::user_stack_start, scheduler::user_stack_size, process.physical_user_stack);
|
||||
|
||||
//2.2 Allocate all user segments
|
||||
|
||||
auto header = reinterpret_cast<elf::elf_header*>(buffer);
|
||||
auto program_header_table = reinterpret_cast<elf::program_header*>(buffer + header->e_phoff);
|
||||
|
||||
for(size_t p = 0; p < header->e_phnum; ++p){
|
||||
auto& p_header = program_header_table[p];
|
||||
|
||||
if(p_header.p_type == 1){
|
||||
auto address = p_header.p_vaddr;
|
||||
auto first_page = paging::page_align(address);
|
||||
auto left_padding = address - first_page;
|
||||
auto bytes = left_padding + paging::PAGE_SIZE + p_header.p_memsz;
|
||||
auto pages = (bytes / paging::PAGE_SIZE) + 1;
|
||||
|
||||
scheduler::segment_t segment;
|
||||
segment.size = pages * paging::PAGE_SIZE;
|
||||
|
||||
allocate_user_memory(process, first_page, pages, segment.physical);
|
||||
|
||||
process.segments.push_back(segment);
|
||||
|
||||
//Copy the code into memory
|
||||
|
||||
auto virtual_memory = virtual_allocator::allocate(pages);
|
||||
|
||||
paging::map_pages(virtual_memory, segment.physical, pages);
|
||||
|
||||
auto memory_start = virtual_memory + left_padding;
|
||||
|
||||
std::copy_n(reinterpret_cast<char*>(memory_start), buffer + p_header.p_offset, p_header.p_memsz);
|
||||
|
||||
paging::unmap_pages(virtual_memory, pages);
|
||||
}
|
||||
}
|
||||
|
||||
//2.3 Allocate kernel stack
|
||||
auto virtual_kernel_stack = virtual_allocator::allocate(scheduler::kernel_stack_size / paging::PAGE_SIZE);
|
||||
auto physical_kernel_stack = physical_allocator::allocate(scheduler::kernel_stack_size / paging::PAGE_SIZE);
|
||||
|
||||
if(!paging::map_pages(virtual_kernel_stack, physical_kernel_stack, scheduler::kernel_stack_size / paging::PAGE_SIZE)){
|
||||
return false;
|
||||
}
|
||||
|
||||
process.physical_kernel_stack = physical_kernel_stack;
|
||||
process.kernel_rsp = virtual_kernel_stack + (scheduler::user_stack_size - 8);
|
||||
|
||||
//3. Clear stacks
|
||||
clear_physical_memory(process.physical_user_stack, scheduler::user_stack_size / paging::PAGE_SIZE);
|
||||
clear_physical_memory(process.physical_kernel_stack, scheduler::kernel_stack_size / paging::PAGE_SIZE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void init_context(scheduler::process_t& process, const char* buffer){
|
||||
auto header = reinterpret_cast<const elf::elf_header*>(buffer);
|
||||
|
||||
auto pages = scheduler::user_stack_size / paging::PAGE_SIZE;
|
||||
auto virt = virtual_allocator::allocate(pages);
|
||||
|
||||
paging::map_pages(virt, process.physical_user_stack, pages);
|
||||
|
||||
auto rsp = virt + scheduler::user_stack_size - 8;
|
||||
rsp -= sizeof(interrupt::syscall_regs) * 8;
|
||||
|
||||
auto regs = reinterpret_cast<interrupt::syscall_regs*>(rsp);
|
||||
|
||||
regs->rsp = scheduler::user_rsp - sizeof(interrupt::syscall_regs) * 8; //Not sure about that
|
||||
regs->rip = header->e_entry;
|
||||
regs->cs = gdt::USER_CODE_SELECTOR + 3;
|
||||
regs->ds = gdt::USER_DATA_SELECTOR + 3;
|
||||
regs->rflags = 0x200;
|
||||
|
||||
process.context = reinterpret_cast<interrupt::syscall_regs*>(scheduler::user_rsp - sizeof(interrupt::syscall_regs) * 8);
|
||||
|
||||
paging::unmap_pages(virt, pages);
|
||||
|
||||
//TODO virt should be deallocated
|
||||
}
|
||||
|
||||
void start() __attribute__((noreturn));
|
||||
void start(){
|
||||
started = true;
|
||||
|
||||
current_ticks = 0;
|
||||
|
||||
current_pid = 0;
|
||||
pcb[current_pid].rounds = TURNOVER;
|
||||
pcb[current_pid].state = process_state::RUNNING;
|
||||
pcb[current_pid].state = scheduler::process_state::RUNNING;
|
||||
|
||||
init_task_switch(current_pid);
|
||||
}
|
||||
|
||||
} //end of anonymous namespace
|
||||
|
||||
void scheduler::init(){ //Create the idle task
|
||||
create_idle_task();
|
||||
}
|
||||
|
||||
int64_t scheduler::exec(const std::string& file){
|
||||
auto content = read_elf_file(file);
|
||||
|
||||
if(!content){
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto buffer = content->c_str();
|
||||
|
||||
auto& process = new_process();
|
||||
|
||||
if(!create_paging(buffer, process)){
|
||||
k_print_line("Impossible to initialize paging");
|
||||
return -1;
|
||||
}
|
||||
|
||||
init_context(process, buffer);
|
||||
|
||||
queue_process(process.pid);
|
||||
|
||||
if(!started){
|
||||
start();
|
||||
} else {
|
||||
return process.pid;
|
||||
}
|
||||
}
|
||||
|
||||
void scheduler::kill_current_process(){
|
||||
if(DEBUG_SCHEDULER){
|
||||
k_printf("Kill %u\n", current_pid);
|
||||
@ -242,34 +461,6 @@ void scheduler::reschedule(){
|
||||
//At this point we just have to return to the current process
|
||||
}
|
||||
|
||||
scheduler::process_t& scheduler::new_process(){
|
||||
//TODO use get_free_pid() that searchs through the PCB
|
||||
auto pid = next_pid++;
|
||||
|
||||
auto& process = pcb[pid];
|
||||
|
||||
process.process.system = false;
|
||||
process.process.pid = pid;
|
||||
process.process.priority = scheduler::DEFAULT_PRIORITY;
|
||||
process.state = process_state::NEW;
|
||||
process.process.tty = stdio::get_active_terminal().id;
|
||||
|
||||
return process.process;
|
||||
}
|
||||
|
||||
void scheduler::queue_process(scheduler::pid_t pid){
|
||||
thor_assert(pid < scheduler::MAX_PROCESS, "pid out of bounds");
|
||||
|
||||
auto& process = pcb[pid];
|
||||
|
||||
thor_assert(process.process.priority <= scheduler::MAX_PRIORITY, "Invalid priority");
|
||||
thor_assert(process.process.priority >= scheduler::MIN_PRIORITY, "Invalid priority");
|
||||
|
||||
process.state = process_state::READY;
|
||||
|
||||
run_queues[process.process.priority].push_back(pid);
|
||||
}
|
||||
|
||||
scheduler::pid_t scheduler::get_pid(){
|
||||
return current_pid;
|
||||
}
|
||||
|
@ -720,186 +720,6 @@ void readelf_command(const std::vector<std::string>& params){
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string> read_elf_file(const std::string& file, const std::string& name){
|
||||
auto content = disks::read_file(file);
|
||||
|
||||
if(content.empty()){
|
||||
k_print(name);
|
||||
k_print_line(": The file does not exist or is empty");
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
if(!elf::is_valid(content)){
|
||||
k_print(name);
|
||||
k_print_line(": This file is not an ELF file or not in ELF64 format");
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
return {std::move(content)};
|
||||
}
|
||||
|
||||
bool allocate_user_memory(scheduler::process_t& process, size_t address, size_t size, size_t& ref){
|
||||
//1. Calculate some stuff
|
||||
auto first_page = paging::page_align(address);
|
||||
auto left_padding = address - first_page;
|
||||
auto bytes = left_padding + paging::PAGE_SIZE + size;
|
||||
auto pages = (bytes / paging::PAGE_SIZE) + 1;
|
||||
|
||||
//2. Get enough physical memory
|
||||
auto physical_memory = physical_allocator::allocate(pages);
|
||||
|
||||
if(!physical_memory){
|
||||
k_print_line("Cannot allocate physical memory, probably out of memory");
|
||||
return false;
|
||||
}
|
||||
|
||||
//3. Find a start of a page inside the physical memory
|
||||
|
||||
auto aligned_physical_memory = paging::page_aligned(physical_memory) ? physical_memory :
|
||||
(physical_memory / paging::PAGE_SIZE + 1) * paging::PAGE_SIZE;
|
||||
|
||||
//4. Map physical allocated memory to the necessary virtual memory
|
||||
|
||||
paging::user_map_pages(process, first_page, aligned_physical_memory, pages);
|
||||
|
||||
ref = physical_memory;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void clear_physical_memory(size_t memory, size_t pages){
|
||||
auto virt = virtual_allocator::allocate(pages);
|
||||
|
||||
paging::map_pages(virt, memory, pages);
|
||||
|
||||
auto it = reinterpret_cast<uint64_t*>(virt);
|
||||
std::fill_n(it, (pages * paging::PAGE_SIZE) / sizeof(uint64_t), 0);
|
||||
|
||||
paging::unmap_pages(virt, pages);
|
||||
|
||||
//TODO virt should be deallocated
|
||||
}
|
||||
|
||||
bool create_paging(char* buffer, scheduler::process_t& process){
|
||||
//1. Prepare PML4T
|
||||
|
||||
//Get memory for cr3
|
||||
process.physical_cr3 = physical_allocator::allocate(1);
|
||||
process.paging_size = paging::PAGE_SIZE;
|
||||
|
||||
clear_physical_memory(process.physical_cr3, 1);
|
||||
|
||||
//Map the kernel pages inside the user memory space
|
||||
paging::map_kernel_inside_user(process);
|
||||
|
||||
//2. Create all the other necessary structures
|
||||
|
||||
//2.1 Allocate user stack
|
||||
allocate_user_memory(process, scheduler::user_stack_start, scheduler::user_stack_size, process.physical_user_stack);
|
||||
|
||||
//2.2 Allocate all user segments
|
||||
|
||||
auto header = reinterpret_cast<elf::elf_header*>(buffer);
|
||||
auto program_header_table = reinterpret_cast<elf::program_header*>(buffer + header->e_phoff);
|
||||
|
||||
for(size_t p = 0; p < header->e_phnum; ++p){
|
||||
auto& p_header = program_header_table[p];
|
||||
|
||||
if(p_header.p_type == 1){
|
||||
auto address = p_header.p_vaddr;
|
||||
auto first_page = paging::page_align(address);
|
||||
auto left_padding = address - first_page;
|
||||
auto bytes = left_padding + paging::PAGE_SIZE + p_header.p_memsz;
|
||||
auto pages = (bytes / paging::PAGE_SIZE) + 1;
|
||||
|
||||
scheduler::segment_t segment;
|
||||
segment.size = pages * paging::PAGE_SIZE;
|
||||
|
||||
allocate_user_memory(process, first_page, pages, segment.physical);
|
||||
|
||||
process.segments.push_back(segment);
|
||||
|
||||
//Copy the code into memory
|
||||
|
||||
auto virtual_memory = virtual_allocator::allocate(pages);
|
||||
|
||||
paging::map_pages(virtual_memory, segment.physical, pages);
|
||||
|
||||
auto memory_start = virtual_memory + left_padding;
|
||||
|
||||
std::copy_n(reinterpret_cast<char*>(memory_start), buffer + p_header.p_offset, p_header.p_memsz);
|
||||
|
||||
paging::unmap_pages(virtual_memory, pages);
|
||||
}
|
||||
}
|
||||
|
||||
//2.3 Allocate kernel stack
|
||||
auto virtual_kernel_stack = virtual_allocator::allocate(scheduler::kernel_stack_size / paging::PAGE_SIZE);
|
||||
auto physical_kernel_stack = physical_allocator::allocate(scheduler::kernel_stack_size / paging::PAGE_SIZE);
|
||||
|
||||
if(!paging::map_pages(virtual_kernel_stack, physical_kernel_stack, scheduler::kernel_stack_size / paging::PAGE_SIZE)){
|
||||
return false;
|
||||
}
|
||||
|
||||
process.physical_kernel_stack = physical_kernel_stack;
|
||||
process.kernel_rsp = virtual_kernel_stack + (scheduler::user_stack_size - 8);
|
||||
|
||||
//3. Clear stacks
|
||||
clear_physical_memory(process.physical_user_stack, scheduler::user_stack_size / paging::PAGE_SIZE);
|
||||
clear_physical_memory(process.physical_kernel_stack, scheduler::kernel_stack_size / paging::PAGE_SIZE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void init_context(scheduler::process_t& process, const char* buffer){
|
||||
auto header = reinterpret_cast<const elf::elf_header*>(buffer);
|
||||
|
||||
auto pages = scheduler::user_stack_size / paging::PAGE_SIZE;
|
||||
auto virt = virtual_allocator::allocate(pages);
|
||||
|
||||
paging::map_pages(virt, process.physical_user_stack, pages);
|
||||
|
||||
auto rsp = virt + scheduler::user_stack_size - 8;
|
||||
rsp -= sizeof(interrupt::syscall_regs) * 8;
|
||||
|
||||
auto regs = reinterpret_cast<interrupt::syscall_regs*>(rsp);
|
||||
|
||||
regs->rsp = scheduler::user_rsp - sizeof(interrupt::syscall_regs) * 8; //Not sure about that
|
||||
regs->rip = header->e_entry;
|
||||
regs->cs = gdt::USER_CODE_SELECTOR + 3;
|
||||
regs->ds = gdt::USER_DATA_SELECTOR + 3;
|
||||
regs->rflags = 0x200;
|
||||
|
||||
process.context = reinterpret_cast<interrupt::syscall_regs*>(scheduler::user_rsp - sizeof(interrupt::syscall_regs) * 8);
|
||||
|
||||
paging::unmap_pages(virt, pages);
|
||||
|
||||
//TODO virt should be deallocated
|
||||
}
|
||||
|
||||
void queue_process(const std::string& file){
|
||||
auto content = read_elf_file(file, "exec");
|
||||
|
||||
if(!content){
|
||||
return;
|
||||
}
|
||||
|
||||
auto buffer = content->c_str();
|
||||
|
||||
auto& process = scheduler::new_process();
|
||||
|
||||
if(!create_paging(buffer, process)){
|
||||
k_print_line("Impossible to initialize paging");
|
||||
return;
|
||||
}
|
||||
|
||||
init_context(process, buffer);
|
||||
|
||||
scheduler::queue_process(process.pid);
|
||||
}
|
||||
|
||||
void exec_command(const std::vector<std::string>& params){
|
||||
if(params.size() < 2){
|
||||
k_print_line("exec: Need the name of the executable to read");
|
||||
@ -913,13 +733,7 @@ void exec_command(const std::vector<std::string>& params){
|
||||
return;
|
||||
}
|
||||
|
||||
scheduler::init();
|
||||
|
||||
for(size_t i = 1; i < params.size(); ++i){
|
||||
queue_process(params[i]);
|
||||
}
|
||||
|
||||
scheduler::start();
|
||||
scheduler::exec(params[1]);
|
||||
}
|
||||
|
||||
void vesainfo_command(const std::vector<std::string>&){
|
||||
|
Loading…
x
Reference in New Issue
Block a user