2017-08-01 170 views
0

我需要幫助理解下面的代碼。它是用C++編寫的線程工作和上下文切換的例子(BC31編譯器)。線程和上下文切換C++

我明白爲什麼PCB存在爲了切換上下文(尤其是保持SS和SP寄存器),並且我也明白它通過使用這個程序可以使 返回到無中斷的地步發生了。關於代碼,我明白了什麼是DISABLE_INTERRUPT和ENABLE_INTERRUPT用於 (忽略代碼中某些敏感部分的中斷)。我也理解createProcess函數,其中創建每個線程的本地堆棧,並將線程的標誌,段和偏移量寫入其中。在函數timerISR中,我理解上下文切換正在完成的部分(寫入和讀取SS和SP)。

對於其他代碼,我不能說我明白它。函數returnNextThread(),initNewRoutine(),returnOldRoutine(),finishThread()是什麼點?

不過,我最感興趣的是如何被concurently運行這個程序,實際發生的出現使這三個函數交錯運行時...

如何這一切工作? 我真的很感激一個簡單的解釋。

#include<stdio.h> 
#include<stdlib.h> 
#include<iostream.h> 
#include<dos.h> 

typedef struct PCB_struct { 
    unsigned ss; 
    unsigned sp; 
    unsigned finished; 
    unsigned quant; 
} PCB; 

#define DISABLE_INTERRUPT asm cli 
#define ENABLE_INTERRUPT asm sti 
PCB *threads[4]; 
volatile unsigned addressOfInterruptVector = 0x08; 
volatile unsigned adressOfFreePlaceForInterrupt = 0x60; 
volatile unsigned numberOfInterrupts=0; 
volatile PCB *activeThread; 
volatile unsigned activeThreadNumber=0; 
volatile unsigned numberOfFinishedThreads=0; 
volatile int necessarilyContextSwitch=0; 

PCB* returnNextThread() { 
    if(activeThreadNumber==0) { 
     if(threads[1]->finished==0) { 
      activeThreadNumber=1; 
      return threads[1]; 
     } 
     else if(threads[2]->finished==0) { 
      activeThreadNumber=2; 
      return threads[2]; 
     } 
     else if(threads[3]->finished==0) { 
      activeThreadNumber=3; 
      return threads[3]; 
     } 
     else { 
      activeThreadNumber=0; 
      return threads[0]; 
     } 
    } 
    else if(activeThreadNumber==1) { 
     if(threads[2]->finished==0) { 
      activeThreadNumber=2; 
      return threads[2]; 
     } 
     else if(threads[3]->finished==0) { 
      activeThreadNumber=3; 
      return threads[3]; 
     } 
     else { 
      activeThreadNumber=0; 
      return threads[0]; 
     } 
    } 
    else if(activeThreadNumber==2) { 
     if(threads[1]->finished==0) { 
      activeThreadNumber=1; 
      return threads[1]; 
     } 
     else if(threads[3]->finished==0) { 
      activeThreadNumber=3; 
      return threads[3]; 
     } 
     else { 
      activeThreadNumber=0; 
      return threads[0]; 
     } 
    } 
    else if(activeThreadNumber==3) { 
     if(threads[2]->finished==0) { 
      activeThreadNumber=2; 
      return threads[2]; 
     } 
     else if(threads[1]->finished==0) { 
      activeThreadNumber=1; 
      return threads[1]; 
     } 
     else { 
      activeThreadNumber=0; 
      return threads[0]; 
     } 
    } 
    activeThreadNumber=0; 
    return threads[0]; 
} 

unsigned tmpSs=0; 
unsigned tmpSp=0; 
void interrupt timerISR() { 
    if(!necessarilyContextSwitch) numberOfInterrupts--; 
    if(numberOfFinishedThreads<3 && (numberOfInterrupts==0 || necessarilyContextSwitch==1)) { 
     asm { 
      mov tmpSs,ss 
      mov tmpSp,sp 
     } 
     activeThread->ss=tmpSs; 
     activeThread->sp=tmpSp; 
     activeThread=returnNextThread(); 
     tmpSs=activeThread->ss; 
     tmpSp=activeThread->sp; 
     numberOfInterrupts=activeThread->quant; 
     asm { 
      mov ss,tmpSs 
      mov sp,tmpSp 
     } 
    } 
    if(!necessarilyContextSwitch) asm int 60h; 
    necessarilyContextSwitch=0; 
} 

unsigned oldRoutineOffset, oldRoutineSegment; 

void initNewRoutine() { 
    unsigned offsetAddress=addressOfInterruptVector*4; 
    unsigned segmentAddress=addressOfInterruptVector*4+2; 
    unsigned emptyOffset=adressOfFreePlaceForInterrupt*4; 
    unsigned emptySegment=adressOfFreePlaceForInterrupt*4+2; 
    DISABLE_INTERRUPT 
    asm { 
     push es 
     push ax 
     push di 
     mov ax,0 
     mov es,ax 

     mov di, word ptr segmentAddress 
     mov ax, word ptr es:di 
     mov word ptr oldRoutineSegment, ax 
     mov word ptr es:di, seg timerISR 

     mov di, word ptr offsetAddress 
     mov ax, word ptr es:di 
     mov word ptr oldRoutineOffset, ax 
     mov word ptr es:di, offset timerISR 

     mov di, word ptr emptyOffset 
     mov ax, word ptr oldRoutineOffset 
     mov word ptr es:di, ax 
     mov di, word ptr emptySegment 
     mov ax, word ptr oldRoutineSegment 
     mov word ptr es:di, ax 

     pop di 
     pop ax 
     pop es 
    } 
    ENABLE_INTERRUPT 
} 

void returnOldRoutine() { 
    unsigned offsetAddress=addressOfInterruptVector*4; 
    unsigned segmentAddress=addressOfInterruptVector*4+2; 
    DISABLE_INTERRUPT 
    asm { 
     push es 
     push ax 
     push di 

     mov ax,0 
     mov es,ax 

     mov di, word ptr segmentAddress 
     mov ax, word ptr oldRoutineSegment 
     mov word ptr es:di, ax 

     mov di, word ptr offsetAddress 
     mov ax, word ptr oldRoutineOffset 
     mov word ptr es:di, ax 

     pop di 
     pop ax 
     pop es 
    } 
    ENABLE_INTERRUPT 
} 

int finishThread() { 
    necessarilyContextSwitch=1; 
    DISABLE_INTERRUPT 
    activeThread->finished=1; 
    cout << "Thread " << activeThreadNumber << " finished." << endl; 
    ENABLE_INTERRUPT 
    timerISR(); 
    return 0; 
} 

void function1() { 
    for(int i=0;i<30;i++) { 
     cout << "Execution: function 1: " << i << endl; 
     for(int j=0;j<10000;j++) { 
      for(int k=0;k<30000;k++); 
     } 
    } 
    finishThread(); 
} 

void function2() { 
    for(int i=0;i<30;i++) { 
     cout << "Execution: function 2: " << i << endl; 
     for(int j=0;j<10000;j++) { 
      for(int k=0;k<30000;k++); 
     } 
    } 
    finishThread(); 
} 

void function3() { 
    for(int i=0;i<30;i++) { 
     cout << "Execution: function 3: " << i << endl; 
     for(int j=0;j<10000;j++) { 
      for(int k=0;k<30000;k++); 
     } 
    } 
    finishThread(); 
} 

void createProcess(PCB *block, void (*method)()) { 
    unsigned* st1 = new unsigned[1024]; 

    st1[1023] = 0x200;   
    st1[1022] = FP_SEG(method); 
    st1[1021] = FP_OFF(method); 

    block->sp = FP_OFF(st1+1012); 
    block->ss = FP_SEG(st1+1012); 
    block->finished=0; 
} 

void mainThread() { 

    for(int i=0;i<30;i++) { 
     DISABLE_INTERRUPT 
     cout << "Main Thread: " << i << endl; 
     ENABLE_INTERRUPT 
     for(int j=0;j<30000;j++) { 
      for(int k=0;k<30000;k++); 
     } 
    } 
} 

int main() { 
    DISABLE_INTERRUPT 
    threads[1]=new PCB(); 
    createProcess(threads[1], function1); 
    threads[1]->quant=20; 

    threads[2]=new PCB(); 
    createProcess(threads[2], function2); 
    threads[2]->quant=40; 

    threads[3]=new PCB(); 
    createProcess(threads[3], function3); 
    threads[3]->quant=20; 

    threads[0]=new PCB(); 
    activeThread=threads[0]; 
    activeThreadNumber=0; 
    activeThread->quant=20; 
    numberOfInterrupts=activeThread->quant; 
    ENABLE_INTERRUPT 
    initNewRoutine(); 
    mainThread(); 
    returnOldRoutine(); 
    cout << "Main program finished." << endl; 
    return 0; 
} 
+0

小心你從這段代碼中取得的。 C++看起來不再像這樣了。 – user4581301

+0

@ user4581301我知道它不再是這個樣子,但我處於這種情況,我需要使用它,因爲它是 – johnny94

+2

哦,我的。誰在課堂上做這件事是真正的虐待狂。 – SergeyA

回答

2

這看起來像是一個在很老的CPU上實現的自制任務調度器。你可能需要一個非常老的系統來運行它。

在高電平時,代碼的功能如下:

  • PCB塊只是每個線程的性能。它們保持2個寄存器片的值(線程花費在CPU上多長時間)並標記它是否完成
  • 當你創建一個新的線程(進程)時,這個結構被初始化,但沒有其他事情發生
  • 只要時間閾值過去,timerInterrupt就會被CPU自動觸發。在看到中斷時,中斷處理程序通過調用CPU中斷來分析結構並切換到適當的線程。
4

這是可怕的代碼(老是沒有藉口)。無論如何,timerISR每隔一段時間就會觸發一次,並切換到returnNextThread(基本上是調度程序)所確定的相應線程。

finishThread顯然通過標記完成並強制上下文切換來結束一個線程。哪一部分不清楚?

initNewRoutinereturnOldRoutine只是安裝和卸載定時器ISR(不幸的命名)。

+0

謝謝。但是,如何在特定時間後調用timerISR? 而且,這個彙編代碼是用於中斷向量的地址操作的? – johnny94

+2

這已經由DOS設置了,代碼只是將該向量替換爲指向'timerISR'而不是DOS處理程序。它在'int 60h'處複製舊的處理程序並鏈接到它。 – Jester

+1

@ johnny94曾經是PC板上的[8253/8254芯片](http://wiki.osdev.org/Programmable_Interval_Timer)(不知道它是否還在使用,我想它可能是現代芯片組的模擬),它每當CPU計數器達到零時(它在〜1.9MHz IIRC處滴答),CPU就設置IRQ(中斷請求)引腳。如果中斷沒有被禁止('CLI/STI'指令),那麼CPU檢測到IRQ將在內存開始時檢查中斷表,並根據它執行下一條指令,有效執行中斷處理程序,而不是繼續下一個應用程序的指令。 – Ped7g