時序競態
- 產生原因
- 改進
- 總結
產生原因
#include <cstdio>
#include <stdio.h>
#include <sys/time.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <errno.h>void catch_sigalrm(int signo)
{printf("pause sucess 11111\n");
}unsigned int mysleep(unsigned int seconds)
{struct sigaction act, oldact;sigset_t new1,old;act.sa_flags = 0;act.sa_handler = catch_sigalrm;sigemptyset(&act.sa_mask);int ret=sigaction(SIGALRM, &act, &oldact);if (ret == -1){perror("sigaction error:");exit(1);}sigemptyset(&new1);sigemptyset(&old);sigaddset(&new1,SIGALRM);sigaddset(&old, SIGALRM);sigprocmask(SIG_BLOCK,&new1, NULL);sigprocmask(SIG_UNBLOCK, &old, NULL);alarm(seconds);ret=pause();//主動掛起 等信號if (ret == -1 && errno== EINTR){printf("pause sucess \n");}ret=alarm(0);sigaction(SIGALRM,&oldact,NULL);return ret;
}int main()
{mysleep(3);printf("----------------\n");return 0;
}
如果進程在執行完alarm函數后,突然失去CPU,被阻塞等待(這是有可能的,進程在執行過程中,若非原子操作,都有可能隨時失去CPU),如果失去CPU的時間大于了執行完,則此時在執行pause函數前,信號已經到了,因此會先處理信號(軟中斷,而不是先執行pause函數),在信號處理完后,再去執行pause函數,此時進程會被永遠掛起,不會被喚醒,因為SIGALRM信號已經被處理了。
時序競態:即由于進程之間執行的順序不同,導致同一個進程多次運行后產生了不同結果的現象。如上述sleep函數,有時執行結果是正確的,有時卻會導致進程永遠被掛起,因此這就是一個時序競態問題。因此需要重新對該函數進行改進。
改進
代碼如下(示例):
#include <cstdio>
#include <stdio.h>
#include <sys/time.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <errno.h>void doaction(int signo)
{printf("---------%d 信號-------\n",signo);
}int mysleep(int second)
{int unslept;struct sigaction cur, old;sigset_t n_ew,o_ld,supemask;//安裝信號cur.sa_flags = 0;sigemptyset(&cur.sa_mask);cur.sa_handler = doaction;sigaction(SIGALRM,&cur, &old);//設置阻塞信號集,阻塞SIGALRM信號sigemptyset(&n_ew);sigaddset(&n_ew,SIGALRM);sigprocmask(SIG_BLOCK,&n_ew, &o_ld);//定時alarm(second);//構建一個臨時的sigsuspend有效阻塞信號集supemask = o_ld;sigemptyset(&supemask);sigsuspend(&supemask);//supemask未阻塞信號掛起unslept = alarm(0);//恢復之前狀態sigaction(SIGALRM,&old,NULL);sigprocmask(SIG_SETMASK,&o_ld,NULL);return unslept;//int ret=pause();//if (ret == -1 && errno == EINTR)//{// printf("pause success\n");//}}int main()
{mysleep(3);}
使用sigsuspend函數,sigsuspend具有“原子操作“,這樣就能夠避免。
總結
- 競態條件,跟系統負載有很緊密的關系,體現出信號的不可靠性。系統負載越嚴重,信號不可靠性越強。
- 不可靠由其實現原理所致。信號是通過軟件方式實現(跟內核調度高度依賴,延時性強),每次系統調用結束后,或中斷處理處理結束后,需通過掃描PCB中的未決信號集,來判斷是否應處理某個信號。當系統負載過重時,會出現時序混亂。
- 這種意外情況只能在編寫程序過程中,提早預見,主動規避,而無法通過gdb程序調試等其他手段彌補。且由于該錯誤不具規律性,后期捕捉和重現十分困難。