Cooperative Multitasking: Difference between revisions

From OSDev Wiki
Jump to navigation Jump to search
m (actually fix grammAr (how ironic))
(This is co-operative multi-tasking, not pre-emptive. Changed references to "preempt" to "yield".)
Line 2: Line 2:
{{In_Progress}}
{{In_Progress}}


In this tutorial, we'll cover the creation of a kernel-level multitasking system. That is, kernel threads and processes. The code here is specific to the IA-32 architecture.
In this tutorial, we'll cover the creation of a (追記) co-operative (not pre-emptive) (追記ここまで)kernel-level multitasking system. That is, kernel threads and processes. The code here is specific to the IA-32 architecture.


== Requirements ==
== Requirements ==
Line 29: Line 29:
extern void createTask(Task*, void(*)(), uint32_t, uint32_t*);
extern void createTask(Task*, void(*)(), uint32_t, uint32_t*);


extern void (削除) preempt (削除ここまで)(); // Switch task frontend
extern void (追記) yield (追記ここまで)(); // Switch task frontend
extern void switchTask(Registers *old, Registers *new); // The function which actually switches
extern void switchTask(Registers *old, Registers *new); // The function which actually switches


Line 36: Line 36:


== ''task.c'' ==
== ''task.c'' ==
This file defines the actual wrappers that ''switchTask()'' uses, ''createTask()'' and ''(削除) preempt (削除ここまで)()'':
This file defines the actual wrappers that ''switchTask()'' uses, ''createTask()'' and ''(追記) yield (追記ここまで)()'':
<source lang="c">
<source lang="c">
#include "task.h"
#include "task.h"
Line 46: Line 46:
static void otherMain() {
static void otherMain() {
printk("Hello multitasking world!"); // Not implemented here...
printk("Hello multitasking world!"); // Not implemented here...
(削除) preempt (削除ここまで)();
(追記) yield (追記ここまで)();
}
}


Line 75: Line 75:
}
}


void (削除) preempt (削除ここまで)() {
void (追記) yield (追記ここまで)() {
Task *last = runningTask;
Task *last = runningTask;
runningTask = runningTask->next;
runningTask = runningTask->next;
Line 148: Line 148:
void doIt() {
void doIt() {
printk("Switching to otherTask... \n");
printk("Switching to otherTask... \n");
(削除) preempt (削除ここまで)();
(追記) yeild (追記ここまで)();
printk("Returned to mainTask!\n");
printk("Returned to mainTask!\n");
}
}

Revision as of 20:29, 8 August 2017

Difficulty level
Difficulty 2.png
Medium

This page is under construction! This page or section is a work in progress and may thus be incomplete. Its content may be changed in the near future.

In this tutorial, we'll cover the creation of a co-operative (not pre-emptive) kernel-level multitasking system. That is, kernel threads and processes. The code here is specific to the IA-32 architecture.

Requirements

task.h

This header defines the function and types used in here. Its code is

#ifndef __TASK_H__
#define __TASK_H__
#include<stdint.h>
externvoidinitTasking();
typedefstruct{
uint32_teax,ebx,ecx,edx,esi,edi,esp,ebp,eip,eflags,cr3;
}Registers;
typedefstructTask{
Registersregs;
structTask*next;
}Task;
externvoidinitTasking();
externvoidcreateTask(Task*,void(*)(),uint32_t,uint32_t*);
externvoidyield();// Switch task frontend
externvoidswitchTask(Registers*old,Registers*new);// The function which actually switches
#endif /* __TASK_H__ */

task.c

This file defines the actual wrappers that switchTask() uses, createTask() and yield():

#include"task.h"
staticTask*runningTask;
staticTaskmainTask;
staticTaskotherTask;
staticvoidotherMain(){
printk("Hello multitasking world!");// Not implemented here...
yield();
}
voidinitTasking(){
// Get EFLAGS and CR3
asmvolatile("movl %%cr3, %%eax; movl %%eax, %0;":"=m"(mainTask.regs.cr3)::"%eax");
asmvolatile("pushfl; movl (%%esp), %%eax; movl %%eax, %0; popfl;":"=m"(mainTask.regs.eflags)::"%eax");
createTask(&otherTask,otherMain,mainTask.regs.eflags,(uint32_t*)mainTask.regs.cr3);
mainTask.next=&otherTask;
otherTask.next=&mainTask;
runningTask=&mainTask;
}
voidcreateTask(Task*task,void(*main)(),uint32_tflags,uint32_t*pagedir){
task->regs.eax=0;
task->regs.ebx=0;
task->regs.ecx=0;
task->regs.edx=0;
task->regs.esi=0;
task->regs.edi=0;
task->regs.eflags=flags;
task->regs.eip=(uint32_t)main;
task->regs.cr3=(uint32_t)pagedir;
task->regs.esp=(uint32_t)allocPage()+0x1000;// Not implemented here
task->next=0;
}
voidyield(){
Task*last=runningTask;
runningTask=runningTask->next;
switchTask(&last->regs,&runningTask->regs);
}

switch.S

This is the file that actually changes between tasks. It defines a function, switchTask(), which does all the magic. It saves all registers to from and loads them from to. It is trickier than you might think. Its function prototype is

voidswitchTask(Registers*from,Registers*to);

Its code is

.section.text
.globalswitchTask
switchTask:
pusha
pushf
mov%cr3,%eax#Push CR3
push%eax
mov44(%esp),%eax#The first argument, where to save
mov%ebx,4(%eax)
mov%ecx,8(%eax)
mov%edx,12(%eax)
mov%esi,16(%eax)
mov%edi,20(%eax)
mov36(%esp),%ebx#EAX
mov40(%esp),%ecx#IP
mov20(%esp),%edx#ESP
add4ドル,%edx#Remove the return value ;)
mov16(%esp),%esi#EBP
mov4(%esp),%edi#EFLAGS
mov%ebx,(%eax)
mov%edx,24(%eax)
mov%esi,28(%eax)
mov%ecx,32(%eax)
mov%edi,36(%eax)
pop%ebx#CR3
mov%ebx,40(%eax)
push%ebx#Goodbye again ;)
mov48(%esp),%eax#Now it is the new object
mov4(%eax),%ebx#EBX
mov8(%eax),%ecx#ECX
mov12(%eax),%edx#EDX
mov16(%eax),%esi#ESI
mov20(%eax),%edi#EDI
mov28(%eax),%ebp#EBP
push%eax
mov36(%eax),%eax#EFLAGS
push%eax
popf
pop%eax
mov24(%eax),%esp#ESP
push%eax
mov40(%eax),%eax#CR3
mov%eax,%cr3
pop%eax
push%eax
mov32(%eax),%eax#EIP
xchg(%esp),%eax#We do not have any more registers to use as tmp storage
mov(%eax),%eax#EAX
ret#This ends all!

Doing it

Put this on some source file

#include"task.h"
voiddoIt(){
printk("Switching to otherTask... \n");
yeild();
printk("Returned to mainTask!\n");
}

Now, from your kernel_main() call doIt()!

Congratulations! You've just implement kernel multitasking! If you want to call preempt() from your IRQ #0 handler, you should modify switchTask() to work with interrupts and iret.

Retrieved from "https://wiki.osdev.org/index.php?title=Cooperative_Multitasking&oldid=21057"