2017-07-12 11 views
3

私は、x86-64上でC++およびインラインアセンブリ(AT & T)を使用してコンテキスト切り替えを実装しようとしています。
同じ関数のコンテキストを保存して再読み込みすると正しく動作しているようです。
しかし、関数を生成しようとすると、2番目の関数コンテキストをロードしようとした後にGDBを使ってseg fault/corruptスタックが返されました。例えばコンテキストスイッチの実装 - 2番目の関数が再実行されない

は、それが
PRINT1
PRINT2
PRINT1
//壊れスタックを印刷し、私はそれをリロードする前に、スレッド1(第一機能)のコンテキストを保存した場合のプログラムは、しかし

を実行を停止します問題はありません。例えば

は、それが
PRINT1
PRINT1
PRINT1

は、私がコンテキスト保存とスタック用のメモリ空間を作成しています印刷します。コンテキストを保存するとき、スタックポインタとベースポインタは構造体に保存されます。その後、スタックポインタはコンテキスト値をプッシュするためにコンテキストメモリをポイントします。

私は何が壊れたスタックの原因を知りたいのですが、なぜ2番目の関数のコンテキストを読み込めないのですか?可能であれば、私のコード内のエラーを指摘するのを手伝ってください。ありがとうございました!あなたは非早期クロバー出力への書き込み後に入力を読み込むよう

#include <stdio.h> 
#include <iostream> 
#include <vector> 
#include <map> 
#include <memory> 
#include <algorithm> 
namespace CORO 
{ 
using ThreadID = unsigned; 
static int thread_count; 

enum STATE 
{ 
    READY, 
    ACTIVE, 
    WAITING, 
    ENDED 
}; 

struct thd_data 
{ 
    int parent_ID = 0; 
    int id = 0; 
    STATE state = READY; 

    int * stack_mem; 
    void * stackptr; 
    void * stackbp; 
    void*(*funcptr)(void*); 
    void * param = nullptr; 
    int * context_mem; 
    int * context_sp; 

    thd_data() 
    :stack_mem{new int[1024]}, context_mem{new int[1024]} 
    { 

    } 

    thd_data(const thd_data & rhs) 
    :stack_mem{new int[1024]}, context_mem{new int[1024]} 
    { 

    } 

    thd_data & operator=(const thd_data & rhs) 
    { 

    } 
}; 

static thd_data* curr_thd; 

std::map<int, std::shared_ptr<thd_data>> threadmap; 
std::vector<int>activeListID; 

// Returns a pointer to next thread 
thd_data * FindNextThread() 
{ 
    int new_id; 
    for(const auto & elem : activeListID) 
    { 
    if(elem != curr_thd->id) 
    { 
     new_id = elem; 
     break; 
    } 
    } 
    auto threadmap_elem = threadmap.find(new_id); 
    if(threadmap_elem != threadmap.end()) 
    { 
    return &(*threadmap_elem->second); 
    } 
    else 
    { 
    return nullptr; 
    } 
} 

void thd_init() 
{ 
    threadmap[0] = std::make_shared<thd_data>(); 
    auto main_thd = threadmap.find(0)->second; 

    main_thd->state = ACTIVE; 
    main_thd->id = 0; 
    main_thd->param = nullptr; 
    main_thd->funcptr = nullptr; 
    activeListID.push_back(main_thd->id); 

    curr_thd = &(*main_thd); 
} 

ThreadID new_thd(void*(*func)(void*), void *param) 
{ 
    thread_count += 1; // increment counter 

    threadmap[thread_count] = std::make_shared<thd_data>(); 
    auto thd = threadmap.find(thread_count)->second; 

    thd->state = READY; 
    thd->id = thread_count; 
    activeListID.push_back(thd->id); 

    thd->stackptr = thd->stack_mem+1024; 
    thd->stackbp = thd->stack_mem; 
    thd->funcptr = func; 
    thd->param = param; 
    return thd->id; 
} 

void thd_yield() 
{ 
    // Find the next ready thread 
    thd_data* thd = FindNextThread(); 

    if(thd == nullptr) 
    return; 

    // Move ID to the end of vector 
    activeListID.erase(std::remove(activeListID.begin(), activeListID.end(), curr_thd->id), activeListID.end()); 
    activeListID.push_back(curr_thd->id); 

    // Save context 
    { 
    asm volatile 
    (
     "movq %%rsp, %0\n\t" // save stack pointer 
     "movq %%rbp, %1\n\t" // save rbp 
     "movq %3, %%rsp\n\t" // point to context mem then push register values into it 
     "pushq %%rax\n\t" 
     "pushq %%rbx\n\t" 
     "pushq %%rcx\n\t" 
     "pushq %%rdx\n\t" 
     "pushq %%rsi\n\t" 
     "pushq %%rdi\n\t" 
     "pushq %%r8\n\t" 
     "pushq %%r9\n\t" 
     "pushq %%r10\n\t" 
     "pushq %%r11\n\t" 
     "pushq %%r12\n\t" 
     "pushq %%r13\n\t" 
     "pushq %%r14\n\t" 
     "pushq %%r15\n\t" 
     "pushfq\n\t" 
     "movq %%rsp, %2\n\t" // save rsp into context sp (end of context mem) 
     "movq %4, %%rsp\n\t" // restore stackptr into rsp 
     :"+m"(curr_thd->stackptr) 
     ,"+m"(curr_thd->stackbp)  
     ,"+m"(curr_thd->context_sp) 
     :"m"(curr_thd->context_mem) 
     ,"m"(curr_thd->stackptr)  
     :"rsp" 
    ); 
    } 

    curr_thd->state = WAITING; 
    curr_thd = thd; 

    // Calls function if thread is not running 
    if(thd->state == READY) 
    { 
    thd->state = ACTIVE; 
    thd->funcptr(thd->param); 
    } 
    else 
    { 
    // Restore context 
    { 
     asm volatile 
     (
     "movq %0, %%rbp\n\t" // restore stackbp into rbp 
     "movq %1, %%rsp\n\t" // point to context memory to pop 
     "popfq\n\t" 
     "popq %%r15\n\t" 
     "popq %%r14\n\t" 
     "popq %%r13\n\t" 
     "popq %%r12\n\t" 
     "popq %%r11\n\t" 
     "popq %%r10\n\t" 
     "popq %%r9\n\t" 
     "popq %%r8\n\t" 
     "popq %%rdi\n\t" 
     "popq %%rsi\n\t" 
     "popq %%rdx\n\t" 
     "popq %%rcx\n\t" 
     "popq %%rbx\n\t" 
     "popq %%rax\n\t" 
     "movq %2, %%rsp\n\t" // point to TCB stack pointer 
     : 
     :"m"(thd->stackbp) 
     ,"m"(thd->context_sp) 
     ,"m"(thd->stackptr) 
     :"rsp" 
    ); 
    } 
    } 
} 
} // end namespace 
void* print1(void *a) 
{ 
    int i; 
    for(i=0; i< 20; i++) 
    { 
    std::cout<<"Print1 i: "<<i<<std::endl; 
    if((i+1)%4==0) 
     CORO::thd_yield(); 
    } 
    return NULL; 
} 

void* print2(void *a) 
{ 
    int i; 
    for(i=0; i< 20; i++) 
    { 
    std::cout<<"Print2 i: "<<i<<std::endl; 
    if((i+1)%4==0) 
     CORO::thd_yield(); 
    } 
    return NULL; 
} 


int main() 
{ 
    CORO::ThreadID id; 
    CORO::thd_init(); 
    id = CORO::new_thd(print2, NULL); 
    print1(NULL); 
} 
+0

あなたは '' movq%3、%% rsp \ n \ t "'を実行したときにRSPに何があると思いますか?そして、あなたが '' movq%1、%% rsp \ n \ t "'を実行したときに起こると思われることは何ですか? –

+0

私は、RSPがmovq%3のコンテキストメモリの開始を指していると思います。 movq%1の場合、RSPはコンテキストメモリの終わりを指しています。これはコンテキストメモリの開始+プッシュした回数です。 – Aeria

答えて

0

まず、あなたの最初のasm文は未定義値でrspをoverwirtes。このような操作の場合は、出力パラメーターを読み取る必要があります。curr_thd->stackptrは入力パラメーターであってはなりません。

新しいスレッドを開始すると、コードは新しいスタックに切り替わらず、古いスレッドスタックを使用します。これはあなたのクラッシュを説明します。

2番目のasm文は、最初のasm文を残すのに適したレジスタ値をリストアしますが、2番目のasm文を残すのに適したスタック状態と命令ポインタで終了します。この結果、未定義の動作が発生します。関数が何らかの形でコピーされていると、コンテキストスイッチ関数の間違ったコピーにもなります。

GCCインラインアセンブラは、出力またはクローバーリストにないレジスタの内容を変更したり、asm文を1つ入力して別の(同じスレッド内に)残すことはできません。その結果、未定義の動作が発生します。したがって、コンテキストを保存し、それを復元することは、asmステートメントを分離することはできません。

コンテキストスイッチには、1つのアセンブリブロックを使用する必要があります。特にコンテキストスイッチの場合、インラインアセンブリを避けるのが最も簡単です。

+0

ありがとうございます。しかし、もう少し精巧にお考えですか?最初のasmステートメントでは、最初の行に書かれているようにstackptrを早期に破棄し、後で読むことが必要なのでしょうか?2番目のasm文では、最初の/ 2番目の文を残すのに適したレジスタの値とスタックはどういう意味ですか?ありがとう! – Aeria

関連する問題