日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux信号实现精确到微秒的sleep函数:通过sigsuspend函数解决时序竞态问题

發(fā)布時(shí)間:2023/11/30 linux 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux信号实现精确到微秒的sleep函数:通过sigsuspend函数解决时序竞态问题 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

原理就是先使用定時(shí)器定時(shí),然后再使用pause函數(shù)或者sigsuspend函數(shù)主動阻塞掛起,最終恢復(fù)現(xiàn)場。

如果使用pause函數(shù)的話,優(yōu)點(diǎn)是使用簡單,缺點(diǎn)是有可能產(chǎn)生時(shí)序競態(tài),導(dǎo)致進(jìn)程一直阻塞下去:在定時(shí)和掛起之間有一個(gè)縫隙,有可能定時(shí)后因?yàn)槠渌驔]有直接掛起,而是被動掛起或者處理其他信號,但這段時(shí)間時(shí)鐘還在繼續(xù)計(jì)時(shí),當(dāng)時(shí)間到了以后信號就被發(fā)送,等回來主動掛起的時(shí)候再也等不到那個(gè)信號了,因此進(jìn)程就會被一直掛起。為了解決這個(gè)問題,我們在定時(shí)前先將SIGALRM信號屏蔽,然后定時(shí)、掛起,在掛起的同時(shí)我們解除對SIGALRM的屏蔽,這樣就不用擔(dān)心主動掛起前錯(cuò)過信號了,最后恢復(fù)現(xiàn)場。

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

代碼如下:

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

// // Created by edward on 2021/5/7. //#ifndef LINUX_UTILS_H #define LINUX_UTILS_H#include <string> #include <initializer_list> #include <signal.h>/*!* 檢查系統(tǒng)調(diào)用返回值* @param x 返回值* @param msg 錯(cuò)誤提示語句* @param y 錯(cuò)誤狀態(tài),默認(rèn)為-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函數(shù):
2021.05.11更新:修復(fù)了傳入?yún)?shù)為0或者負(fù)數(shù)的bug。如果傳入?yún)?shù)都是0的話將導(dǎo)致進(jìn)程進(jìn)入阻塞狀態(tài)無法被喚醒

struct itimerval my_sleep(int seconds, int microseconds) {if (seconds <= 0 && microseconds <= 0)return {0, 0}; //注冊SIGALRM信號捕捉函數(shù)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");//設(shè)置定時(shí)器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會產(chǎn)生競態(tài),導(dǎo)致信號失效,最終導(dǎo)致進(jìn)程無限制掛起//通過首先將信號屏蔽防止信號失效,然后再使用sigpending函數(shù)在掛起期間解除對ALRM信號的屏蔽,使得進(jìn)程最終能夠被喚醒//在掛起時(shí)解除屏蔽alarm信號suspendmask = oldmask;del2mask(&suspendmask, {SIGALRM});sigsuspend(&suspendmask);//恢復(fù)現(xiàn)場//恢復(fù)SIGALRM信號捕獲函數(shù)check_error(sigaction(SIGALRM, &oldact, nullptr), "sigaction error");//重置定時(shí)器check_error(getitimer(ITIMER_REAL, &new_value)); //獲取剩余定時(shí)時(shí)間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; //返回剩余定時(shí)時(shí)間 }

Utils.cpp:工具類實(shí)現(xiàn),非常簡單

// // 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");} }

總結(jié)

以上是生活随笔為你收集整理的Linux信号实现精确到微秒的sleep函数:通过sigsuspend函数解决时序竞态问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。