使用队列进行任务切换

时间:2012-06-13 12:11:39

标签: c scheduling multitasking osdev

我正在开发自己的业余爱好操作系统,现在我遇到了调度程序/任务切换问题。

我计划使用FIFO队列作为结构来保存进程。我使用链表实现了它。

我还决定使用iret方法从一个任务切换到另一个任务(所以当操作系统在iret之前提供中断请求时,我会更改ESP寄存器以便移动到新任务)。

但我有一个问题。 当操作系统启动时,它会启动两个任务:

  • 空闲

这两个我没有问题。 但是,如果我尝试启动另外两个任务(内置一个简单的printf),则任务队列已损坏。

如果之后我尝试打印队列,它只打印两个刚刚创建的任务,并且空闲和shell消失了,但是os继续工作(我认为在特定时刻新的esp字段任务被替换为shell的esp内容)。

任务数据结构是:

typedef struct task_t{
    pid_t pid;  
    char name[NAME_LENGTH];
    void (*start_function)();
    task_state status;
    task_register_t *registers;
    unsigned int cur_quants;
    unsigned int eip;
    long int esp;
    unsigned int pdir;
    unsigned int ptable;
    struct task_t *next;
}task_t;

并且tss是:

typedef struct {    
        unsigned int edi;   //+0
        unsigned int esi;   //+1
        unsigned int ebp;  //+2
        unsigned int esp;  //+3 (can be null)
        unsigned int ebx;  //+4
        unsigned int edx;  //+5
        unsigned int ecx;  //+6
        unsigned int eax;  //+7       
        unsigned int eip;  //+8
        unsigned int cs;   //+9
        unsigned int eflags;  //+10
        unsigned int end;           
} task_register_t;

调度程序功能如下:

void schedule(unsigned int *stack){
    asm("cli");
    if(active == TRUE){
      task_t* cur_task = dequeue_task();      
      if(cur_task != NULL){
        cur_pid = cur_task->pid;
        dbg_bochs_print("@@@@@@@");
        dbg_bochs_print(cur_task->name);
        if(cur_task->status!=NEW){
          cur_task->esp=*stack;
        } else {
          cur_task->status=READY;         
          ((task_register_t *)(cur_task->esp))->eip = cur_task->eip;                  
        }
        enqueue_task(cur_task->pid, cur_task);
        cur_task=get_task();
        if(cur_task->status==NEW){
          cur_task->status=READY;
        }
        dbg_bochs_print(" -- ");
        dbg_bochs_print(cur_task->name);
        dbg_bochs_print("\n");        
        //load_pdbr(cur_taskp->pdir);
        *stack = cur_task->esp;
      } else {
        enqueue_task(cur_task->pid, cur_task);
      }
    }
    active = FALSE;
    return;
    asm("sti");
}

使用以下值初始化tss:

void new_tss(task_register_t* tss, void (*func)()){
    tss->eax=0;    
    tss->ebx=0;
    tss->ecx=0;
    tss->edx=0;
    tss->edi =0;
    tss->esi =0;
    tss->cs = 8;
    tss->eip = (unsigned)func;
    tss->eflags = 0x202;
    tss->end = (unsigned) suicide;
    //tss->fine = (unsigned)end; //per metterci il suicide  
    return;
}

创建新任务的功能如下:

pid_t new_task(char *task_name, void (*start_function)()){
    asm("cli"); 
    task_t *new_task;
    table_address_t local_table;
    unsigned int new_pid = request_pid();   
    new_task = (task_t*)kmalloc(sizeof(task_t));    
    strcpy(new_task->name, task_name);
    new_task->next = NULL;
    new_task->start_function = start_function;
    new_task->cur_quants=0;
    new_task->pid = new_pid;
    new_task->eip = (unsigned int)start_function;
    new_task->esp = (unsigned int)kmalloc(STACK_SIZE) + STACK_SIZE-100;
    new_task->status = NEW;
    new_task->registers = (task_register_t*)new_task->esp;
    new_tss(new_task->registers, start_function);
    local_table = map_kernel();
    new_task->pdir = local_table.page_dir;
    new_task->ptable = local_table.page_table;
    //new_task->pdir = 0;
    //new_task->ptable = 0;
    enqueue_task(new_task->pid, new_task);
    //(task_list.current)->cur_quants = MAX_TICKS;          
    asm("sti");
    return new_pid;
}

我确信我只是忘记了一些事情,或者我错过了一些考虑。但我无法想象我错过了什么。

实际上我只在内核模式下工作,并且在相同的地址空间内(启用了pagiing,但实际上我对所有任务使用相同的pagedir)。

这里定义了ISR宏: https://github.com/inuyasha82/DreamOs/blob/master/include/processore/handlers.h

我声明了四种功能以处理ISR:

  1. 异常
  2. EXCEPTION_EC(错误代码例外)
  3. IRQ
  4. SYSCALL
  5. 显然,调度程序由IRQ例程调用,因此宏看起来像:

    __asm__("INT_"#n":"\
            "pushad;" \
            "movl %esp, %eax;"\
        "pushl %eax;"\
            "call _irqinterrupt;"\
            "popl %eax;"\
            "movl %eax, %esp;"\
            "popad;"\
            "iret;")
    

    irq处理函数是:

    void _irqinterrupt(unsigned int esp){
        asm("cli;");
        int irqn;
        irqn = get_current_irq();  
        IRQ_s* tmpHandler; 
        if(irqn>=0) {               
            tmpHandler = shareHandler[irqn];        
            if(tmpHandler!=0) {
                tmpHandler->IRQ_func();         
                #ifdef DEBUG
                    printf("2 - IRQ_func: %d, %d\n", tmpHandler->IRQ_func, tmpHandler);
                #endif
                while(tmpHandler->next!=NULL) {
                    tmpHandler = tmpHandler->next;                           
                    #ifdef DEBUG
                        printf("1 - IRQ_func (_prova): %d, %d\n", tmpHandler->IRQ_func, tmpHandler);
                    #endif
                    if(tmpHandler!=0) tmpHandler->IRQ_func();
                }
          } else printf("irqn: %d\n", irqn);
        }
        else printf("IRQ N: %d E' arrivato qualcosa che non so gestire ", irqn);
        if(irqn<=8 && irqn!=2) outportb(MASTER_PORT, EOI);
        else if(irqn<=16 || irqn==2){     
          outportb(SLAVE_PORT, EOI);
          outportb(MASTER_PORT, EOI);
        }
    
        schedule(&esp);
        asm("sti;");
        return;
    }
    

    这些是enqueue_task和dequeue_task函数:

    void enqueue_task(pid_t pid, task_t* n_task){
      n_task->next=NULL;
      if(task_list.tail == NULL){
        task_list.head = n_task;
        task_list.tail = task_list.head;    
      } else {
        task_list.head->next=n_task;    
        task_list.head = n_task;    
      }
    }
    
    task_t* dequeue_task(){
        if(task_list.head==NULL){
          return NULL;
        } else {
          task_t* _task;
          _task = task_list.tail; 
          task_list.tail=_task->next;
          return _task;
        }
        return;
    }
    

    提前致谢, 如果您需要更多详细信息,请告诉我们!

1 个答案:

答案 0 :(得分:1)

很难说。你的装配部分如何看起来像?让我想到的是问题(因为你可以保存和恢复两个任务而不是更多)是你没有正确推送和弹出所有寄存器。你确实使用pusha和popa作为isr吗?

我还想补充一点,就像你在那里做cli和sti一样,可能会有危险。在你的isrs中设置cli作为第一个操作码。然后你根本不需要使用sti,因为iret会自动为你打开它(实际上它在eflags寄存器中有点)。

祝你好运!