1. 程式人生 > >【Linux】利用訊號實現sleep函式

【Linux】利用訊號實現sleep函式

在另一篇文章Linux訊號中,介紹了訊號的產生與處理方式,以及一系列訊號集函式的使用。
本文使用訊號機制,模擬實現sleep函式並瞭解競態條件。

在此之前先介紹一波需要用到的函式。

sigaction函式

#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

可以讀取和修改於指定訊號相關聯的處理動作。
引數 signnum 為指定訊號的編號。
若act指標非空,則根據 act 修改訊號的處理動作,oldact可以為空,或者傳出原來的處理動作。act和oldact都指向下面的結構體:

struct sigaction
{
    void    (*sa_handler) (int) // 訊號處理函式  SIG_IGN 表示忽略 SIG_DFL 表示預設動作
    sigset_t sa_mask;  // 額外要遮蔽訊號集
    int     sa_flags;  // 一般為0
    void (*sa_sigaction)(int, siginfo_t *, void *); // 實時訊號處理函式
}

當某個訊號的處理函式被呼叫時,核心自動將當前訊號加入程序的訊號遮蔽字,當訊號處理函式返回時自動恢復原來的訊號遮蔽字。保證在處理某個訊號時,如果這種訊號再次產生,那麼它會被阻塞到當前處理結束為止。如果在調動訊號處理函式時,還需要遮蔽別的訊號,則可以通過sa_mask 指定。

pause 函式

#include <unistd.h>
int pause(void);

掛起呼叫程序直到有訊號遞達。 如果訊號的處理動作是終止程序,則程序終止。如果訊號的處理動作是忽略,則程序繼續處於掛起狀態。只有訊號的處理動作是捕捉pause函式才會返回。

mysleep1

首先來實現 sleep 版本1:
1. 呼叫sigaction()捕捉訊號SIGALRM
2. 呼叫alarm()設定鬧鐘
3. 呼叫pause() 掛起等待
4. 取消鬧鐘
5. 恢復捕捉動作

#include <stdio.h>
#include <signal.h>
// 什麼事情也不做 void handler(int signum) {} unsigned mysleep(unsigned seconds) { // 1. 捕捉訊號 SIGALRM struct sigaction act, oldact; act.sa_handler = handler; act.sa_flags = 0; sigemptyset(&act.sa_mask); sigaction(SIGALRM, &act, &oldact); // 設定act 並傳出原來的act // 2. 設定鬧鐘 alarm(seconds); // 3. 掛起等待訊號遞達 pause(); // 4. 取消鬧鐘 unsigned ret = alarm(0); // 恢復捕捉 sigaction(SIGALRM, &oldact, NULL); return ret; } int main() { while(1) { printf(" 我正在睡覺 zzz \n"); mysleep(1); } return 0; }

sleep1

關於mysleep1有幾個問題:

Q1:訊號處理函式handler函式什麼都不幹,為什麼還要註冊它作為SIGALRM的處理函式?不註冊訊號處理函式可以嗎*

答:不可以。因為pause() 函式使程序掛起等待,直到有訊號遞達並且要執行自定義的訊號處理函式才有機會返回。

Q2:為什麼在mysleep函式返回前要恢復SIGALRM訊號原來的sigaction?

答:main函式作為呼叫者只想睡一下覺,沒叫你在它睡覺的時候把它打的鼻青臉腫的,所以呼叫之前什麼樣就給恢復成什麼樣子。

Q3:mysleep函式的返回值表示什麼含義? 什麼情況下返回非0值?
答:mysleep 返回值表示鬧鐘剩餘時間。 在取消鬧鐘時,上一個鬧鐘返回為0時。

mysleep2

重新審視上面程式碼,會發現有一個bug,假如在設定鬧鐘後,出現大量優先順序較高的程序需要執行,測試該程序就會被切出去,即CPU資源被分配給了別的程序,如果時間很長的話,當該程序再次或者CPU資源的時候,鬧鐘時間已過,而pause永遠不會返回。

出現這個問題的原因是系統執行程式碼的時序是不確定的。如果在寫程式時考慮不周密,可能由於時序問題而導致錯誤,這叫做競態條件(Race Condition)

我們可以在設定鬧鐘之前,遮蔽訊號 SIGALRM, 在讓程序掛起時,解除對該程序的遮蔽,然後在讓程序掛起等待訊號遞達。可以利用 sigsuspend函式 幫我們圓夢。

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

和pause 函式一樣,該函式沒有成返回值,只有執行一個訊號處理函式之後,才會返回。

呼叫sigsuspend時,程序的訊號遮蔽字由sigmask引數指定,可以通過指定sigmask來臨時解除對某個訊號的遮蔽,然後掛起等待,返回時,程序的訊號遮蔽字恢復為原來的值。

下面的從新實現的mysleep2:
1. 捕捉SIGALRM訊號
2. 遮蔽SIGALRM訊號,讓該訊號處於未決狀態,直接解除對該訊號的遮蔽
3. 呼叫alarm() 設定鬧鐘
4. 呼叫sigsuspend( )臨時解除型號,並掛起等待執行完訊號處理函式返回
5. 取消鬧鐘
6. 恢復訊號捕捉
7. 恢復訊號遮蔽

#include <stdio.h>
#include <signal.h>

// 什麼事情也不做
void handler(int signum)
{}

unsigned mysleep(unsigned seconds)
{
    struct sigaction act, oldact;
    sigset_t newmask, oldmask, suspmask;
    // 1. 訊號捕捉
    act.sa_handler = handler;
    act.sa_flags = 0;
    sigemptyset(&act.sa_mask);
    sigaction(SIGALRM, &act, &oldact);

    // 2. 訊號遮蔽
    sigemptyset(&newmask);
    sigaddset(&newmask, SIGALRM);
    sigprocmask(SIG_BLOCK, &newmask, &oldmask);

    // 3. 設定鬧鐘
    alarm(seconds);

    // 4. 臨時解除遮蔽並掛起等待
    suspmask = oldmask;
    sigdelset(&suspmask, SIGALRM); // 確保SIGALRM 不會被阻塞
    sigsuspend(&suspmask);
    // 5. 取消鬧鐘
    unsigned ret = alarm(0);

    // 6. 恢復捕捉動作
    sigaction(SIGALRM, &oldact, NULL);

    // 7. 恢復訊號遮蔽
    sigprocmask(SIG_SETMASK, &oldmask, NULL);
    return ret;
}

int main()
{
    while(1)
    {
        printf("我正在睡覺。。\n");
        mysleep(1);
    }
    return 0;
}

這裡寫圖片描述

相關推薦

Linux利用訊號實現sleep函式

在另一篇文章Linux訊號中,介紹了訊號的產生與處理方式,以及一系列訊號集函式的使用。 本文使用訊號機制,模擬實現sleep函式並瞭解競態條件。 在此之前先介紹一波需要用到的函式。 sigaction函式 #include <signal.h>

jQuery利用jQuery實現“記住我”的功能

jquer sms sep jquery實現 .com script lis put bar 【1】先下載jQuery.cookie插件:使用幫助請參考鏈接(https://github.com/carhartl/jquery-cookie)。 【2】安裝插件:

Qt利用QAxObject實現word轉pdf

通過QAxObject類操作office的com元件操作word,呼叫word的介面儲存為pdf,所以必須安裝了office才能用。 下面先貼程式碼再做說明 QAxObject *pWordApplication = new QAxObject("Word.Appli

前端利用ajax實現偽檔案非同步上傳下載

利用ajax可以實現很酷的效果,在不重新整理頁面的情況下提交表單、修改資料狀態等等,可是如果表單裡還有input:file可就慘了,ajax不支援檔案的處理啊! ajax是使用了瀏覽器內部的XmlHttpRequest物件來傳輸XML資料的。既然是Xml的資料傳輸,那麼傳輸

LinuxPOSIX訊號

POSIX訊號量和SystemV訊號量作⽤相同,都是⽤於同步操作,達到⽆衝突的訪問共享資源⺫的。 但POSIX可以⽤於執行緒間同步。 一個計數器+等待佇列,計數器用來標記當前是否有資源可供操作,等待佇列則是沒有資源就將pcb加入佇列中,等有資源的時候喚醒佇列中的執行緒。 訊號量的操作就是對計

LinuxC語言實現資料夾拷貝

在《【Linux】利用C語言檔案流複製單一檔案》(點選開啟連結)講述瞭如何用C語言拷貝檔案,但是這隻能拷貝單一檔案。如果你要用LinuxC拷貝整個資料夾,同樣要像《【Java】利用檔案輸入輸出流完成把一個資料夾內的所有檔案拷貝的另一的資料夾的操作》(點選開啟連結)一樣,先用

WCF利用WCF實現上傳下載檔案服務

引言 前段時間,用WCF做了一個小專案,其中涉及到檔案的上傳下載。出於複習鞏固的目的,今天簡單梳理了一下,整理出來,下面展示如何一步步實現一個上傳下載的WCF服務。 服務端       1.首先新建一個名為FileService的WCF服務庫專案,如下圖:  

探索利用 canvas 實現資料壓縮

前言 HTTP 支援 GZip 壓縮,可節省不少傳輸資源。但遺憾的是,只有下載才有,上傳並不支援。如果上傳也能壓縮,那就完美了。特別適合大量文字提交的場合,比如部落格園,就是很好的例子。 雖然標準不支援「上傳壓縮」,但仍可以自己來實現。 Flash 首選方案當然是 Flash,畢竟它提供了壓縮 API。除了

Android利用 ACRA 實現在規定時間內崩潰次數超過規定值就自動清理 APP 資料

其實 ACRA 是一個崩潰處理的類庫,其功能就是收集App崩潰堆疊資訊,生成報告併發送到指定端,當然它也可以自己定製對應的操作,所以是個處理崩潰很不錯的庫。 ACRA Application Crash Reports for Android 其實在規定時間內崩潰次數超

IOS利用ASIHTTPRequest 實現一個簡單的登陸驗證

【原創作品, 歡迎轉載,轉載請在明顯處註明! 謝謝。 今天給大家帶來一個簡單的登陸驗證,用的是ASIHttpRequest 這個開源類庫,使用的方法很簡單,從網上下載下來以後,新增到專案中,並新增一下這些框架。 下面上程式碼 // // ViewControll

LinuxLVS+Keepalived實現高可用負載均衡(Web群集)

一、Keepalived概述 keepalived是一個類似於layer3,4,5交換機制的軟體,也就是我們平時說的第3層、第4層和第5層交換。Keepalived的作用是檢測web伺服器的狀態,

Redis利用 Redis 實現分散式鎖

## 技術背景 首先我們需要先來了解下什麼是分散式鎖,以及為什麼需要分散式鎖。 對於這個問題,我們可以簡單將鎖分為兩種——記憶體級鎖以及分散式鎖,記憶體級鎖即我們在 Java 中的 synchronized 關鍵字(或許加上程序級鎖修飾更恰當些),而分散式鎖則是應用在分散式系統中的一種鎖機制。分散式鎖的應

Linux環境程式設計之訊號處理(三、利用alarm()和pause()函式實現sleep()函式)

        sleep()是執行掛起一段時間,而alarm()函式是定時器,pause()函式則是掛起程序,當出現訊號打斷時,才會繼續往前執行;         先來分享下alarm()函式,alarm()函式用來設定一個定時器,當時間超時時,會產生SIGALRM訊號,

Linux使用read、write、lseek函式實現檔案讀寫操作

本文章主要講述Linux環境下檔案的讀寫操作,使用到的函式有open、read、write、lseek。 open:用於開啟或者建立檔案。 read:從檔案中讀指定位元組的資料到記憶體中。 write:講記憶體中資料寫入到檔案中。 lseek:可以改變當前檔案偏移量。 #

Linux執行緒總結:執行緒同步 -互斥鎖,條件變數,訊號實現多生產者多消費者模型

學習環境 :  Centos6.5 Linux 核心 2.6 Linux執行緒部分總結分為兩部分:(1)執行緒的使用 ,(2)執行緒的同步與互斥。 第一部分執行緒的使用主要介紹,執行緒的概念,建立執行緒,執行緒退出,以及執行緒的終止與分離。【完

訊號的原理和利用--實現sleep函式

訊號就是當一個程序收到一個訊號,程序就要對個訊號作出相關處理,比如我們常用的 kill-9 PID ,這條命令就是向某個程序傳送9號訊號,以終止這個程序,那麼具體到底怎麼實現程序的終止的呢?讓我們一起來看一下。 我們要明白以下三點: 1.程序可以對個訊號作

Linux生產者消費者程式設計實現-執行緒池+訊號

生產者消費者程式設計實現,採用了執行緒池以及訊號量技術。 執行緒的概念就不多說,首先說一下多執行緒的好處:多執行緒技術主要解決處理器單元內多個執行緒執行的問題,它可以顯著減少處理器單元的閒置時間,增加處理器單元的吞吐能力。 那麼為什麼又需要執行緒池呢? 我們知道應

管用 使用VMtools實現主機Windows與虛擬機Linux文件共享

工具 http 環境 functions user sha use 提示 文件服務 實現windows主機與linux虛擬機文件共享,有很多方法,包括使用samba文件服務器等,本文介紹通過vmware虛擬機軟件中的vmtools工具來實現文件共享。 一、環境 1、主機

linux扒站命令之利用wget快速扒站利用wget快速扒站

追溯 創建目錄 元素 頁面 根目錄 uri 鏈接 目錄 -h 在Linux下,通過一個命令就可以把整個站相關的文件全部下載下來。 wget -r -p -k -np 參數說明: -r : 遞歸下載 -p : 下載所有用於顯示 HTML 頁面的圖片之類的元素 -k : 在

LinuxXshell如何連接虛擬機實現文件上傳

上傳 .net Language alt+ 會有 ear 傳輸文件 下載 執行命令 經常會有從本地到服務器以及從服務器到本地的文件傳輸,所以需要xshell連接到linux服務,VMware14安裝CentOS7 自己也是新手,做一下記錄,方便以後回顧 1