Linux 定時器介紹

来源:https://www.cnblogs.com/englyf/archive/2022/09/03/16651865.html
-Advertisement-
Play Games

在linux系統中定時器有分為軟定時和硬體定時器,硬體定時器一般指的是CPU的一種底層寄存器,它負責按照固定時間頻率產生中斷信號,形成信號源。基於硬體提供的信號源,系統就可以按照信號中斷來計數,計數在固定頻率下對應固定的時間,根據預設的時間參數即可產生定時中斷信號,這就是軟定時。 ...


以下內容為本人的著作,如需要轉載,請聲明原文鏈接 微信公眾號「englyf」https://www.cnblogs.com/englyf/p/16651865.html


曾經常去沙縣小吃,就為了蹭上一碗4塊錢的蔥油拌面,聽著邊上的幾位小哥老說

華仔,有軟硬之分。

其實寫代碼也有這種講究。


在linux系統中定時器有分為軟定時和硬體定時器,硬體定時器一般指的是CPU的一種底層寄存器,它負責按照固定時間頻率產生中斷信號,形成信號源。基於硬體提供的信號源,系統就可以按照信號中斷來計數,計數在固定頻率下對應固定的時間,根據預設的時間參數即可產生定時中斷信號,這就是軟定時。

這裡主要講軟定時器,而硬體定時器涉及到硬體手冊這裡略過。

1. 利用內核節拍器相關定時器實現定時

linux內核有可調節的系統節拍,由於節拍依據硬體定時器的定時中斷計數得來,節拍頻率設定後,節拍周期恆定,根據節拍數可以推得精確時間。從系統啟動以來記錄的節拍數存放在全局變數jiffies中,系統啟動時自動設置jiffies為0。

#include <linux/jiffies.h>

高節拍數可以計算更高的時間精度,但是會頻繁觸發系統中斷,犧牲系統效率。

定義定時器

struct timer_list {
    struct list_head entry; // 定時器鏈表的入口
    unsigned long expires; // 定時器超時節拍數
    struct tvec_base *base; // 定時器內部值,用戶不要使用
    void (*function)(unsigned long); // 定時處理函數
    unsigned long data; // 要傳遞給定時處理函數的參數
    int slack;
};

設置節拍數expires時,可以使用函數msecs_to_jiffies將毫秒值轉化為節拍數。

初始化定時器

void init_timer(struct timer_list *timer);

註冊定時器到內核,並啟動

void add_timer(struct timer_list *timer);

刪除定時器

int del_timer(struct timer_list *timer);

如果程式運行在多核處理器上,此函數有可能導致運行出錯,建議改用del_timer_sync。

同步刪除定時器

int del_timer_sync(struct timer_list *timer);

如果程式運行在多處理器上,此函數會等待其它處理器對此定時器的操作完成。另外,此函數不能用在中斷上下文中。

修改定時值並啟動定時器

int mod_timer(struct timer_list *timer, unsigned long expires);

註意:在應用層開發過程中,一般不會使用內核的函數來設定定時器。

2. 應用層的alarm鬧鐘

在應用層開發時,設置鬧鐘參數,並啟動鬧鐘定時器非常方便

#include<unistd.h>

unsigned int alarm(unsigned int seconds);

註意:每個進程只允許設置一個鬧鐘,重覆設置會覆蓋前一個鬧鐘。

當時間到達seconds秒後,會有SIGALRM信號發送給當前進程,可以通過函數signal註冊該信號的回調處理函數callback_fun

#include <signal.h>

typedef void (*sig_t)(int);
sig_t signal(int signum, sig_t handler);

3. 利用POSIX中內置的定時器介面

設定鬧鐘適用的情形比較簡單,而為了更靈活地使用定時功能,可以用到POSIX中的定時器功能。

創建定時器

#include <time.h>

int timer_create(clockid_t clock_id, struct sigevent *evp, timer_t *timerid)

通過clock_id可以指定時鐘源,evp傳入超時通知配置參數,timerid返回被創建的定時器的id。evp如果為NULL,超時觸發時,預設發送信號SIGALRM通知進程。

clock_id是枚舉值,如下

CLOCK_REALTIME :Systemwide realtime clock.
CLOCK_MONOTONIC:Represents monotonic time. Cannot be set.
CLOCK_PROCESS_CPUTIME_ID :High resolution per-process timer.
CLOCK_THREAD_CPUTIME_ID :Thread-specific timer.
CLOCK_REALTIME_HR :High resolution version of CLOCK_REALTIME.
CLOCK_MONOTONIC_HR :High resolution version of CLOCK_MONOTONIC.

結構體sigevent

union sigval
{
    int sival_int; //integer value
    void *sival_ptr; //pointer value
}

struct sigevent
{
    int sigev_notify; //notification type
    int sigev_signo; //signal number
    union sigval sigev_value; //signal value
    void (*sigev_notify_function)(union sigval);
    pthread_attr_t *sigev_notify_attributes;
}

類型timer_t

#ifndef _TIMER_T
#define _TIMER_T
typedef int timer_t; /* timer identifier type */
#endif /* ifndef _TIMER_T */

設置定時器,比如初次觸發時間,迴圈觸發的周期等。設置完成後啟動定時器。

int timer_settime(timer_t timerid, int flags, const struct itimerspec *value, struct itimerspec *ovalue);

struct timespec{
   time_t tv_sec;
   long tv_nsec;  
};

struct itimerspec {
   struct timespec it_interval; 
   struct timespec it_value;   
}; 

獲取定時剩餘時間

int timer_gettime(timer_t timerid, struct itimerspec *value);

獲取定時器超限的次數

int timer_getoverrun(timer_t timerid);

定時器超時後發送的同一個信號如果掛起未處理,那麼在下次超時發生後,上一個信號會丟失,這就是定時器的超限。

刪除定時器

int timer_delete (timer_t timerid);

示例:超時觸發信號

#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>

void sig_handler(int signo)
{
    time_t t;
    char str[32];

    time(&t);
    strftime(str, sizeof(str), "%T", localtime(&t));

    printf("handler %s::%d\n", str, signo);
}

int main()
{
    struct sigaction act;
    memset(&act, 0, sizeof(act));
    act.sa_handler = sig_handler;
    act.sa_flags = 0;

    sigemptyset(&act.sa_mask);
    if (sigaction(SIGUSR1, &act, NULL) == -1) {
        perror("fail to sigaction");
        exit(-1);
    }

    timer_t timerid;
    struct sigevent evp;
    memset(&evp, 0, sizeof(evp));
    // 定時器超時觸發信號 SIGUSR1
    evp.sigev_notify = SIGEV_SIGNAL;
    evp.sigev_signo = SIGUSR1;
    if (timer_create(CLOCK_REALTIME, &evp, &timerid) == -1) {
        perror("fail to timer_create");
        exit(-1);
    }

    // 設置初始觸發時間4秒,之後每2秒再次觸發
    struct itimerspec its;
    its.it_value.tv_sec = 4;
    its.it_value.tv_nsec = 0;
    its.it_interval.tv_sec = 2;
    its.it_interval.tv_nsec = 0;
    if (timer_settime(timerid, 0, &its, 0) == -1) {
        perror("fail to timer_settime");
        exit(-1);
    }

    while(1);
    
    return 0;
}

上面的代碼中註冊信號響應回調用了函數sigaction,其實這裡用函數signal也可以的。

示例:超時啟動子線程

#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

void timer_thread(union sigval v)
{
    time_t t;
    char str[32];

    time(&t);
    strftime(str, sizeof(str), "%T", localtime(&t));

    printf("timer_thread %s::%d\n", str, v.sival_int);
}

int main()
{
    timer_t timerid;
    struct sigevent evp;
    memset(&evp, 0, sizeof(evp));
    evp.sigev_notify = SIGEV_THREAD;
    evp.sigev_value.sival_int = 123;
    evp.sigev_notify_function = timer_thread;
    if (timer_create(CLOCK_REALTIME, &evp, &timerid) == -1) {
        perror("fail to timer_create");
        exit(-1);
    }

    struct itimerspec its;
    its.it_value.tv_sec = 4;
    its.it_value.tv_nsec = 0;
    its.it_interval.tv_sec = 2;
    its.it_interval.tv_nsec = 0;
    if (timer_settime(timerid, 0, &its, 0) == -1) {
        perror("fail to timer_settime");
        exit(-1);
    }

    while(1);
    
    return 0;
}

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本文主要介紹火焰圖及使用技巧,學習如何使用火焰圖快速定位軟體的性能卡點。 結合最佳實踐實戰案例,幫助讀者更加深刻地理解火焰圖構造及原理,理解 CPU 耗時,定位性能瓶頸。 ...
  • 微服務Docker打包 現在的微服務時代,你的代碼沒個微服務、分散式人家都會覺得低端,當然!對於我們開發人員來說,掌握這些技術意味著漲薪。 ​ 我們項目中用到了多個微服務,我們上一節課程打包用的是手動上傳,但是很麻煩,有沒有更好的方式呢,是有的,我們可以直接通過idea將我們的微服務打包成Docke ...
  • 線程 筆記目錄:(https://www.cnblogs.com/wenjie2000/p/16378441.html) 程式(program) 是為完成特定任務、用某種語言編寫的一組指令的集合。簡單的說:就是我們寫的代碼 進程 進程是指運行中的程式,比如我們使用QQ,就啟動了一個進程,操作系統就會 ...
  • 摘要:都說 Python 簡單快捷,那本篇博客就為大家帶來一些實用的 Python 技巧,而且僅需要 1 行代碼,就可以解決一些小問題。 本文分享自華為雲社區《你猜 1 行Python代碼能幹什麼呢?神奇的單行 Python 代碼》,作者:夢想橡皮擦。 1 行代碼的由來 都說 Python 簡單快捷 ...
  • 文章來源:【公眾號:同程藝龍技術中心】 背景 會員系統是一種基礎系統,跟公司所有業務線的下單主流程密切相關。如果會員系統出故障,會導致用戶無法下單,影響範圍是全公司所有業務線。所以,會員系統必須保證高性能、高可用,提供穩定、高效的基礎服務。 隨著同程和藝龍兩家公司的合併,越來越多的系統需要打通同程 ...
  • 《Python項目開發實戰》PDF高清版免費下載地址 ↑ ↑ ↑ ↑ ↑ ↑ ↑ 點擊即可下載 內容簡介 · · · · · · 通過實際項目增強你的Python技能 通過理論和實踐的結合以及實際可操作的指導,本書將為你呈現真實世界中的Python編程。書中的實際教程聚焦於功能,覆蓋了基本的創建應用 ...
  • using()的本質是C#中的語法糖。 using()語句編譯後生成 try{...} finally{...} 語句,進而確保資源釋放。 using()是程式中使用非托管資源的最佳方式。 ...
  • 此篇文章演示基本的基於docker部署.netcore服務,liunx系統騰訊雲ubuntu,.net core版本3.1。 1.安裝docker apt install docker.io 2.拉取.net core依賴鏡像 docker pull mcr.microsoft.com/dotnet ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...