线程和上下文切换C++
我需要帮助理解下面的代码。它是用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;
}
这看起来像是一个在很老的CPU上实现的自制任务调度器。你可能需要一个非常老的系统来运行它。
在高电平时,代码的功能如下:
- PCB块只是每个线程的性能。它们保持2个寄存器片的值(线程花费在CPU上多长时间)并标记它是否完成
- 当你创建一个新的线程(进程)时,这个结构被初始化,但没有其他事情发生
- 只要时间阈值过去,timerInterrupt就会被CPU自动触发。在看到中断时,中断处理程序通过调用CPU中断来分析结构并切换到适当的线程。
这是可怕的代码(老是没有借口)。无论如何,timerISR
每隔一段时间就会触发一次,并切换到returnNextThread
(基本上是调度程序)所确定的相应线程。
finishThread
显然通过标记完成并强制上下文切换来结束一个线程。哪一部分不清楚?
initNewRoutine
和returnOldRoutine
只是安装和卸载定时器ISR(不幸的命名)。
谢谢。但是,如何在特定时间后调用timerISR? 而且,这个汇编代码是用于中断向量的地址操作的? – johnny94
这已经由DOS设置了,代码只是将该向量替换为指向'timerISR'而不是DOS处理程序。它在'int 60h'处复制旧的处理程序并链接到它。 – Jester
@ johnny94曾经是PC板上的[8253/8254芯片](http://wiki.osdev.org/Programmable_Interval_Timer)(不知道它是否还在使用,我想它可能是现代芯片组的模拟),它每当CPU计数器达到零时(它在〜1.9MHz IIRC处滴答),CPU就设置IRQ(中断请求)引脚。如果中断没有被禁止('CLI/STI'指令),那么CPU检测到IRQ将在内存开始时检查中断表,并根据它执行下一条指令,有效执行中断处理程序,而不是继续下一个应用程序的指令。 – Ped7g
小心你从这段代码中取得的。 C++看起来不再像这样了。 – user4581301
@ user4581301我知道它不再是这个样子,但我处于这种情况,我需要使用它,因为它是 – johnny94
哦,我的。谁在课堂上做这件事是真正的虐待狂。 – SergeyA