c++ - 线程和上下文切换 C++

标签 c++ multithreading assembly x86 interrupt

我需要帮助来理解以下代码。它是一个用C++(BC31编译器)编写的线程工作和上下文切换的例子。

我理解为什么 PCB 存在是为了切换上下文(特别是为了保留 SS 和 SP 寄存器),我也理解通过使用这个程序可以 像什么都没发生一样回到被打断的地方。关于代码,我了解宏 DISABLE_INTERRUPT 和 ENABLE_INTERRUPT 的用途 (忽略代码某些敏感部分的中断)。我还了解函数 createProcess,其中为每个线程创建了本地堆栈,并且 线程的标志、段和偏移量被写入其中。在函数 timerISR 中,我了解完成上下文切换的部分(SS 和 SP 的写入和读取)。

对于其余的代码,我不能说我看懂了。函数 returnNextThread()、initNewRoutine()、returnOldRoutine()、finishThread() 有什么意义?

不过,我最感兴趣的是这个程序是如何同时运行的,那里实际发生了什么,以便这三个函数在运行时交织......

这一切是如何运作的? 我真的很感激一个简单的解释。

#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;
}

最佳答案

那是可怕的代码(老不是借口)。无论如何,timerISR 会经常触发并切换到由 returnNextThread(基本上是调度程序)确定的适当线程。

finishThread 显然通过将线程标记为已完成并强制进行上下文切换来结束线程。哪一部分不清楚?

initNewRoutinereturnOldRoutine 只是安装和卸载定时器 ISR(不幸的命名)。

关于c++ - 线程和上下文切换 C++,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45447672/

相关文章:

c++ - 使用 FFTW_MEASURE 时 FFT 输出为空白,但使用 FFTW_ESTIMATE 时可以正常工作

linux - Termux 上的汇编可执行文件现在会产生非法指令错误

c++ - 在 visual studio 2010 C++ 中构建错误

c++ - 在 std::unordered_map 中 move 插入/放置失败后恢复 move 的元素

c++ - `memory_order_relaxed` 是防止部分读取原子存储所必需的吗

java - 如何立即终止套接字 IO 操作上的线程阻塞?

c++ - RC 在多线程/多处理上下文中代表什么?

c++ - 嵌套锁和简单锁的区别

gcc - 使用 icc、gcc 和 clang 的相同实现的不同性能的原因是什么?

c - 如何从操作码/机器码中获取汇编指令或助记符?