2012-04-02 30 views
1

我正在系統課程的shell實驗室工作,並且自從週五晚上以來我一直試圖解決一些非常奇怪的競爭條件錯誤,並且似乎無法確定。我的信號處理程序中的競態條件? (C)

我當前的代碼:http://buu700.com/tsh

之前的所有START OF MY CODEEND OF MY CODE由課程導師提供的,所以沒有那應該是問題的根源。

我們也有一個測試腳本;這裏是我目前的測試結果輸出:http://buu700.com/sdriver


/***************** 
* Signal handlers 
*****************/ 

/* 
* sigchld_handler - The kernel sends a SIGCHLD to the shell whenever 
*  a child job terminates (becomes a zombie), or stops because it 
*  received a SIGSTOP, SIGTSTP, SIGTTIN or SIGTTOU signal. The 
*  handler reaps all available zombie children, but doesn't wait 
*  for any other currently running children to terminate. 
*/ 
void 
sigchld_handler(int sig) 
{ 
    pid_t pid; 
    int status, termsig; 
    struct job_t *job; 


    sigset_t s; 

    sigemptyset(&s); 
    sigaddset(&s, SIGCHLD); 
    sigaddset(&s, SIGINT); 
    sigaddset(&s, SIGTSTP); 

    sigprocmask(SIG_BLOCK, &s, NULL); 


    while ((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) { 
     if (WIFEXITED(status)) { 
      deletejob(job_list, pid); 
     } 
     if ((termsig = WTERMSIG(status))) { 
      deletejob(job_list, pid); 
      safe_printf("Job [%i] (%i) %s by signal %i\n", 
          pid2jid(pid), pid, "terminated", termsig); 
     } 
     if (WIFSTOPPED(status)) { 
      job = getjobpid(job_list, pid); 
      job->state = ST; 
      safe_printf("Job [%i] (%i) %s by signal %i\n", 
          pid2jid(pid), pid, "stopped", SIGTSTP); 
     } 
    } 

    if (errno != ECHILD) 
     unix_error("waitpid error"); 

    sigprocmask(SIG_UNBLOCK, &s, NULL); 

    return; 
} 



/* 
* sigint_handler - The kernel sends a SIGINT to the shell whenver the 
* user types ctrl-c at the keyboard. Catch it and send it along 
* to the foreground job. 
*/ 
void 
sigint_handler(int sig) 
{ 
    sigset_t s; 

    sigemptyset(&s); 
    sigaddset(&s, SIGCHLD); 
    sigaddset(&s, SIGINT); 
    sigaddset(&s, SIGTSTP); 

    sigprocmask(SIG_BLOCK, &s, NULL); 

    kill(-1, sig); 

    sigprocmask(SIG_UNBLOCK, &s, NULL); 

    return; 
} 



/* 
* sigtstp_handler - The kernel sends a SIGTSTP to the shell whenever 
*  the user types ctrl-z at the keyboard. Catch it and suspend the 
*  foreground job by sending it a SIGTSTP. 
*/ 
void 
sigtstp_handler(int sig) 
{ 
    sigset_t s; 

    sigemptyset(&s); 
    sigaddset(&s, SIGCHLD); 
    sigaddset(&s, SIGINT); 
    sigaddset(&s, SIGTSTP); 

    sigprocmask(SIG_BLOCK, &s, NULL); 

    kill(-1, sig); 

    sigprocmask(SIG_UNBLOCK, &s, NULL); 

    return; 
} 
+5

我發現你對教授提供的代碼_adorable_有信心。 :) – sarnold 2012-04-02 01:25:21

+0

哈哈,它看起來對我來說是非常可靠的代碼,並且大多數學生已經完成了實驗室,清楚了課程提供的代碼的工作原理。 (據我所知,雖然沒有一個是我的教授親自寫的)。 – 2012-04-02 01:28:02

+0

你能否給出一個更好的故障描述。並解釋你爲什麼認爲這是一種競爭條件? – 2012-04-02 03:43:01

回答

1

我有一個懷疑,錯誤來自於你對其他信號的賽車在你的信號處理程序:

sigint_handler(int sig) 
{ 
    sigset_t s; 

    sigemptyset(&s); 
    sigaddset(&s, SIGCHLD); 
    sigaddset(&s, SIGINT); 
    sigaddset(&s, SIGTSTP); 

    sigprocmask(SIG_BLOCK, &s, NULL); 

當您註冊在sigaction(2)的信號處理程序中,您可以提供一個sa_mask,在您的信號處理程序運行時,內核將使用它來阻止您的信號。這是自動完成的,不需要額外的工作。註冊信號處理程序時,只需填充一次此掩碼即可。

另一種可能來自SIGTSTP信號;我的APUE, 2nd edition一份350頁說,部分:

只有一個作業控制外殼應的配置[SIGTSTPSIGTTINSIGTTOU]重置爲SIG_DFL

什麼沒說的那些段落,但我認爲這是公平的假設,就是外殼應的信號配置設置爲SIG_DFL子進程 - 它仍然需要做一些事情來處理信號本身。

您是否正確處理了孩子的信號處置?

+0

感謝您的回覆。課程提供的代碼實際上有一個sigaction封裝器,它處理你描述的阻塞問題;實際上,我只是把它作爲處理程序中的一個隨機無意義的東西,而這些東西往往會在無目標地進行調試(它根本不會影響測試儀的輸出)的情況​​下進行攻擊。我現在將它刪除,因爲它絕對不正確。 --- 我在做調試時也做了類似於SIG_DFL的建議;這看起來是否接近正確? http://buu700.com/runco​​mmand – 2012-04-02 02:02:52

+1

馬上讓我感到害怕的是這樣的:'sprintf(cmd,「%s%s」,cmd,argv [i]);'。請注意'sprintf(3)'中的警告:_C99和POSIX.1-2001指定如果調用'sprintf()','snprintf()','vsprintf()'或'vsnprintf( )會導致在重疊對象之間發生複製(例如,如果目標字符串數組和一個提供的輸入參數指向同一個緩衝區)。_ – sarnold 2012-04-02 02:10:44

+0

謝謝,只是修正了這個問題(當我編寫它時感覺很危險)。我仍然從測試腳本中獲得相同的輸出結果,但現在該部分應該會更好。 – 2012-04-02 02:26:49