📄 kernelmultitasker.c
字号:
// The scheduler needs to make a task (but not a fully-fledged // process) for itself. All other task switches will follow // as nested tasks from the scheduler. int status = 0; int interrupts = 0; processImage schedImage = { scheduler, scheduler, NULL, 0xFFFFFFFF, NULL, 0xFFFFFFFF, 0xFFFFFFFF, "", 0, { NULL } }; status = createNewProcess("scheduler process", kernelProc->priority, kernelProc->privilege, &schedImage, 0); if (status < 0) return (status); schedulerProc = getProcessById(status); removeProcessFromQueue(schedulerProc); // Set the instruction pointer to the scheduler task schedulerProc->taskStateSegment.EIP = (unsigned) scheduler; // Interrupts should always be disabled for this task, and we manually set // the NT (nested task) flag as well, since Virtual PC doesn't do it when // we switch the first time. schedulerProc->taskStateSegment.EFLAGS = 0x00004002; // Get a page directory schedulerProc->taskStateSegment.CR3 = (unsigned) kernelPageGetDirectory(KERNELPROCID); // Not busy markTaskBusy(schedulerProc->tssSelector, 0); // The scheduler task should now be set up to run. We should set // up the kernel task to resume operation // Before we load the kernel's selector into the task reg, mark it as // not busy, since one cannot load the task register, with a busy TSS // selector... markTaskBusy(kernelProc->tssSelector, 0); // Make the kernel's Task State Segment be the current one. In // reality, it IS still the currently running code kernelProcessorLoadTaskReg(kernelProc->tssSelector); // Reset the schedulerTime and schedulerTimeslices schedulerTime = 0; schedulerTimeslices = 0; // Make sure the scheduler is set to "run" schedulerStop = 0; // Set the "switched by call" flag schedulerSwitchedByCall = 0; // Make note that the multitasker has been enabled. We do it a little // early so we can finish some of our tasks of creating threads without // complaints multitaskingEnabled = 1; // Yield control to the scheduler kernelMultitaskerYield(); // Disable interrupts, so we can insure that we don't immediately get // a timer interrupt. kernelProcessorSuspendInts(interrupts); // Hook the system timer interrupt. oldSysTimerHandler = kernelInterruptGetHandler(INTERRUPT_NUM_SYSTIMER); if (oldSysTimerHandler == NULL) return (status = ERR_NOTINITIALIZED); // Install a task gate for the interrupt, which will // be the scheduler's timer interrupt. After this point, our // new scheduler task will run with every clock tick status = kernelDescriptorSetIDTTaskGate((0x20 + INTERRUPT_NUM_SYSTIMER), schedulerProc->tssSelector); if (status < 0) { kernelProcessorRestoreInts(interrupts); return (status); } // Reenable interrupts after we get control back from the scheduler kernelProcessorRestoreInts(interrupts); // Return success return (status = 0);}static int createKernelProcess(void){ // This function will create the kernel process at initialization time. // Returns 0 on success, negative otherwise. int status = 0; int kernelProcId = 0; processImage kernImage = { (void *) KERNEL_VIRTUAL_ADDRESS, kernelMain, NULL, 0xFFFFFFFF, NULL, 0xFFFFFFFF, 0xFFFFFFFF, "", 0, { NULL } }; // The kernel process is its own parent, of course, and it is owned // by "admin". We create no page table, and there are no arguments. kernelProcId = createNewProcess("kernel process", 1, PRIVILEGE_SUPERVISOR, &kernImage, 0); if (kernelProcId < 0) // Damn. Not able to create the kernel process return (kernelProcId); // Get the pointer to the kernel's process kernelProc = getProcessById(kernelProcId); // Make sure it's not NULL if (kernelProc == NULL) // Can't access the kernel process return (status = ERR_NOSUCHPROCESS); // Interrupts are initially disabled for the kernel kernelProc->taskStateSegment.EFLAGS = 0x00000002; // Set the current process to initially be the kernel process kernelCurrentProcess = kernelProc; // Deallocate the stack that was allocated, since the kernel already // has one. kernelMemoryRelease(kernelProc->userStack); // Create the kernel process' environment status = kernelEnvironmentCreate(KERNELPROCID, (variableList *) &(kernelProc->environment), NULL); if (status < 0) // Couldn't create an environment structure for this process return (status); // Make the kernel's text streams be the console streams kernelProc->textInputStream = kernelTextGetConsoleInput(); kernelProc->textInputStream->ownerPid = KERNELPROCID; kernelProc->textOutputStream = kernelTextGetConsoleOutput(); // Make the kernel process runnable kernelProc->state = proc_ready; // Return success return (status = 0);}static void incrementDescendents(kernelProcess *theProcess){ // This will walk up a chain of dependent child threads, incrementing // the descendent count of each parent kernelProcess *parentProcess = NULL; if (theProcess->processId == KERNELPROCID) // The kernel is its own parent return; parentProcess = getProcessById(theProcess->parentProcessId); if (parentProcess == NULL) // No worries. Probably not a problem return; parentProcess->descendentThreads++; // Do a recursion to walk up the chain incrementDescendents(parentProcess); // Done return;}static void decrementDescendents(kernelProcess *theProcess){ // This will walk up a chain of dependent child threads, decrementing // the descendent count of each parent kernelProcess *parentProcess = NULL; if (theProcess->processId == KERNELPROCID) // The kernel is its own parent return; parentProcess = getProcessById(theProcess->parentProcessId); if (parentProcess == NULL) // No worries. Probably not a problem return; parentProcess->descendentThreads--; // Do a recursion to walk up the chain decrementDescendents(parentProcess); // Done return;}static void kernelProcess2Process(kernelProcess *kernProcess, process *userProcess){ // Given a kernel-space process structure, create the corresponding // user-space version. strncpy(userProcess->processName, (char *) kernProcess->processName, MAX_PROCNAME_LENGTH); userProcess->userId = kernProcess->userId; userProcess->processId = kernProcess->processId; userProcess->type = kernProcess->type; userProcess->priority = kernProcess->priority; userProcess->privilege = kernProcess->privilege; userProcess->parentProcessId = kernProcess->parentProcessId; userProcess->descendentThreads = kernProcess->descendentThreads; userProcess->cpuPercent = kernProcess->cpuPercent; userProcess->state = kernProcess->state;}////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Below here, the functions are exported for external use////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////int kernelMultitaskerInitialize(void){ // This function intializes the kernel's multitasker. int status = 0; int count; // Make sure multitasking is NOT enabled already if (multitaskingEnabled) return (status = ERR_ALREADY); // Now we must initialize the process queue for (count = 0; count < MAX_PROCESSES; count ++) processQueue[count] = NULL; numQueued = 0; // We need to create the kernel's own process. status = createKernelProcess(); // Make sure it was successful if (status < 0) return (status); // Now start the scheduler status = schedulerInitialize(); if (status < 0) // The scheduler couldn't start return (status); // Create an "idle" thread to consume all unused cycles status = spawnIdleThread(); // Make sure it was successful if (status < 0) return (status); // Start the exception handler thread. status = exceptionThreadInitialize(); // Make sure it was successful if (status < 0) return (status); // Log a boot message kernelLog("Multitasking started"); // Return success return (status = 0);}int kernelMultitaskerShutdown(int nice){ // This function will shut down the multitasker and halt the scheduler, // returning exclusive control to the kernel process. If the nice // argument is non-zero, this function will do a nice orderly shutdown, // killing all the running processes gracefully. If it is zero, the // resources allocated to the processes will never be freed, and the // multitasker will just stop. Returns 0 on success, negative otherwise. int status = 0; // Make sure multitasking has been enabled if (!multitaskingEnabled) // We can't yield if we're not multitasking yet return (status = ERR_NOTINITIALIZED); // If we are doing a "nice" shutdown, we will kill all the running // processes (except the kernel and scheduler) gracefully. if (nice) kernelMultitaskerKillAll(); // Set the schedulerStop flag to stop the scheduler schedulerStop = 1; // Yield control back to the scheduler, so that it can stop kernelMultitaskerYield(); // Make note that the multitasker has been disabled multitaskingEnabled = 0; // Deallocate the stack used by the scheduler kernelMemoryReleaseSystem(schedulerProc->userStack); // Print a message kernelLog("Multitasking stopped"); return (status = 0);}void kernelExceptionHandler(void){ // This code sleeps until woken up by an exception. Before multitasking // starts it is referenced by an interrupt gate. Afterward, it has its // TSS referenced by a task gate descriptor in the IDT. char tmpMsg[256]; void *stackMemory = NULL; int count; extern kernelSymbol *kernelSymbols; extern int kernelNumberSymbols; while(1) { // We got an exception. deadProcess = kernelCurrentProcess; deadProcess->state = proc_stopped; kernelCurrentProcess = exceptionProc; // Don't get into a loop. if (exceptionProc->state != proc_sleeping) kernelPanic("Double-fault while processing exception"); exceptionProc->state = proc_running; // If the fault occurred while we were processing an interrupt, // we should tell the PIC that the interrupt service routine is // finished. It's not really fair to kill a process because an // interrupt handler is screwy, but that's what we have to do for // the time being. if (kernelProcessingInterrupt) kernelPicEndOfInterrupt(0xFF); if (!multitaskingEnabled || (deadProcess == kernelProc)) strcpy(tmpMsg, "The kernel has experienced a fatal exception"); // Get the dead process else if (deadProcess == NULL) // We have to make an error here. We can't return to the program // that caused the exception, and we can't tell the multitasker // to kill it. We'd better make a kernel panic. kernelPanic("Exception handler unable to determine current process"); else sprintf(tmpMsg, "Process \"%s\" caused a fatal exception", deadProcess->processName); if (multitaskingEnabled) { if (deadProcess->taskStateSegment.EIP >= KERNEL_VIRTUAL_ADDRESS) { char *symbolName = NULL; if (kernelSymbols) { // Find roughly the kernel function where the exception // happened for (count = 0; count < kernelNumberSymbols; count ++) { if ((deadProcess->taskStateSegment.EIP >= kernelSymbols[count].address) && (deadProcess->taskStateSegment.EIP < kernelSymbols[count + 1].address)) { symbolName = kernelSymbols[count].symbol; break; } } } if (symbolName) sprintf(tmpMsg, "%s in function %s", tmpMsg, symbolName); else sprintf(tmpMsg, "%s at kernel address %08x", tmpMsg, deadProcess->taskStateSegment.EIP); } else sprintf(tmpMsg, "%s at application address %08x", tmpMsg, deadProcess->taskStateSegment.EIP); } if (kernelProcessingInterrupt) sprintf(tmpMsg, "%s while processing interrupt %d", tmpMsg, kernelPicGetActive()); if (!multitaskingEnabled || (deadProcess == kernelProc))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -