linux多线程时序问题,Linux时序竞态问题(sleep函数的实现)
時序競態是指同樣的程序,多次調用運行的結果不同,這是由于爭奪系統資源所造成的。比如說我們要使用alarm和pause函數來實現一個sleep的功能,那么由于alarm函數的實現過程并不是一個原子操作,那么隨時可能被中斷。比如說alarm了1秒,在這個過程中,進程失去了CPU,然后當該進程再次獲得CPU的時候可能這個時間已經大于1秒了,那么對于alarm來說就已經發出了SIGALRM信號。此時往下繼續調用pause函數的話,它會一直都收不到alarm發來的信號,所以導致進程的永久掛起。
為了解決這個問題,引用了sigsuspend函數。sigsuspend用于在接收到某個信號之前,臨時用mask替換進程的信號掩碼,并暫停進程執行,直到收到信號為止。也就是說,進程執行到sigsuspend時,sigsuspend并不會立刻返回,進程處于TASK_INTERRUPTIBLE狀態并立刻放棄CPU,等待UNBLOCK(mask之外的)信號的喚醒。進程在接收到UNBLOCK(mask之外)信號后,調用處理函數,然后還原信號集,sigsuspend返回,進程恢復執行。下面通過使用alarm和sigsuspend函數來實現sleep函數,代碼中有詳細的注釋來解釋說明:
#include
#include
#include
#include
#include
#include
void sig_alarm(int num){ // 空函數僅用來捕捉信號
}
unsigned int mysleep(unsigned int n_seconds){
struct sigaction n_sig, o_sig; // 一個用來設定,一個用來備份
sigset_t nsigmask, osigmask, tmpsigmask; // 用來設定,備份,替換
/*初始化*/
n_sig.sa_handler = sig_alarm; // 設定信號的捕捉動作
n_sig.sa_flags = 0;
sigemptyset(&n_sig.sa_mask);
sigaction(SIGALRM, &n_sig, &o_sig); // 設定SIGALRM的處理行為
/*對SIGALRM信號設置阻塞,防止在掛起前出現遞達態*/
sigemptyset(&nsigmask);
sigaddset(&nsigmask, SIGALRM);
sigprocmask(SIG_BLOCK, &nsigmask, &osigmask); // 設置新mask,備份舊mask
alarm(n_seconds); // 設置n秒的鬧鐘
/*對原有的mask取消SIGALRM的阻塞*/
tmpsigmask = osigmask;
sigdelset(&tmpsigmask, SIGALRM);
sigsuspend(&tmpsigmask); // 掛起等待沒有被阻塞的信號
sigprocmask(SIG_SETMASK, &osigmask, NULL); // 還原mask
sigaction(SIGALRM, &o_sig, NULL); // 還原處理行為
unsigned int unsletp = alarm(0); // 獲取剩余的睡眠時間
/*因為如果沒有睡眠夠n_seconds就收到了一個喚醒信號,那么再次調用alarm會返回剩余的時間*/
return unsletp;
}
int main(void)
{
int ans = mysleep(5);
printf("%d\n", ans);
return 0;
}
本文同步分享在 博客“Ch_zaqdt”(CSDN)。
如有侵權,請聯系 support@oschina.cn 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。
總結
以上是生活随笔為你收集整理的linux多线程时序问题,Linux时序竞态问题(sleep函数的实现)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 16-关系表达式
- 下一篇: linux 其他常用命令