2017-08-01 239 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