2013-03-13 51 views
1

我遇到問題。我需要實現一個使用定時器和SIGALRM來切換ucontext線程的程序,但是當我使用evict_thread函數切換線程時出現了分段錯誤。我相信這是由於在程序執行期間的不同時間發生的競爭條件造成的。這裏是我的evict_thread使用SIGALRM切換線程上下文

void evict_thread(int signal) 
{ 
// Check that there is more than one thread in the queue 
if ((int)list_length(runqueue) > 1) 
{ 
    // Remove the currently executing thread from the runqueue and store its id 
    int evict_thread_id = list_shift_int(runqueue); 

    // Place the thread at the back of the run queue 
    list_append_int(runqueue, evict_thread_id); 

    // Get the id of the thread that is now at the head of the run queue 
    int exec_thread_id = list_item_int(runqueue, 0); 

    // Set the start time for new thread to the current time 
    clock_gettime(CLOCK_REALTIME, &thread_table[exec_thread_id]->start); 

    printf("Switching context from %s to %s\n", 
     thread_table[evict_thread_id]->thread_name, 
     thread_table[exec_thread_id]->thread_name); 

    // Execute the thread at the head of the run queue 
    if (swapcontext(&thread_table[evict_thread_id]->context, &thread_table[exec_thread_id]->context) == -1) 
    { 
     perror("swapcontext failed\n"); 
     printf("errno: %d.\n", errno); 
     return; 
    } 
} 
return;  
} 

上述功能被稱爲以下方式

// Set the SIGALRM 
if (sigset(SIGALRM, evict_thread) == -1) 
{ 
    perror("sigset failed\n"); 
    printf("errno: %d.\n", errno); 
    return; 
} 

// Initialize timer 
thread_switcher.it_interval.tv_sec = 0; 
thread_switcher.it_interval.tv_usec = quantum_size; 
thread_switcher.it_value.tv_sec = 0; 
thread_switcher.it_value.tv_usec = quantum_size; 
setitimer(ITIMER_REAL, &thread_switcher, 0); 

運行隊列被簡單地認爲是指數爲指針的全局表的ucontext線程的整數的全局列表。該列表是使用來自libslack.org上的C通用實用程序庫的列表數據結構實現的。當我禁用定時器並讓每個線程在切換上下文之前運行完成時,程序運行正常,但是當切換線程時在執行期間,我會在80%左右的時間內出現分段錯誤。

另外,當我嘗試使用gdb來回溯分段錯誤時,它說它發生在系統調用中。

+0

這段代碼實際上是一個非常好的* * * *做的簡編! – paulsm4 2013-03-14 00:13:11

+0

[setitimer,SIGALRM和多線程進程(linux,c)]的可能重複(http://stackoverflow.com/questions/2586926/setitimer-sigalrm-multithread-process-linux-c) – paulsm4 2013-03-14 00:14:44

回答

0

我不能給你如何使它工作的任何建議,但這裏有什麼不工作的幾個點:異步運行有關的其他代碼

信號處理程序。例如當某些代碼更新您的runqueue,並且信號處理程序運行時,信號可能會啓動 list_append_int(runqueue, evict_thread_id); 您有一個相當嚴重的競爭條件。

printf()不應該在信號處理程序中調用,它可能會死鎖或更糟。 Here's在信號處理程序中可以安全調用的函數列表。沒有提到setcontext/swapcontext可以安全地在信號處理程序中調用,儘管它的linux手冊頁說您可以在信號處理程序中調用setcontext() - 我不確定什麼是權威性的。

還要注意什麼setcontext手冊頁()說:

在信號發生時,當前用戶上下文被保存,並通過內核的信號處理程序創建一個新的 上下文。

所以,當你發出swapcontext(),你可能會節省信號處理的情況下,而不是之前踢的信號時所運行的當前上下文。

0

請記住,信號處理異步運行你的主要代碼。 man 7 signal頁面值得仔細閱讀,以確保您遵守準則。例如,在部分Async-signal-safe-functions中沒有提及printf或其他功能,例如swapcontext。這意味着你不能可靠地從信號處理程序調用這些函數。

一般來說,儘量在信號處理程序中儘可能少做一些工作。通常這只是意味着在信號處理程序中設置sig_atomic_t類型的標誌,然後檢查主循環中該標誌的狀態。

也許重新安排您的代碼,以便上下文切換髮生在主循環中,而不是來自信號處理程序。您可能可以在主循環中使用sigwait來等待定時器信號。

0

作爲一個猜測:你正在向內核傳遞一些東西,因爲你切換了上下文。你正在問一個段錯誤,但你的代碼正在做有趣的事情。

也許如果你考慮一個更標準的線程調度模型,你可以避免這些問題。 而不是嘗試使用上下文切換來安排線程,還有其他方式來執行此操作。你可以從你的驅逐線程中調用它們,使用你完全相同的當前程序模型。

這個建議中的一些有點系統特定。如果你能告訴我們你的操作系統是什麼,我們可以找到適合你情況的東西。或者你可以自己檢查一下。

閱讀關於POSIX線程調度。要特別注意SCHED_FIFO,它可以和你的模型一起工作。

https://computing.llnl.gov/tutorials/pthreads/man/sched_setscheduler.txt

這一般適用於使用POSIX線程庫調度線程,而不是你想做到這艱辛的道路。