📄 kernelmultitasker.c
字号:
// there's simply nothing to do. However, if there is some process // that is in a 'waiting' state, then there will be stuff to do when // it wakes up. if (nextProcess == NULL) { // Resume the loop schedulerSwitchedByCall = 1; continue; } // Update some info about the next process nextProcess->waitTime = 0; nextProcess->state = proc_running; // Export (to the rest of the multitasker) the pointer to the // currently selected process. kernelCurrentProcess = nextProcess; // Set the system timer 0 to interrupt this task after a known // period of time (mode 0) status = kernelSysTimerSetupTimer(0, 0, TIME_SLICE_LENGTH); if (status < 0) { kernelError(kernel_warn, "The scheduler was unable to control " "the system timer"); // Shut down the scheduler. schedulerShutdown(); // Just in case kernelProcessorStop(); } // In the final part, we do the actual context switch. // Acknowledge the timer interrupt if one occurred if (!schedulerSwitchedByCall) kernelPicEndOfInterrupt(INTERRUPT_NUM_SYSTIMER); // Reset the "switched by call" flag schedulerSwitchedByCall = 0; // Move the selected task's selector into the link field // cast required! Davide Airaghi ((kernelTSS *)(schedulerProc->taskStateSegment))->oldTSS = nextProcess->tssSelector; // Return to the task. Do an interrupt return. kernelProcessorIntReturn(); // Continue to loop } // If we get here, then the scheduler is supposed to shut down schedulerShutdown(); // We should never get here return (status = 0);}static int schedulerInitialize(void){ // This function will do all of the necessary initialization for the // scheduler. Returns 0 on success, negative otherwise // 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 // cast required! Davide Airaghi ((kernelTSS *)(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. // cast required! Davide Airaghi ((kernelTSS *)(schedulerProc->taskStateSegment))->EFLAGS = 0x00004002; // Get a page directory // cast required! Davide Airaghi ((kernelTSS *)(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; // Clear 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", KERNEL_PRIORITY, 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 // cast required! Davide Airaghi ((kernelTSS *)(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;}static int fpuExceptionHandler(void){ // This function gets called when a EXCEPTION_DEVNOTAVAIL (7) exception // occurs. It can happen under two circumstances: // CR0[EM] is set: No FPU is present. We can implement emulation here // later in this case, if we want. // CR0[TS] and CR0[MP] are set: A task switch has occurred since the // last FP operation, and we need to restore the state. int status = 0; if (fpuProcess == kernelCurrentProcess->processId) // This process was the last to use the FPU. Just clear the task // switched bit kernelProcessorClearTaskSwitched(); else { // Some other process has been using the FPU, or it has not been // used at all. Figure out whether we should restore the state // or else initialize the FPU for this process. if (kernelCurrentProcess->fpuStateValid) // Restore the FPU state kernelProcessorFpuStateRestore(kernelCurrentProcess->fpuState); else { // The process has not previously used the FPU. Initialize it // and remember that from now on we have to save FPU state. kernelProcessorFpuInit(); kernelCurrentProcess->fpuInUse = 1; } fpuProcess = kernelCurrentProcess->processId; } return (status = 0);}// set the permission (allow: perm = PORT_VAL_BIT_TRUE; not allow: perm = PORT_VAL_BIT_FALSE )// of a process to have or not to have IO perm on the specified port// return: 0 = ok, < 0 = not ok// Davide Airaghistatic int kernelMultitaskerSetIOperm(int processId, unsigned int portNum,unsigned perm) { int status = 0; kernelProcess *Process = NULL; void * newTSS = NULL; unsigned int newTSSsize = 0; int interrupts = 0; unsigned char * p = NULL; char a = 10 , b = 11; // Make sure multitasking has been enabled if (!multitaskingEnabled) return (status = ERR_NOTINITIALIZED); Process = getProcessById(processId); if (Process == NULL) { // There's no such process kernelError(kernel_error, "No process %d to set IOPerm", processId); return (status = ERR_NOSUCHPROCESS); } if (portNum >= IO_PORTS) return (status = ERR_BOUNDS); if (portNum > Process->max_io_port && perm == PORT_VAL_BIT_FALSE) { // next for debug // kernelError(kernel_error,"p# %d >? maxp# %d",portNum,Process->max_io_port); return 0; } // for Debug //kernelError(kernel_error,"TSS is %d bytes",Process->TSSsize); if ((portNum > Process->max_io_port) && perm == PORT_VAL_BIT_TRUE) { // TSS isn't big enough to store IOMap ... we have to do something! // Allocate data for TSS! newTSSsize = sizeof(kernelTSS)+(sizeof(unsigned char)*(IOMAP_REQUESTED_SIZE(portNum)-1)); newTSS = kernelMalloc(newTSSsize); if (newTSS == NULL) { return (ERR_MEMORY); } kernelMemClear((void *)newTSS, newTSSsize); kernelMemCopy(Process->taskStateSegment,newTSS,Process->TSSsize); // OK, now the very critical part of the routine ... // disable ints for precaution kernelProcessorSuspendInts(interrupts); // Fill in the process' Task State Segment descriptor status = kernelDescriptorSet( Process->tssSelector, // TSS selector number newTSS, // Starts at... newTSSsize, // Limit of a TSS segment 1, // Present in memory PRIVILEGE_SUPERVISOR, // TSSs are supervisor privilege level 0, // TSSs are system segs 0xB, // TSS, 32-bit, busy 0, // 0 for SMALL size granularity 0); // Must be 0 in TSS
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -