1

私は自分の趣味のOSを開発していますが、スケジューラ/タスクの切り替えに関する問題に悩まされています。

プロセスを保持するための構造として FIFO キューを使用することを計画しました。リンクリストを使用して実装しました。

また、タスクから別のタスクに切り替えるために iret メソッドを使用することにしました (そのため、os が iret の直前に割り込み要求を処理していたとき、新しいタスクに移動するために ESP レジスタを変更しました)。

しかし、私には問題があります。OS が起動すると、次の 2 つのタスクが起動します。

  • アイドル
  • シェル

そして、これら2つで問題はありません。しかし、他の 2 つのタスク (単純に printf を内部に含む) を起動しようとすると、タスク キューが破損しました。

その後、キューを印刷しようとすると、作成されたばかりの2つのタスクのみが印刷され、アイドル状態でシェルが消えますが、OSは引き続き機能します(特定の瞬間に新しいタスクのespフィールドが置き換えられたと思いますシェルの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;
}

私は何かを忘れただけか、考慮事項を見逃していると確信しています。しかし、私は何が欠けているのか理解できません。

実際、私はカーネル モードで、同じアドレス空間内でのみ作業しています (ページングは​​有効になっていますが、実際にはすべてのタスクで同じ pagedir を使用しています)。

ISR マクロは、 https ://github.com/inuyasha82/DreamOs/blob/master/include/processore/handlers.h で定義されています。

ISR を処理するために、次の 4 種類の関数を宣言しました。

  1. 例外
  2. EXCEPTION_EC (エラー コード付きの例外)
  3. IRQ
  4. システムコール

明らかに、スケジューラは 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;
}

詳細が必要な場合はお知らせください。

4

1 に答える 1

1

わかりにくいです。isr の組み立て部分はどのように見えますか? 私が問題だと思うのは (2 つのタスクを保存して復元することはできますが、それ以上はできないため)、すべてのレジスタを適切にプッシュおよびポップしないことです。isr には pusha と popa を使用しますよね?

また、あなたが行ったように cli と sti を使用することは危険な場合があることも付け加えておきます。isrs で、cli を最初のオペコードとして設定します。次に、iret が自動的にこれをオンにするため、sti を使用する必要はまったくありません (実際には eflags レジスタに少し入っています)。

幸運を!

于 2012-06-14T02:39:31.397 に答える