Move thread creation implementation into scheduler

This commit is contained in:
Baptiste Wicht 2014-02-03 21:15:00 +01:00
parent 3d68d1eb50
commit d3a20db821
4 changed files with 237 additions and 229 deletions

View File

@ -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

View File

@ -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();

View File

@ -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;
}

View File

@ -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>&){