Code: Select all
typedef enum {
RUNNING,
READY,
WAITING,
TERMINATED
} process_state_t;
typedef struct process {
void (*func)(); // Pointer to the function to be executed
process_state_t state; // State of the process (RUNNING, READY, etc.)
unsigned int stack_pointer; // Pointer to the stack for this process
unsigned int stack_size; // Size of the stack
struct process *next; // Pointer to the next process in the list
} process_t;
process_t *ready_queue = NULL; // A linked list of processes in the READY state
process_t *current_process = NULL; // The currently running process
extern void *malloc(size_t size);
extern void printf(const char *format, ...);
extern void free(void *ptr);
// Assembly function to perform the actual context switch
void perform_context_switch(unsigned int* old_esp, unsigned int new_esp) {
__asm__ __volatile__ (
"mov %%esp, %0\n\t" // Save current ESP to *old_esp
"mov %1, %%esp\n\t" // Load new ESP
"popa\n\t" // Restore general-purpose registers
"iret\n\t" // Return from interrupt
: "=m" (*old_esp) // Output constraint for saving old ESP
: "r" (new_esp) // Input constraint for new ESP
: "memory" // Memory clobber
);
// This line will never execute due to `iret`
}
void add_process(void (*func)(), unsigned int stack_size) {
process_t *new_process = (process_t*)malloc(sizeof(process_t));
if (new_process == NULL) return;
new_process->func = func;
new_process->state = READY;
new_process->stack_size = stack_size;
// Allocate stack
void* stack_mem = malloc(stack_size);
if (stack_mem == NULL) {
free(new_process);
return;
}
// Point to top of stack (stack grows down)
unsigned int *stack = (unsigned int*)((unsigned int)stack_mem + stack_size);
// Set up initial interrupt frame
*(--stack) = 0x202; // EFLAGS with interrupts enabled
*(--stack) = 0x08; // CS - assuming this is your code segment
*(--stack) = (unsigned int)func; // EIP - starting point
// Save initial registers
*(--stack) = 0; // EAX
*(--stack) = 0; // ECX
*(--stack) = 0; // EDX
*(--stack) = 0; // EBX
*(--stack) = (unsigned int)stack_mem + stack_size; // ESP
*(--stack) = 0; // EBP
*(--stack) = 0; // ESI
*(--stack) = 0; // EDI
new_process->stack_pointer = (unsigned int)stack;
new_process->next = NULL;
// Add to ready queue
if (ready_queue == NULL) {
ready_queue = new_process;
} else {
process_t *last = ready_queue;
while (last->next != NULL) last = last->next;
last->next = new_process;
}
}
void switch_context(struct interrupt_frame* frame) {
if (current_process == NULL) {
if (ready_queue != NULL) {
current_process = ready_queue;
ready_queue = ready_queue->next;
current_process->state = RUNNING;
current_process->next = NULL;
__asm__ __volatile__ (
"mov %0, %%esp\n\t"
"popa\n\t"
"iret\n\t"
: : "r" (current_process->stack_pointer)
);
}
return;
}
// Save the current process state
current_process->stack_pointer = frame->esp;
current_process->state = READY;
// Move current process to end of ready queue
if (ready_queue == NULL) {
ready_queue = current_process;
} else {
process_t *last = ready_queue;
while (last->next != NULL) last = last->next;
last->next = current_process;
}
current_process->next = NULL;
// Get the next process from the ready queue
if (ready_queue != NULL) {
process_t *next = ready_queue;
ready_queue = ready_queue->next;
next->state = RUNNING;
next->next = NULL;
process_t *old = current_process;
current_process = next;
printf(" Process at %x (func: %x)\n",
(unsigned int)next, (unsigned int)next->func);
perform_context_switch(&old->stack_pointer, next->stack_pointer);
}
__asm__ __volatile__ (
"popa\n\t"
"iret\n\t"
);
}
#pragma GCC target("general-regs-only")
__attribute__((interrupt)) void PIT_handler(struct interrupt_frame* frame) {
//
// printf("PIT called\n");
switch_context(frame);
outb(0x20, 0x20);
outb(0xA0, 0x20);
}
#pragma GCC reset_options
void task_1() {
printf("This is PROCESS 1\n");
while (1)
{
/* code */
}
}
void task_2() {
printf("This is PROCESS 2\n");
while (1)
{
/* code */
}
}
add_process(task_1, 1024); // Task 1 with a stack size of 1024 bytes
add_process(task_2, 1024); // Task 2 with a stack size of 1024 bytes