成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

血淚教訓(xùn):Linux 定時(shí)器踩坑指南,看完少走三年彎路

系統(tǒng) Linux
今天咱們就來徹底搞懂Linux定時(shí)器的前世今生,保證看完之后你也能成為定時(shí)器專家!

大家好,我是小康。

朋友們,今天要跟大家聊個(gè)讓無數(shù)程序員頭疼的話題——Linux定時(shí)器。別看這玩意兒平時(shí)不起眼,但真要用起來,坑多得你想哭??

一、寫在前面的話

你有沒有遇到過這樣的場景?

  • 寫個(gè)網(wǎng)絡(luò)程序,需要定期發(fā)送心跳包
  • 做個(gè)游戲服務(wù)器,要每秒更新玩家狀態(tài)
  • 搞個(gè)監(jiān)控系統(tǒng),定時(shí)檢查服務(wù)是否正常
  • 甚至只是想讓程序延時(shí)幾秒再執(zhí)行某個(gè)操作

如果你點(diǎn)頭了,那恭喜你——定時(shí)器絕對是你繞不開的技能點(diǎn)!

我記得剛開始寫Linux程序的時(shí)候,遇到需要定時(shí)執(zhí)行任務(wù)的場景,第一反應(yīng)就是Google一下"Linux定時(shí)器怎么用"。結(jié)果搜出來一堆a(bǔ)larm()、setitimer()、timerfd_create()...看得我一頭霧水。

到底該用哪個(gè)?它們有什么區(qū)別?為什么有這么多種定時(shí)器?

相信很多小伙伴都有過同樣的困惑。今天咱們就來徹底搞懂Linux定時(shí)器的前世今生,保證看完之后你也能成為定時(shí)器專家!

二、第一代:古老而經(jīng)典的alarm()

1. 最簡單的開始

話說回來,Linux最早的定時(shí)器就是alarm(),簡單到爆:

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

void timeout_handler(int sig) {
    printf("時(shí)間到!該起床搬磚了!\n");
}

int main() {
    signal(SIGALRM, timeout_handler);
    alarm(5);  // 5秒后觸發(fā)
    pause();   // 等待信號
    return 0;
}

看起來挺簡單的對吧? 但是兄弟,這里面的坑可不少:

  • 只能精確到秒 - 你想要毫秒級定時(shí)?不好意思,做不到
  • 全局只能有一個(gè) - 你在一個(gè)地方調(diào)用了alarm(10),另一個(gè)地方又調(diào)用alarm(5),前面那個(gè)就被覆蓋了
  • 容易被系統(tǒng)調(diào)用中斷 - sleep()、read()這些函數(shù)被SIGALRM打斷后會(huì)提前返回

2. 真實(shí)踩坑經(jīng)歷

我當(dāng)年就因?yàn)椴恢繿larm()是全局唯一的,在一個(gè)多模塊的項(xiàng)目里用了好幾個(gè)alarm(),結(jié)果定時(shí)器莫名其妙地不按預(yù)期工作。調(diào)試了好久才發(fā)現(xiàn)是被互相覆蓋了。

三、第二代:更靈活的setitimer()

1. 進(jìn)步在哪里?

既然alarm()這么局限,Linux就推出了升級版——setitimer():

#include <sys/time.h>
#include <signal.h>
#include <stdio.h>

void timer_handler(int sig) {
    staticint count = 0;
    printf("第%d次定時(shí)觸發(fā)!\n", ++count);
}

int main() {
    struct itimerval timer;
    
    signal(SIGALRM, timer_handler);
    
    // 設(shè)置定時(shí)器:1秒后開始,每0.5秒觸發(fā)一次
    timer.it_value.tv_sec = 1;     // 首次觸發(fā)時(shí)間
    timer.it_value.tv_usec = 0;
    timer.it_interval.tv_sec = 0;   // 重復(fù)間隔
    timer.it_interval.tv_usec = 500000; // 0.5秒 = 500000微秒
    
    setitimer(ITIMER_REAL, &timer, NULL);
    
    while(1) {
        pause();  // 等待信號
    }
    
    return 0;
}

這就厲害多了!

  • 支持微秒級精度
  • 可以設(shè)置周期性觸發(fā)
  • 有三種定時(shí)器類型(REAL、VIRTUAL、PROF)

2. 但是...新的問題來了

雖然setitimer()比alarm()強(qiáng)大,但還是有些讓人頭疼的地方:

  • 還是基于信號 - 信號處理的那些坑一個(gè)都沒少
  • 每個(gè)進(jìn)程還是只能有一個(gè)ITIMER_REAL - 多個(gè)定時(shí)器?也不支持
  • 信號可能丟失 - 在信號處理函數(shù)執(zhí)行期間,新的信號可能被丟棄

四、第三代:專業(yè)級的POSIX定時(shí)器

1. 更加專業(yè)的選擇

在timerfd出現(xiàn)之前,還有一個(gè)重要的過渡產(chǎn)品——POSIX定時(shí)器(timer_create系列)。這玩意兒是POSIX標(biāo)準(zhǔn)定義的,比setitimer()更專業(yè),但又沒有timerfd()那么現(xiàn)代化。

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

timer_t timerid;
int timer_count = 0;

void timer_handler(int sig, siginfo_t *si, void *uc) {
    timer_t *tidp = si->si_value.sival_ptr;
    printf("第%d次POSIX定時(shí)器觸發(fā)!timer_id: %p\n", ++timer_count, tidp);
}

int main() {
    struct sigevent sev;
    struct itimerspec its;
    struct sigaction sa;
    
    // 設(shè)置信號處理函數(shù)
    sa.sa_flags = SA_SIGINFO;
    sa.sa_sigaction = timer_handler;
    sigemptyset(&sa.sa_mask);
    sigaction(SIGUSR1, &sa, NULL);
    
    // 創(chuàng)建定時(shí)器
    sev.sigev_notify = SIGEV_SIGNAL;
    sev.sigev_signo = SIGUSR1;
    sev.sigev_value.sival_ptr = &timerid;
    
    if (timer_create(CLOCK_REALTIME, &sev, &timerid) == -1) {
        perror("timer_create failed");
        return-1;
    }
    
    // 設(shè)置定時(shí)器參數(shù):1秒后開始,每500ms觸發(fā)一次
    its.it_value.tv_sec = 1;
    its.it_value.tv_nsec = 0;
    its.it_interval.tv_sec = 0;
    its.it_interval.tv_nsec = 500000000;  // 500ms
    
    timer_settime(timerid, 0, &its, NULL);
    
    printf("POSIX定時(shí)器啟動(dòng),按Ctrl+C退出\n");
    
    while (1) {
        pause();
    }
    
    timer_delete(timerid);
    return 0;
}

看起來是不是比setitimer()復(fù)雜多了? 但功能也更強(qiáng)大:

2. POSIX定時(shí)器的優(yōu)勢

  • 支持多個(gè)定時(shí)器 - 終于可以創(chuàng)建多個(gè)了!每個(gè)都有獨(dú)立的timer_t標(biāo)識
  • 納秒級精度 - 和timerfd一樣精確
  • 靈活的通知方式 - 不僅可以發(fā)信號,還可以創(chuàng)建線程或者什么都不做
  • 更好的信息傳遞 - 可以通過siginfo_t傳遞額外信息

3. 三種通知方式

POSIX定時(shí)器最酷的地方是支持三種通知方式:

(1) 信號通知(最常用)

sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = SIGUSR1;

(2) 線程通知(高級用法)

sev.sigev_notify = SIGEV_THREAD;
sev.sigev_notify_function = thread_handler;
sev.sigev_notify_attributes = NULL;

(3) 無通知(輪詢模式)

sev.sigev_notify = SIGEV_NONE;
// 然后用timer_gettime()主動(dòng)查詢

4. 我的使用心得

POSIX定時(shí)器我在一個(gè)服務(wù)器監(jiān)控項(xiàng)目中用過,需要同時(shí)監(jiān)控多個(gè)不同的指標(biāo),每個(gè)指標(biāo)的檢查頻率都不一樣。用setitimer()根本搞不定,但POSIX定時(shí)器就很合適:

timer_t cpu_timer, memory_timer, disk_timer, network_timer;

// CPU使用率:每秒檢查一次
create_posix_timer(&cpu_timer, SIGUSR1, 1000);

// 內(nèi)存使用率:每30秒檢查一次
create_posix_timer(&memory_timer, SIGUSR2, 300000);

// 磁盤IO:每分鐘檢查一次  
create_posix_timer(&disk_timer, SIGRTMIN, 600000);

// 網(wǎng)絡(luò)連接:每分鐘檢查一次
create_posix_timer(&network_timer, SIGRTMIN+1, 600000);

這樣每個(gè)監(jiān)控任務(wù)都有自己獨(dú)立的定時(shí)器,互不干擾,代碼邏輯也很清晰。

但是...POSIX定時(shí)器也有它的問題:

  • 還是基于信號 - 信號處理的坑一個(gè)都沒少
  • 代碼復(fù)雜 - 比alarm()和setitimer()復(fù)雜多了
  • 移植性問題 - 有些老系統(tǒng)支持不夠好

所以雖然功能強(qiáng)大,但在現(xiàn)代Linux開發(fā)中,大家更傾向于直接用timerfd。

五、第四代:現(xiàn)代化的timerfd

1. 革命性的改變

到了Linux 2.6.25,終于迎來了真正的現(xiàn)代化定時(shí)器——timerfd!

這東西徹底改變了游戲規(guī)則:把定時(shí)器變成了文件描述符!

#include <sys/timerfd.h>
#include <unistd.h>
#include <stdio.h>
#include <stdint.h>

int main() {
    int timer_fd;
    struct itimerspec timer_spec;
    uint64_t expirations;
    
    // 創(chuàng)建定時(shí)器文件描述符
    timer_fd = timerfd_create(CLOCK_REALTIME, 0);
    if (timer_fd == -1) {
        perror("timerfd_create failed");
        return-1;
    }
    
    // 設(shè)置定時(shí)器:2秒后開始,每1秒觸發(fā)一次
    timer_spec.it_value.tv_sec = 2;
    timer_spec.it_value.tv_nsec = 0;
    timer_spec.it_interval.tv_sec = 1;
    timer_spec.it_interval.tv_nsec = 0;
    
    timerfd_settime(timer_fd, 0, &timer_spec, NULL);
    
    printf("定時(shí)器啟動(dòng),等待觸發(fā)...\n");
    
    for (int i = 0; i < 5; i++) {
        // 就像讀文件一樣讀取定時(shí)器
        ssize_t bytes = read(timer_fd, &expirations, sizeof(expirations));
        if (bytes == sizeof(expirations)) {
            printf("定時(shí)器觸發(fā)了%llu次\n", expirations);
        }
    }
    
    close(timer_fd);
    return 0;
}

這簡直是質(zhì)的飛躍!

2. 為什么timerfd這么香?

  • 文件描述符 - 可以用select()、poll()、epoll()監(jiān)聽,完美融入事件循環(huán)
  • 納秒級精度 - 想要多精確有多精確
  • 無限個(gè)定時(shí)器 - 想創(chuàng)建多少個(gè)就創(chuàng)建多少個(gè)
  • 不依賴信號 - 再也不用擔(dān)心信號處理的各種坑
  • 更好的并發(fā)支持 - 在事件驅(qū)動(dòng)的程序中表現(xiàn)出色

3. 配合epoll使用更香

#include <stdio.h>
#include <unistd.h>
#include <sys/timerfd.h>
#include <sys/epoll.h>
#include <stdint.h>

int main() {
    int timerfd1, timerfd2, epollfd;
    struct itimerspec its;
    struct epoll_event ev, events[10];
    uint64_texp;
    
    // 創(chuàng)建兩個(gè)定時(shí)器
    timerfd1 = timerfd_create(CLOCK_REALTIME, 0);
    timerfd2 = timerfd_create(CLOCK_REALTIME, 0);
    
    // 創(chuàng)建epoll實(shí)例
    epollfd = epoll_create1(0);
    
    // 將定時(shí)器加入epoll監(jiān)聽
    ev.events = EPOLLIN;
    ev.data.fd = timerfd1;
    epoll_ctl(epollfd, EPOLL_CTL_ADD, timerfd1, &ev);
    
    ev.data.fd = timerfd2;
    epoll_ctl(epollfd, EPOLL_CTL_ADD, timerfd2, &ev);
    
    // 設(shè)置定時(shí)器1:每1秒觸發(fā)
    its.it_value.tv_sec = 1;
    its.it_value.tv_nsec = 0;
    its.it_interval.tv_sec = 1;
    its.it_interval.tv_nsec = 0;
    timerfd_settime(timerfd1, 0, &its, NULL);
    
    // 設(shè)置定時(shí)器2:每2秒觸發(fā)
    its.it_value.tv_sec = 2;
    its.it_interval.tv_sec = 2;
    timerfd_settime(timerfd2, 0, &its, NULL);
    
    printf("高性能定時(shí)器系統(tǒng)啟動(dòng)!\n");
    
    while (1) {
        int nfds = epoll_wait(epollfd, events, 10, -1);
        
        for (int n = 0; n < nfds; n++) {
            int fd = events[n].data.fd;
            read(fd, &exp, sizeof(uint64_t));
            
            if (fd == timerfd1) {
                printf("? 快速定時(shí)器觸發(fā) (1秒間隔)\n");
            } elseif (fd == timerfd2) {
                printf("?? 慢速定時(shí)器觸發(fā) (2秒間隔)\n");
            }
        }
    }
    
    return 0;
}

這就是現(xiàn)代Linux程序的標(biāo)準(zhǔn)寫法! 事件驅(qū)動(dòng),高性能,代碼還清晰易懂。

六、實(shí)際項(xiàng)目中該選哪個(gè)?

1. 快速?zèng)Q策指南

如果你只是想要個(gè)簡單的定時(shí):

alarm(5);  // 夠用了,別想太多

如果需要周期性定時(shí),而且精度要求不高:

setitimer(ITIMER_REAL, &timer, NULL);  // 經(jīng)典選擇

如果需要多個(gè)定時(shí)器,但不想用太新的API:

timer_create() + timer_settime();  // POSIX標(biāo)準(zhǔn),兼容性好

如果是現(xiàn)代項(xiàng)目,特別是網(wǎng)絡(luò)服務(wù)器:

timerfd_create() + epoll();  // 這就對了!

2. 性能對比

我之前做過一個(gè)簡單的功能測試,看看各種定時(shí)器的支持能力:

  • alarm(): 全局只能有1個(gè),新的會(huì)覆蓋舊的
  • setitimer(): 每種類型只能1個(gè)(REAL、VIRTUAL、PROF),最多3個(gè)
  • POSIX定時(shí)器: 支持多個(gè),具體數(shù)量受系統(tǒng)限制(通常幾百個(gè)),但信號處理開銷較大
  • timerfd(): 支持多個(gè),數(shù)量主要受文件描述符限制

實(shí)際項(xiàng)目中的選擇建議:

  • 如果只需要1-2個(gè)定時(shí)器:setitimer()夠用
  • 如果需要多個(gè)定時(shí)器:POSIX定時(shí)器和timerfd都可以,但timerfd在事件驅(qū)動(dòng)程序中更高效
  • 如果是高并發(fā)網(wǎng)絡(luò)程序:timerfd() + epoll()性能最好,因?yàn)榭梢院推渌鸌/O事件統(tǒng)一處理

3. 兼容性考慮

  • alarm()/setitimer(): 幾乎所有Unix系統(tǒng)都支持
  • POSIX定時(shí)器: 理論上是POSIX標(biāo)準(zhǔn),但實(shí)際支持情況復(fù)雜:Linux 2.6+原生支持(但可能需要鏈接 -lrt),macOS/BSD支持有限,Windows需要通過Cygwin等兼容層
  • timerfd(): Linux 2.6.25+專有,其他系統(tǒng)不支持

實(shí)際上,跨平臺(tái)的定時(shí)器API是一個(gè)普遍難題,每個(gè)操作系統(tǒng)都有自己的實(shí)現(xiàn)方式。如果你的項(xiàng)目需要真正的跨平臺(tái),可能需要:

  • 使用第三方庫(如libuv、libevent)
  • 或者針對不同平臺(tái)編寫不同的實(shí)現(xiàn)

七、進(jìn)階技巧分享

1. 高精度定時(shí)器

想要更高的精度?試試CLOCK_MONOTONIC:

timer_fd = timerfd_create(CLOCK_MONOTONIC, 0);

CLOCK_MONOTONIC不受系統(tǒng)時(shí)間調(diào)整影響,更適合做精確的間隔定時(shí)。

2. 一次性定時(shí)器

有時(shí)候你只想要一個(gè)一次性的延時(shí):

timer_spec.it_value.tv_sec = 5;        // 5秒后觸發(fā)
timer_spec.it_value.tv_nsec = 0;
timer_spec.it_interval.tv_sec = 0;     // 不重復(fù)
timer_spec.it_interval.tv_nsec = 0;

3. 定時(shí)器管理器

在復(fù)雜項(xiàng)目中,你可能需要管理很多定時(shí)器。我一般會(huì)封裝一個(gè)定時(shí)器管理器:

typedef struct {
    int fd;
    void (*callback)(void *data);
    void *data;
} Timer;

// 創(chuàng)建定時(shí)器
Timer* create_timer(int interval_ms, void (*callback)(void*), void *data);
// 刪除定時(shí)器
void destroy_timer(Timer *timer);
// 在主事件循環(huán)中處理定時(shí)器事件
void handle_timer_event(Timer *timer);

這樣管理起來就清爽多了。

八、總結(jié):定時(shí)器進(jìn)化的啟示

從alarm()到timerfd(),Linux定時(shí)器的進(jìn)化史其實(shí)反映了整個(gè)系統(tǒng)編程的發(fā)展趨勢:

  • 從簡單到復(fù)雜 - 功能越來越強(qiáng)大
  • 從單一到多元 - 支持更多使用場景
  • 從同步到異步 - 更好地融入事件驅(qū)動(dòng)架構(gòu)
  • 從信號到文件描述符 - 統(tǒng)一的編程模型

如果你是新手,建議從alarm()開始理解基本概念,了解一下POSIX定時(shí)器的功能特性,然后直接跳到timerfd()學(xué)習(xí)現(xiàn)代用法。

如果你是老手,是時(shí)候把那些老舊的alarm()和setitimer()代碼重構(gòu)了。如果項(xiàng)目只在Linux上運(yùn)行,直接用timerfd();如果需要跨平臺(tái),考慮使用成熟的第三方庫。

選擇建議總結(jié):

  • 學(xué)習(xí)路徑: alarm() → POSIX定時(shí)器概念 → timerfd()實(shí)踐
  • 跨平臺(tái)項(xiàng)目: 使用libuv、libevent等成熟庫,別自己造輪子
  • Linux專項(xiàng)目: 直接用timerfd() + epoll()
  • 簡單腳本: alarm()夠用,別過度設(shè)計(jì)
責(zé)任編輯:趙寧寧 來源: 跟著小康學(xué)編程
相關(guān)推薦

2021-02-03 13:03:00

編程程序員語言

2021-03-31 08:33:17

SysTick定時(shí)器SysTick定時(shí)器

2023-01-18 23:20:25

編程開發(fā)

2024-08-13 08:48:50

2009-11-11 10:14:10

linux定時(shí)器操作系統(tǒng)

2025-02-17 11:02:11

2024-04-17 08:21:44

2023-01-10 13:53:21

Linux定時(shí)器

2010-06-13 10:50:05

職場辛酸教訓(xùn)

2022-07-27 10:39:14

Spring代碼IDEA

2025-06-24 10:00:00

智能指針代碼unique_ptr

2025-04-14 09:31:03

2021-08-03 14:33:53

cron定時(shí)器Linux命令

2021-08-11 10:10:26

Linux定時(shí)器數(shù)組

2023-12-11 09:50:35

Linux定時(shí)器

2018-11-02 08:10:58

Linuxsystemd定時(shí)器

2012-07-31 09:19:02

程序員

2022-08-17 09:01:10

數(shù)據(jù)存儲(chǔ)

2018-12-03 12:20:52

Systemd定時(shí)器Linux
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 国产欧美在线 | h视频免费看| 一级片在线观看 | 亚洲精品2 | av在线电影网 | 久久国产精品-国产精品 | 午夜影院污 | 欧美日韩久久精品 | 中文字幕在线看第二 | 国产在线精品一区 | 精品国产乱码久久久久久中文 | 久久久.com | 亚洲精品久久久久中文字幕欢迎你 | 久久爆操 | 在线精品亚洲欧美日韩国产 | 一级毛片免费视频 | 久久五月婷 | 国产精品国产三级国产aⅴ无密码 | 2018中文字幕第一页 | 无码日韩精品一区二区免费 | 日韩视频一级 | 福利视频三区 | k8久久久一区二区三区 | 久草精品视频 | 成人国产精品免费观看视频 | 在线一区二区观看 | 国产精品99一区二区 | 久久国产一区二区 | 中文字幕一区二区三区四区五区 | 国产精品亚洲一区二区三区在线观看 | 中文字幕一区二区三区四区 | 国产夜恋视频在线观看 | 91亚洲国产亚洲国产 | 成人午夜免费在线视频 | 精品久久久久久亚洲精品 | 国产露脸对白88av | 男人天堂999 | 欧美视频1区 | 欧美视频成人 | 美女爽到呻吟久久久久 | 欧美电影网 |