Linux信號實現精確到微秒的sleep函數:通過sigsuspend函數解決時序競態問題

原理就是先使用定時器定時,然后再使用pause函數或者sigsuspend函數主動阻塞掛起,最終恢復現場。

如果使用pause函數的話,優點是使用簡單,缺點是有可能產生時序競態,導致進程一直阻塞下去:在定時和掛起之間有一個縫隙,有可能定時后因為其他原因沒有直接掛起,而是被動掛起或者處理其他信號,但這段時間時鐘還在繼續計時,當時間到了以后信號就被發送,等回來主動掛起的時候再也等不到那個信號了,因此進程就會被一直掛起。為了解決這個問題,我們在定時前先將SIGALRM信號屏蔽,然后定時、掛起,在掛起的同時我們解除對SIGALRM的屏蔽,這樣就不用擔心主動掛起前錯過信號了,最后恢復現場。

可以根據代碼理解一下,其實是一個很符合直覺的過程。需要注意的是pausesigsuspend只有失敗返回值-1,不過這個失敗的意思是掛起失敗,也就是恢復運行,從這個意義上來講應該是成功返回值,因此我們不要對-1返回值做處理(我順手處理了,然后一直出錯檢查了半天)。

代碼如下:

Utils.h:里面是一些我封裝的函數,為了簡化代碼

//
// Created by edward on 2021/5/7.
//#ifndef LINUX_UTILS_H
#define LINUX_UTILS_H#include <string>
#include <initializer_list>
#include <signal.h>/*!* 檢查系統調用返回值* @param x 返回值* @param msg 錯誤提示語句* @param y 錯誤狀態,默認為-1*/
void check_error(int x, const std::string &msg = "error", int y = -1);
/*!* 清零mask,并將il中的信號加入到mask中* @param mask* @param il*/
void add2mask(sigset_t *mask, std::initializer_list<int> il);
/*!* 將il中的信號從mask中刪除* @param mask* @param il*/
void del2mask(sigset_t *mask, std::initializer_list<int> il);#endif //LINUX_UTILS_H

mysleep函數:
2021.05.11更新:修復了傳入參數為0或者負數的bug。如果傳入參數都是0的話將導致進程進入阻塞狀態無法被喚醒

struct itimerval my_sleep(int seconds, int microseconds) {if (seconds <= 0 && microseconds <= 0)return {0, 0};	//注冊SIGALRM信號捕捉函數struct sigaction act, oldact;act.sa_handler = alrm_handler;act.sa_flags = 0;sigset_t mask, oldmask,suspendmask;sigemptyset(&mask);     //屏蔽鍵盤信號add2mask(&mask, {SIGINT, SIGQUIT, SIGTSTP});act.sa_mask = mask;check_error(sigaction(SIGALRM, &act, &oldact), "sigaction error");//屏蔽alarm信號add2mask(&mask, {SIGALRM});check_error(sigprocmask(SIG_BLOCK, &mask, &oldmask), "sigprocmask error");//設置定時器struct itimerval new_value, old_value;new_value.it_value = {seconds, microseconds};new_value.it_interval = {0, 0};check_error(setitimer(ITIMER_REAL, &new_value, &old_value), "setitimer error");//主動阻塞掛起等待被信號喚醒//pause();      //使用pause會產生競態,導致信號失效,最終導致進程無限制掛起//通過首先將信號屏蔽防止信號失效,然后再使用sigpending函數在掛起期間解除對ALRM信號的屏蔽,使得進程最終能夠被喚醒//在掛起時解除屏蔽alarm信號suspendmask = oldmask;del2mask(&suspendmask, {SIGALRM});sigsuspend(&suspendmask);//恢復現場//恢復SIGALRM信號捕獲函數check_error(sigaction(SIGALRM, &oldact, nullptr), "sigaction error");//重置定時器check_error(getitimer(ITIMER_REAL, &new_value));    //獲取剩余定時時間old_value.it_interval = {0, 0};old_value.it_value = {0, 0};check_error(setitimer(ITIMER_REAL, &old_value, nullptr), "setitimer error");//解除對ALRM信號的屏蔽add2mask(&mask, {SIGALRM});check_error(sigprocmask(SIG_UNBLOCK, &mask, nullptr), "sigprocmask error");return new_value;   //返回剩余定時時間
}

Utils.cpp:工具類實現,非常簡單

//
// Created by edward on 2021/5/7.
//#include "utils.h"using std::string;void check_error(int x, const string &msg, int y) {if (x == y) {perror(msg.c_str());exit(1);}
}void add2mask(sigset_t *mask, std::initializer_list<int> il) {check_error(sigemptyset(mask), "sigemptyset error");for (auto signum : il) {check_error(sigaddset(mask, signum), "sigaddset error");}
}void del2mask(sigset_t *mask, std::initializer_list<int> il) {for (auto signum : il) {check_error(sigdelset(mask, signum), "sigdelset error");}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/383545.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/383545.shtml
英文地址,請注明出處:http://en.pswp.cn/news/383545.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux創建多個子進程并通過捕獲SIGCHLD信號進行非阻塞回收

我們通過fork函數創建多個子進程&#xff0c;并通過exec函數族在子進程中進行其他的工作&#xff0c;但是為了避免僵尸進程&#xff0c;我們要對子進程進行回收。常用的回收方式是wait或者waitpid進行阻塞回收&#xff0c;因為如果非阻塞回收很難把握時機&#xff0c;而阻塞回收…

Linux創建守護進程

守護進程&#xff08;Daemon&#xff09;是運行在后臺的一種特殊進程。它獨立于控制終端并且周期性地執行某種任務或等待處理某些發生的事件。它不需要用戶輸入就能運行而且提供某種服務&#xff0c;不是對整個系統就是對某個用戶程序提供服務。Linux系統的大多數服務器就是通過…

Linux創建多個子線程并回收

創建子線程的邏輯相比子進程要更容易理解一些&#xff0c;因為線程沒有像進程那樣復制很多東西另起爐灶&#xff0c;子線程從傳入的開始函數開始運行&#xff0c;但是難點在于傳入參數和回收時獲取退出狀態&#xff0c;因為這兩個原本都是void *類型的&#xff0c;而我們在使用…

Qt發布程序

Windows&#xff1a; https://www.cnblogs.com/linuxAndMcu/p/10974927.html Ubuntu&#xff1a; https://blog.csdn.net/u014779536/article/details/107854060

K210入門

之前購買了一個Sipeed Maix M1w Dock k210的開發板&#xff0c;想著自己鼓搗鼓搗&#xff0c;在網上看到了一些好的教程&#xff0c;在這里記錄一下&#xff1a; 嵌入式AI從入門到放肆【K210篇】-- 硬件與環境&#xff1a;介紹了各種開發環境的搭建&#xff0c;但是不是特別詳細…

C++輸入輸出:cin/cout 還是 scanf/printf?

相信使用C的人都有一種迷惑或者是不自信&#xff1a;在輸入輸出的時候是不是應該使用scanf/printf更好呢&#xff0c;因為傳說cin/cout龜速&#xff0c;我當時也長期被這個所困擾&#xff0c;后來在閱讀C primer第五版的時候我自己做了一個測試&#xff0c;發現如果不使用std::…

UVA - 101:The Blocks Problem

原本以為是一道很簡單的模擬題&#xff0c;結果寫了一個小時。。。很長時間不碰算法題&#xff0c;的確手感差很多。不過我覺得隨著刷題慢慢多起來應該會好的。 題目的意思也有點含糊&#xff0c;需要自己去猜&#xff0c;大概意思就是槽里有一堆木頭&#xff0c;每個槽剛開始…

UVA - 12096:The SetStack Computer

題目描述很簡單&#xff0c;難點在于如何對集合進行編碼&#xff0c;因為是無限的&#xff0c;好像沒有一個方向進行編碼。 紫書給的題解十分巧妙&#xff1a;給新出現的集合進行編碼 的確&#xff0c;我們沒有必要為所有可能出現的集合編碼后再開始&#xff0c;我們就可以簡單…

UVA - 540:Team Queue

主要的關鍵在于&#xff1a;不要試圖讓所有團隊的人在一個隊列里面&#xff0c;因為這樣如果新入隊的是一個前面團隊的成員則必須先出隊再入隊。 應該把每個團隊看做一個整體&#xff0c;用一個隊列維護團隊的順序&#xff0c;用t個隊列維護每個團隊內部的順序。 還有就是要維…

UVA-136:Ugly Numbers

很簡單的一道題&#xff0c;但是我竟然蠢到想不明白為什么如果從頭生成會出現大量重復的數字。 寫的時候主要出現的錯誤在爆int上&#xff0c;一定要注意數據范圍。 #include <iostream> #include <queue> #include <set>using namespace std; using ll lo…

類的成員函數可以訪問屬于該類的任意對象的私有變量

之前在書上看到成員函數可以訪問類的私有變量的時候覺得是廢話嘛&#xff0c;如果成員函數都不能訪問那私有變量不就變成了花瓶了。然而發現自己還是太naive。 這句話的意思是&#xff1a;在類的作用域內&#xff0c;包含成員函數、靜態成員函數和友元函數內&#xff0c;可以訪…

GMP使用入門

最近寫了一個高精度的模板&#xff0c;想要用GMP庫測試一下&#xff0c;總結一下GMP環境的搭建。 環境搭建&#xff1a;GMP大法教你重新做人(從入門到實戰) 解壓.tar.lz的 時候可能會遇到一點問題&#xff0c;可以參考這個博客&#xff1a;.tar.lz壓縮包解壓 需要注意的是C需要…

UVA - 400:Unix ls

題目的難點在于要求前面的每一列的是最大長度L2&#xff0c;最后一列的長度是L。對于寬度為WIDTH60的一行來說&#xff0c;一行可以放下多少個單詞決定了需要多少行&#xff0c;知道了行數才能開始根據行數開始放置。 我的做法是col (WIDTH 2) / (L 2)&#xff0c;即提前給W…

UVA - 1592:Database

題目的意思是找到兩行在兩列處相等&#xff0c;主要要做的是記錄某個值是否重復出現過。 經過思考&#xff0c;我的思路是&#xff1a;每一列用一個unordered_map<string,vector<int>>記錄單詞出現的行數&#xff0c;對于某一行中的兩列&#xff0c;如果有兩個元素…

C++ array初始化需要雙層大括號

對于array的初始化我們可以使用列表初始化&#xff1a; array<int, 8> test {1,2,3,4,5,6,7,8 };但是當我們不再使用簡單的內置類型array時&#xff1a; array<pair<int, int>, 8> dirs {{-1, -1},{-1, 0},{-1, 1},{0, -1},{0, 1},{1, -1},{1, 0},{1, 1}…

Qt for Android環境配置

最近想寫一個小APP&#xff0c;但是又不想用Android Studio進行開發&#xff0c;想要用C進行開發&#xff0c;聽說Qt可以進行Android開發&#xff0c;就想嘗試一下&#xff0c;結果花了一天時間來配置環境。。。而且發現windows下配置環境更簡單一些&#xff08;我中途還切換到…

UVa-12333:Revenge of Fibonacci 高精度

之前自己仿照紫書上寫了高精度庫&#xff0c;完善了乘法、減法&#xff0c;并且通過了和C高精度庫GMP的對拍測試&#xff0c;并在一些OJ上過了一些高精度的模板題&#xff0c;代碼倉庫地址&#xff1a;https://github.com/Edward-Elric233/BigInt 求解思路 題目的意思是求前1…

vim命令筆記

vim折疊函數&#xff1a;https://www.cnblogs.com/zlcxbb/p/6442092.html Vim錄制宏及使用&#xff1a;https://www.jianshu.com/p/9d999c72a9f3 將vim與系統剪貼板的交互使用&#xff1a;https://zhuanlan.zhihu.com/p/73984381

Educational Codeforces Round 114總結

緒論 https://codeforces.com/contest/1574/ 以前想要打CF&#xff0c;總是覺得沒有時間&#xff0c;要做這個&#xff0c;要做那個&#xff0c;現在時間充裕了一些&#xff0c;想要多打一些CF&#xff0c;但是光打比賽不總結是沒有什么幫助的&#xff0c;這是我從以前的ACM訓…

UVA - 210:Concurrency Simulator

題目鏈接&#xff1a;https://vjudge.net/problem/UVA-210 題目分析 就是一道模擬題&#xff0c;但是細節有點多。 寫代碼兩個小時&#xff0c;調試代碼用了兩天。。。很長時間不刷題了&#xff0c;這道雖然算法簡單但是細節滿滿的題目對我來說是一個很好的熱身。 盡量不要去…