[Linux]繼續探究mysleep函數(競態條件)

之前我們探究過mysleep的簡單用法,我們實現的代碼是這樣的:

#include<stdio.h>
#include<signal.h>void myhandler(int sig)
{}unsigned int mysleep(unsigned int timeout)
{struct sigaction act,oact;act.sa_handler = myhandler;sigemptyset(&act.sa_mask);act.sa_flags = 0;sigaction(SIGALRM,&act,&oact);   //信號注冊函數alarm(timeout);  //鬧鐘timeout秒后響pause();     //掛起等待unsigned int ret = alarm(0);   //清空鬧鐘sigaction(SIGALRM,&oact,NULL);return ret;
}int main()
{printf("ready sleeping!\n");mysleep(3);printf("i am waking!\n");return 0;
}

我們首先注冊了捕捉信號的函數,信號為SIGALRM,然后調用了alarm函數來設置鬧鐘,此時pause來掛起等待,然后內核切換到別的進程運行,在timeout秒后鬧鐘產生SIGALRM信號,從內核態到用戶態的過程中,收到用戶自定義的處理函數,就在用戶態處理函數,進入處理函數myhandler時,SIGALRM信號會被自動屏蔽,當處理完函數后,自動解除屏蔽,進入到內核態執行系統調用,最后切換到用戶態執行主函數控制邏輯。

這里存在一個問題,比如剛剛說的alarm函數調用完成后,pause掛起等待,當把所有的邏輯都處理完成alarm返回時,進入用戶態繼續pause,是不是沒有任何意義呢。還有一點,我們不知道pause掛起等待是在alarm函數之內還是執行后調用的,這就出現了異步情況,我們稱這種現象為競態條件。

我們如何解決這類問題呢。。我們試著將SIGALRM信號屏蔽起來,當執行完alarm函數后再自動解除屏蔽。這樣就保證是在alarm函數執行到時間后掛起的狀態。但是還有一種可能是當解除屏蔽之后還有可能使SIGALRM遞達,因此這種方法還是有缺陷的。我們只能用原子性的操作來避免這種問題的出現。

這時又引出了一個函數:sigsuspend包含了pause的掛起等待功能,同時解決了競態條件的問題(與exec和pause一樣,沒有成功的返回值)

#include <signal.h>
int sigsuspend(const sigset_t *sigmask);

調用sigsuspend時,進程的信號屏蔽字由sigmask參數指定,可以通過指定sigmask來臨時解除對某個信號的屏蔽,然后掛起等待,當sigsuspend返回時,進程的信號屏蔽字恢復為原來的值,如果原來對該信號是屏蔽的,從sigsuspend返回后仍然是屏蔽的。

實現代碼如下:

#include<stdio.h>
#include<signal.h>void myhandler(int sig)
{}unsigned int mysleep(unsigned int timeout)
{struct sigaction act,oact;sigset_t newmask,oldmask,suspmask;act.sa_handler = myhandler;sigemptyset(&act.sa_mask);act.sa_flags = 0;sigemptyset(&newmask);sigaddset(&newmask,SIGALRM);sigprocmask(SIG_BLOCK,&newmask,&oldmask);sigaction(SIGALRM,&act,&oact);   //信號注冊函數alarm(timeout);  //鬧鐘timeout秒后響suspmask = oldmask;sigdelset(&suspmask,SIGALRM);sigsuspend(&suspmask);//pause();     //掛起等待unsigned int ret = alarm(0);   //清空鬧鐘sigaction(SIGALRM,&oact,NULL);sigprocmask(SIG_SETMASK,&oldmask,NULL);return ret;
}int main()
{printf("ready sleeping!\n");mysleep(3);printf("i am waking!\n");return 0;
}

運行結果:

這里寫圖片描述

3秒之后:

這里寫圖片描述

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

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

相關文章

C語言的atoi和C++的to_string

to_stringint to string將其他型轉換成字符串型atoiascii to integer是把字符串轉換成整型數的一個函數 to_string #include <iostream> // std::cout #include <string> // std::string, std::to_stringint main () {std::string perfect std::to_string…

ubuntu 升級python3.5到python3.7,并升級pip3

1, 下載python3.7.tgz 文件&#xff0c;解壓&#xff0c; 2. 編譯安裝 3. 刪除 /usr/bin 目錄下的 pip3, python3 4. 建立新的軟連接&#xff1a; #添加python3的軟鏈接ln -s /usr/local/python3/bin/python3.7 /usr/bin/python3#添加 pip3 的軟鏈接ln -s /usr/local/python3/b…

[Linux]死鎖

死鎖是指多個進程在運行過程中因爭奪資源而造成的一種僵局&#xff0c;當進程處于這種僵持狀態時&#xff0c;若無外力作用&#xff0c;它們都將無法再向前推進。之前信號量的時候我們知道&#xff0c;如果多個進程等待&#xff0c;主要體現在占有鎖的問題上。死鎖也可以被定義…

Python安裝第三方模塊總結 轉載的

轉自 https://www.jellythink.com/archives/541

[C++]vector創建二維數組

c.resize(n);將c重置為大小為n個元素向量&#xff0c;如果n比原來的元素多&#xff0c;則多出的元素常被初始化為0//節選《面向對象的程序設計》杜茂青 int N5, M6; vector<vector<int> > Matrix(N); for(int i 0; i< Matrix.size(); i){ Matrix[i].resize(M…

[Linux]線程安全和可重入函數

線程安全&#xff1a;一個函數被稱為線程安全的&#xff0c;當且僅當被多個并發進程反復調用時&#xff0c;它會一直產生正確的結果。如果一個函數不是線程安全的&#xff0c;我們就說它是線程不安全的。 重入&#xff1a;函數被不同的控制流程調用,有可能在第一次調用還沒返回…

[Linux]信號量

信號量是一個計數器&#xff0c;用于為多個進程提供對共享數據對象的訪問。 在信號量上只有三種操作可以進行&#xff0c;初始化、遞增和增加&#xff0c;這三種操作都是原子操作。遞減操作可以用于阻塞一個進程&#xff0c;增加操作用于解除阻塞一個進程。 為了獲得共享資源…

Linux VIM 程序中有游離的‘\357’ ‘\274’錯誤

gcc date.cpp -o date -lstdc date.cpp:18:20: 錯誤&#xff1a;程序中有游離的‘\357’date.Showdata()&#xfffd;&#xfffd;&#xfffd;^ date.cpp:18:21: 錯誤&#xff1a;程序中有游離的‘\274’date.Showdata()&#xfffd;&#xfffd;&#xfffd;^ date.cpp:18:22…

[Linux]關于SIGCHLD

之前我們就學過&#xff0c;關于wait和waitpid來處理僵尸進程&#xff0c;父進程等待子進程結束后自己才退出&#xff0c;這樣的方法有倆種方式&#xff0c;一種是父進程死死的等子進程退出&#xff0c;也就是使用阻塞的方式等待子進程退出&#xff0c;另一種方式是通過非阻塞的…

C語言思維導圖

本人能力有限&#xff0c;知識點難免概括不全&#xff0c;如有錯誤歡迎指正

轉載一篇關于curl的文章

轉載一篇關于curl的文章 http://www.360doc.com/content/16/0107/15/18578054_526158476.shtml

[Linux]vi/vim下添加多行注釋和取消注釋

添加注釋&#xff08;Centos&#xff09;&#xff1a; 在命令行模式下按ctrlV進入 visual block模式&#xff08;可視化模式&#xff09; 選中你需要注釋的行&#xff0c;再按大寫的I&#xff0c;輸入//&#xff0c;最后按倆下esc即可。 如果想讓前進tab個位&#xff0c;則可在…

pthread和互斥量條件變量函數意義速查表

數據類型 pthread_t 線程 互斥量和條件變量

[Linux]共享內存

共享內存是UNIX提供的進程間通信手段中速度最快的一種&#xff0c;也是最快的IPC形式。為什么是最快的呢&#xff0c;因為數據不需要在客戶進程和服務器進程之間復制&#xff0c;所以是最快的一種IPC。這是虛存中由多個進程共享的一個公共內存塊。 兩個不同進程A、B共享內存的…

僵尸進程的產生,危害和解決方案

概念 僵死狀態&#xff08;Zombies&#xff09;是一個比較特殊的狀態。 當進程退出并且父進程沒有讀取到子進程退出的返回代碼時就會產生僵尸進程。僵尸進程會以終止狀態保持在進程表中&#xff0c;并且會一直在等待父進程讀取退出狀態代碼。所以&#xff0c;只要子進程退出&…

CString string 轉換

https://www.cnblogs.com/HappyEDay/p/7016162.html

[Linux]gdb調試多進程多線程例程

gdb相信學linux的同學已經比較熟悉了吧&#xff0c;它是linux下代碼調試工具。我們在寫c語言&#xff0c;c的代碼時經常會用到&#xff0c;它有一些常用的調試命令: run&#xff08;r&#xff09;&#xff1a;運行程序&#xff0c;如果有斷點在下一個斷點處停止 start&#xf…

gdb調試常用命令速查(段錯誤調試)

編譯程序時需要加上-g&#xff0c;之后才能用gdb進行調試&#xff1a;gcc -g main.c -o main gdb中命令&#xff1a; 回車鍵&#xff1a;重復上一命令 &#xff08;gdb&#xff09;help&#xff1a;查看命令幫助&#xff0c;具體命令查詢在gdb中輸入help 命令,簡寫h &…

C語言字符串 小記

#include "stdafx.h" #include <iostream> #include <string.h> using namespace std;int _tmain(int argc, _TCHAR* argv[]) {char str1[] "12345"; // ""括起來的字符串 會在末尾增加 \0 cout << sizeof(str1) << en…

[Linux]守護進程(精靈進程)

一、守護進程是什么 守護進程是生存期很長的一種進程&#xff0c;可以說它是7*24小時工作的。&#xff08;什么是7*24&#xff0c;一周7天&#xff0c;每天24小時&#xff0c;這不就是一年365天一直在工作嘛&#xff0c;還搞的這么詼諧&#xff0c;哈哈&#xff09;。它們常常…