Max OS 0.3
Loading...
Searching...
No Matches
scheduler.cpp
Go to the documentation of this file.
1
10#include <common/logger.h>
11#include <runtime/gdbstub.h>
12
13using namespace MaxOS;
14using namespace MaxOS::common;
15using namespace MaxOS::processes;
16using namespace MaxOS::processes::resources;
17using namespace MaxOS::memory;
18using namespace MaxOS::hardwarecommunication;
19using namespace MaxOS::system;
20using namespace MaxOS::runtime;
21using namespace MaxOS::console;
22
30: InterruptHandler(0x20),
31 m_shared_memory_registry(resource_type_t::SHARED_MEMORY),
32 m_shared_messages_registry(resource_type_t::MESSAGE_ENDPOINT),
33 m_next_pid(0),
34 m_next_tid(0)
35{
36
37 // Set up the global scheduler
38 Logger::INFO() << "Setting up global Scheduler\n";
39 s_instance = this;
40
41 // Set up the per core scheduler
42 for(const auto& core : CPU::cores)
43 core -> scheduler = new Scheduler(core->id);
44
45 // Load the elfs
46 load_multiboot_elfs(&multiboot);
47
48}
49
56
57 return s_instance;
58}
59
60
68
69 // Scheduler not active
70 if(!s_instance || !s_instance->m_active)
71 return status;
72
73 auto s = core_scheduler()->schedule(status);
74 ASSERT(s->rip != 0, "Cant run a empty state\n");
75 return s;
76}
77
82
83 // No longer the global instance
84 s_instance = nullptr;
85
86 // Free the per core scheduler
87 for(const auto& core : CPU::cores)
88 delete core->scheduler;
89
90}
91
96
97 s_instance -> m_active = true;
98 for(const auto& core : CPU::cores)
99 core->scheduler->activate();
100
101}
102
107
108 s_instance -> m_active = false;
109 for(const auto& core : CPU::cores)
110 core->scheduler->deactivate();
111
112}
113
121
122}
123
131
132 // Find the scheduler with the lowest work load
133 auto core = CPU::cores[0];
134 for(const auto& current : CPU::cores)
135 if((check_threads && current->scheduler->thread_amount() < core->scheduler->thread_amount())
136 || current->scheduler->process_amount() < core->scheduler->process_amount())
137 core = current;
138
139
140 return core;
141
142}
143
144
151
152 for (multiboot_tag* tag = multiboot->start_tag(); tag->type != MULTIBOOT_TAG_TYPE_END; tag = (struct multiboot_tag*) ((multiboot_uint8_t*) tag + ((tag->size + 7) & ~7))) {
153
154 // Tag is not an module
155 if (tag->type != MULTIBOOT_TAG_TYPE_MODULE)
156 continue;
157
158 // Get the tag
159 auto* module = (struct multiboot_tag_module*) tag;
160 auto start = (uintptr_t)PhysicalMemoryManager::to_dm_region(module->mod_start);
161 Logger::DEBUG() << "Creating process from multiboot module for " << module->cmdline << " (at 0x" << (uint64_t) module->mod_start << ")\n";
162
163 // Handle the init ramdisk
164 if (strcmp(module->cmdline, "initrd") == 0)
165 prepare_initrd(module);
166
167 // Try to create the elf from the module
168 ELF64 elf(start);
169 if (!elf.is_valid())
170 continue;
171
172 // Create an array of args for the process
173 char* args[1] = {module->cmdline};
174
175 // Create the process
176 auto* process = new Process(module->cmdline, args, 1, &elf);
177 system_scheduler() -> add_process(process);
178
179 Logger::DEBUG() << "ELF loaded to pid " << process->pid() << "\n";
180 }
181}
182
191
192 // Find the Init process
193 Process* init_process = get_process(4);
194 ASSERT(init_process != nullptr, "Can't find init process\n");
195
196 // Extract info
197 auto start = (uintptr_t)PhysicalMemoryManager::to_dm_region(module->mod_start);
198 uint32_t size = module->mod_end - module->mod_start;
199
200 // Prepare
201 size_t actual_size;
202 MemoryManager::switch_active_memory_manager(init_process->memory_manager);
203 init_process->memory_manager->vmm()->allocate(0xFFF000, size + sizeof(uint32_t), actual_size, WRITE | PRESENT);
204
205 // Copy
206 memcpy((void*)0xFFF000, &size, sizeof(uint32_t));
207 memcpy((void*)(0xFFF000 + sizeof(uint32_t)), (void*)start, size);
208 MemoryManager::switch_active_memory_manager(MemoryManager::s_kernel_memory_manager);
209}
210
215
216 // No threads or processes to get
217 if(!s_instance || !s_instance->m_active)
218 return;
219
220 auto process = current_process();
221 auto thread = current_thread();
222
223 Logger::active_logger()->printf("(%s:t%dc%d) ",process->name.c_str(), thread->tid,CPU::executing_core()->id);
224}
225
226
234uint64_t GlobalScheduler::add_process(Process* process, Scheduler* scheduler) {
235
236 // No cores?
237 if(CPU::cores.empty())
238 return 0;
239
240 // Find the core with the least processes
241 if (!scheduler)
242 scheduler = least_busy_core()->scheduler;
243 auto core_id = scheduler->core_id();
244
245 // Save the pid
246 auto pid = scheduler->add_process(process);
247 m_core_pids.insert(pid,core_id);
248
249 //@TODO better way to do this
250 if (process->name == DEBUG_TARGET && DO_DEBUGGING) {
251 Logger::DEBUG() << "Starting PROG with GDB\n";
252 auto serial = new SerialConsole();
253 auto debugger = new GDBServer(serial);
254 debugger->attach(process->threads()[0]);
255 }
256
257 Logger::DEBUG() << "Adding process " << pid << ": " << process->name << " to core " << core_id <<"\n";
258 return pid;
259}
260
268uint64_t GlobalScheduler::add_thread(Thread* thread, Scheduler* scheduler) {
269
270 // No cores?
271 if(CPU::cores.empty())
272 return 0;
273
274 // Find the core with the least threads
275 if (!scheduler)
276 scheduler = least_busy_core(true)->scheduler;
277 auto core_id = scheduler->core_id();
278
279 // Save the tid
280 auto tid = scheduler->add_thread(thread);
281 m_core_tids.insert(tid,core_id);
282
283 Logger::DEBUG() << "Adding thread " << tid << " to core " << core_id <<"\n";
284 return tid;
285}
286
294
295 // Get the core running the process
296 if(!s_instance)
297 return 0;
298
299 auto core = s_instance -> m_core_pids[process->pid()];
300 return CPU::cores[core]->scheduler->remove_process(process);
301}
302
310
311 // Get the core running the process
312 if(!s_instance)
313 return nullptr;
314
315 auto core = s_instance -> m_core_pids[process->pid()];
316 return CPU::cores[core]->scheduler->force_remove_process(process);
317
318}
319
326 if(!s_instance || !s_instance ->m_active)
327 return nullptr;
328
330}
331
339
340 // Get the core running the pid
341 if(!s_instance)
342 return nullptr;
343
344 auto core = s_instance -> m_core_pids[pid];
345 return CPU::cores[core]->scheduler->get_process(pid);
346}
347
353 return s_instance -> m_next_pid++;
354}
355
362
363 if(!s_instance || !s_instance ->m_active)
364 return nullptr;
365
366 return core_scheduler()->current_thread();
367}
368
376
377 // Get the core running the tid
378 if(!s_instance)
379 return nullptr;
380
381 auto core = s_instance -> m_core_tids[tid];
382 return CPU::cores[core]->scheduler->get_thread(tid);
383}
384
390 return s_instance->m_next_tid++;
391}
392
401
408 if (!s_instance)
409 return false;
410
411 return s_instance->m_active;
412}
413
417Scheduler::Scheduler(uint64_t core_id)
418: m_next_thread_index(0),
419 m_active(false),
420 m_ticks(0),
421 m_core_id(core_id)
422{
423
424 // Name the idle
425 string name = "Idle c";
426 name += string(m_core_id);
427
428 // Create this idle process
429 auto* idle = new Process(name, nullptr, nullptr, 0, true);
431
432 // Idle must use the correct kernel memory
433 idle->memory_manager = MemoryManager::s_kernel_memory_manager;
434 idle->threads()[0]->set_tss_pointer(CPU::cores[core_id]->tss.rsp0);
435}
436
437Scheduler::~Scheduler() = default;
438
439
447
448 // Scheduler cant schedule anything
449 if (m_threads.empty() || !m_active)
450 return cpu_state;
451
452 // Thread that we are dealing with
453 Thread* current_thread = m_threads[m_next_thread_index];
454
455 // Ticked
456 m_ticks++;
458
459 // The idle thread has no time slice to protect
460 Process* running_process = current_process();
461 if (running_process && running_process->is_kernel)
462 return schedule_next(cpu_state);
463
464 // Thread hasn't used its time slot yet
466 return cpu_state;
467
468 // Schedule the next thread
469 return schedule_next(cpu_state);
470}
471
481
482 // Get the current state
483 Thread* current_thread = m_threads[m_next_thread_index];
484 Process* owner_process = current_process();
485 auto old_tid = m_next_thread_index;
486
487 // Save the executing thread state
488 current_thread->execution_state = *cpu_state;
490 if (current_thread->thread_state == ThreadState::RUNNING)
491 current_thread->thread_state = ThreadState::READY;
492
493 // Find a free thread to run
494 while ((++m_next_thread_index) != old_tid){
495 m_next_thread_index %= m_threads.size();
496
497 // Get the current thread
498 current_thread = m_threads[m_next_thread_index];
499 owner_process = current_process();
500
501 // Handle state changes
502 switch (current_thread->thread_state) {
503
504 case ThreadState::SLEEPING:
505
506 // If the wake-up time hasn't occurred yet, run the next thread
509 continue;
510 }
511
512 break;
513
514 case ThreadState::STOPPED:
515
516 // Find the process that has the thread and remove it
517 for (auto thread: owner_process->threads()) {
518 if (thread == current_thread) {
519 owner_process->remove_thread(thread->tid);
520 break;
521 }
522 }
523
524 // Remove the thread
525 m_threads.erase(m_threads.begin() + m_next_thread_index);
526 if(owner_process->threads().empty())
527 remove_process(owner_process);
528 continue;
529
530 case ThreadState::WAITING:
531 continue;
532
533 default:
534 break;
535 }
536 break;
537 }
538
539 int pid0 = m_processes.size() > 0 ? (int) m_processes[0]->pid() : -1;
540 int pid1 = m_processes.size() > 1 ? (int) m_processes[1]->pid() : -1;
541 int pid2 = m_processes.size() > 2 ? (int) m_processes[2]->pid() : -1;
542 ASSERT(owner_process != nullptr, "No owner process: tid %d parent_pid %d core %d index %d threads %d processes %d pids %d %d %d\n",
543 (int) current_thread->tid, (int) current_thread->parent_pid, (int) m_core_id,
544 (int) m_next_thread_index, (int) m_threads.size(), (int) m_processes.size(), pid0, pid1, pid2);
545
546
547 // Load the thread's state
548 return load_process(owner_process, current_thread);
549}
550
558cpu_status_t* Scheduler::load_process(Process* process, Thread* thread) {
559
560 // Prepare the next thread to run
561 thread->thread_state = ThreadState::RUNNING;
562 thread->restore_sse_state();
563
564 // Load into the threads memory region
565 MemoryManager::switch_active_memory_manager(process->memory_manager);
566
567 if (process->name == "DriverManager.elf")
568 asm("nop");
569
570 // Load the tss state for this thread
571 CPU::executing_core() -> tss.rsp0 = thread->tss_pointer();
572 thread->load_port_bitmap();
573
574 return &thread->execution_state;
575}
576
583uint64_t Scheduler::add_process(Process* process) {
584
585 // Get the next process ID
586 auto pid = GlobalScheduler::next_pid();
587 process->set_pid(pid);
588
589 // Add the process to the list
590 m_processes.push_back(process);
591
592 // Add the threads to the list
593 for(const auto& thread : process->threads())
594 thread->tid = add_thread(thread);
595
596 return pid;
597}
598
605uint64_t Scheduler::add_thread(Thread* thread) {
606
607 // Get the next thread ID
608 auto tid = GlobalScheduler::next_tid();
609 thread->tid = tid;
610
611 // Add the thread to the list
612 m_threads.push_back(thread);
613
614 // Return the thread ID
615 return tid;
616}
617
623uint64_t Scheduler::ticks() const {
624
625 return m_ticks;
626}
627
633uint64_t Scheduler::core_id() const {
634
635 return m_core_id;
636}
637
642
643 m_active = true;
644}
645
653
654 // Check if the process has no threads
655 if (!process->threads().empty()) {
656
657 // Set the threads to stopped or remove them if forced
658 for (auto thread: process->threads())
659 thread->thread_state = ThreadState::STOPPED;
660
661 // Need to wait until the threads are stopped before removing the process (this will be called again when all threads are stopped)
662 return -1;
663
664 }
665
666 // Remove the process
667 for (uint32_t i = 0; i < m_processes.size(); i++) {
668 if (m_processes[i] == process) {
669 m_processes.erase(m_processes.begin() + i);
670
671 // Delete the process mem
672 delete process;
673 return 0;
674 }
675 }
676
677 // Process not found
678 return -1;
679}
680
688
689 // If there is no process, fail
690 if (!process)
691 return nullptr;
692
693 // Remove all the threads
694 for (auto thread: process->threads()) {
695
696 // Remove the thread from the scheduler
697 size_t index = m_threads.find(thread) - m_threads.begin();
698 m_threads.erase(m_threads.begin() + index);
699
700 // Delete the thread
701 process->remove_thread(thread->tid);
702
703 }
704
705 // Process will be dead now so run the next process (don't care about the execution state being outdated as it is being
706 // removed regardless)
707 return schedule_next(&current_thread()->execution_state);
708}
709
716
717 Process* current_process = nullptr;
718
719 // Find the process that has the thread being executed
720 for (auto process: m_processes)
721 if (process->pid() == current_thread()->parent_pid) {
722 current_process = process;
723 break;
724 }
725
726 return current_process;
727}
728
736
737 // Try to find the process
738 for (auto process: m_processes)
739 if (process->pid() == pid)
740 return process;
741
742 // Not found
743 return nullptr;
744}
745
751 return m_processes.size();
752}
753
760
761 return m_threads[m_next_thread_index];
762}
763
769 return m_threads.size();
770}
771
776
777 m_active = false;
778}
779
787
788 // Try to find the thread
789 for (auto thread: m_threads)
790 if (thread->tid == tid)
791 return thread;
792
793 return nullptr;
794}
void printf(const char *format,...)
Prints a formatted string to the logger (thread safe)
Definition logger.cpp:250
static Logger * active_logger()
Gets the active logger (to be used to modify the logger)
Definition logger.cpp:240
static LogRecord DEBUG()
Gets the active logger for debug logs.
Definition logger.cpp:204
static LogRecord INFO()
Gets the active logger for info logs.
Definition logger.cpp:195
A driver for the serial output.
Definition serial.h:25
Handles a certain interrupt number.
Definition interrupts.h:44
virtual void handle_interrupt()
Handles an interrupt.
static MemoryManager * s_kernel_memory_manager
The memory manager for any kernel processes and all kernel allocations.
static void * to_dm_region(uintptr_t physical_address)
Converts a physical address to a direct map region address if it is in the lower region using the hig...
Definition physical.cpp:929
void * allocate(size_t size, size_t flags)
Allocate a new chunk of virtual memory.
Definition virtual.cpp:113
Handles the loading and parsing of 64-bit ELF files.
Definition elf.h:217
bool is_valid() const
Checks if the elf file is valid for MaxOS runtime.
Definition elf.cpp:102
The global scheduler that manages all processes and threads across all cores.
Definition scheduler.h:28
static uint64_t next_pid()
Gets the next usable global PID.
static Scheduler * core_scheduler()
Gets the scheduler for the currently executing core.
static Process * get_process(uint64_t pid)
Gets a process on the currently executing core by its PID.
static uint64_t remove_process(Process *process)
Removes a process from the scheduler if the process has no threads, if it does then the threads are s...
uint64_t add_process(Process *process, Scheduler *scheduler=nullptr)
Adds a process to the least busy core.
static void print_running_header()
Print the header for the running processes in the form ({name}:t{tid}c{core})
static uint64_t next_tid()
Gets the next usable global TID.
uint64_t add_thread(Thread *thread, Scheduler *scheduler=nullptr)
Adds a thread to the least busy core.
static void prepare_initrd(multiboot_tag_module *module)
Loads the initrd into the init program at 0xFFF000.
~GlobalScheduler()
Destroys the global scheduler and frees the per core schedulers.
Definition scheduler.cpp:81
system::Core * least_busy_core(bool check_threads=false)
Finds the core with the least amount of threads/processes.
static GlobalScheduler * system_scheduler()
Gets the system scheduler.
Definition scheduler.cpp:55
static Process * current_process()
Gets the process on the currently executing core.
static Thread * get_thread(uint64_t tid)
Gets a thread on the currently executing core by its TID.
GlobalScheduler(system::Multiboot &multiboot)
Constructs a new Global Scheduler object. Registers as the interrupt handler for interrupt 0x20 and s...
Definition scheduler.cpp:29
static void activate()
Activates each core's scheduler.
Definition scheduler.cpp:95
static void deactivate()
Deactivates each core's scheduler.
static bool is_active()
Checks if the global scheduler is active.
void balance()
Moves processes and threads between cores to balance the load so that each core has a similar amount ...
static Thread * current_thread()
Gets the thread on the currently executing core.
static void load_multiboot_elfs(system::Multiboot *multiboot)
Loads any valid ELF files from the multiboot structure.
static system::cpu_status_t * force_remove_process(Process *process)
Removes a process from the scheduler and deletes all threads, begins running the next process.
A process that can be scheduled by the Scheduler, wraps & manages threads as well as its own address ...
Definition process.h:94
uint64_t pid() const
Gets the pid of the process.
Definition process.cpp:429
string name
The name of the process.
Definition process.h:118
common::Vector< Thread * > threads()
Gets the threads of the process.
Definition process.cpp:419
bool is_kernel
Whether this process is a kernel process.
Definition process.h:116
void set_pid(uint64_t pid)
Sets the pid of the process once added to the queue.
Definition process.cpp:399
void remove_thread(uint64_t tid)
Remove a thread by its tid (NOTE: this will not remove it from the scheduler)
Definition process.cpp:375
memory::MemoryManager * memory_manager
The manager for memory used by this process.
Definition process.h:121
Schedules processes to run on the core via their threads.
Definition scheduler.h:90
uint64_t remove_process(Process *process)
Removes a process from the scheduler if the process has no threads, if it does then the threads are s...
uint64_t ticks() const
Gets how long the system has been running for.
uint64_t thread_amount()
Gets how many threads are running on this scheduler.
Thread * current_thread()
Gets the currently executing thread.
system::cpu_status_t * force_remove_process(Process *process)
Removes a process from the scheduler and deletes all threads, begins running the next process.
Process * get_process(uint64_t pid)
Gets a process by its PID.
uint64_t add_process(Process *process)
Adds a process to the scheduler.
Thread * get_thread(uint64_t tid)
Gets a thread by its TID.
uint64_t add_thread(Thread *thread)
Adds a thread to the scheduler.
system::cpu_status_t * schedule(system::cpu_status_t *cpu_state)
Schedules the next thread to run.
uint64_t process_amount()
Gets how processes are running on this scheduler.
void activate()
Activates the scheduler.
Scheduler(uint64_t core_id)
Constructs a new Scheduler object and creates the idle process.
Process * current_process()
Gets the current process.
uint64_t core_id() const
Gets the core that this scheduler handles processes for.
system::cpu_status_t * schedule_next(system::cpu_status_t *status)
Schedules the next thread to run.
void deactivate()
Deactivates the scheduler.
The execution context of a sub-process thread.
Definition process.h:50
uint64_t parent_pid
The parent process ID.
Definition process.h:69
void save_sse_state()
Saves the SSE, x87 FPU, and MMX states from memory to the thread.
Definition process.cpp:125
uint64_t tid
The thread ID.
Definition process.h:68
volatile system::cpu_status_t execution_state
The CPU state of the thread.
Definition process.h:71
volatile size_t ticks
The number of ticks the thread has run for.
Definition process.h:74
uintptr_t tss_pointer() const
Gets the stack pointer to use for the TSS when switching to this thread.
Definition process.h:77
volatile thread_state_t thread_state
The current state of the thread.
Definition process.h:72
void restore_sse_state()
Restores the SSE, x87 FPU, and MMX states from the thread to memory.
Definition process.cpp:138
volatile size_t wakeup_time
The time at which the thread should wake up (if sleeping)
Definition process.h:75
void load_port_bitmap()
Copy this ports.
Definition process.cpp:268
Handler for running a GDB server over serial.
Definition gdbstub.h:71
static common::Vector< Core * > cores
The list of CPU cores in the system (populated during initialization, includes the BSP and cores that...
Definition cpu.h:273
static Core * executing_core()
Gets the core that is currently executing.
Definition cpu.cpp:660
Represents a CPU core in the system.
Definition cpu.h:216
uint8_t id
The ID of this core.
Definition cpu.h:243
processes::Scheduler * scheduler
The scheduler for this core.
Definition cpu.h:249
Parses and provides access to Multiboot 2 information.
Definition multiboot.h:518
multiboot_tag * start_tag() const
struct PACKED MaxOS::system::CPUStatus cpu_status_t
Alias for CPUStatus struct.
GDB Stub definition for remote debugging userspace processes.
constexpr bool DO_DEBUGGING
Whether to use the serial for GDB communcation or for logging.
Definition gdbstub.h:26
constexpr char * DEBUG_TARGET
The userspace process to attach to.
Definition gdbstub.h:29
Defines a Logger class for logging messages with different severity levels to multiple output streams...
@ PRESENT
The page is present in memory.
Definition physical.h:44
@ WRITE
Memory in this page is writable.
Definition physical.h:45
KPI::ResourceType resource_type_t
Alias to make the libsyscore ResourceType accessible here.
Definition resource.h:25
Defines a GlobalScheduler and Scheduler for managing processes and threads.
constexpr size_t TICKS_PER_EVENT
Number of times the clock has to interrupt before switching to the next process/thread.
Definition scheduler.h:84
A tag containing information about a loaded module.
Definition multiboot.h:253
multiboot_uint32_t mod_start
The starting address of the module.
Definition multiboot.h:256
Common header for all multiboot info tags.
Definition multiboot.h:234