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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

libev源码解析——定时器原理

發(fā)布時(shí)間:2023/11/27 生活经验 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 libev源码解析——定时器原理 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

? ? ? ? 本文將回答《libev源碼解析——I/O模型》中拋出的兩個(gè)問題。(轉(zhuǎn)載請(qǐng)指明出于breaksoftware的csdn博客)

? ? ? ? 對(duì)于問題1:為什么backend_poll函數(shù)需要指定超時(shí)?我們讓其一直等待到有事件發(fā)生不是更好么?

? ? ? ? 答案是“必須要指定超時(shí)”。為什么呢?在《libev源碼解析——總覽》中,我們拋出過一個(gè)問題:定時(shí)器和事件是如何關(guān)聯(lián)的?因?yàn)閘ibev是一個(gè)事件庫,所以我們需要將定時(shí)器的邏輯也轉(zhuǎn)換成事件相關(guān)操作。

? ? ? ? 我們看下其實(shí)現(xiàn)原理。libev在初始化默認(rèn)循環(huán)時(shí)調(diào)用了ev_default_loop方法,其會(huì)在底層調(diào)用evpipe_init方法。它會(huì)通過eventfd創(chuàng)建一個(gè)永遠(yuǎn)等不到的事件。這樣我們就可以調(diào)整等待該事件的超時(shí)時(shí)間來達(dá)到定時(shí)執(zhí)行的目的。

static void noinline ecb_cold
evpipe_init (EV_P)
{if (!ev_is_active (&pipe_w)){int fds [2];# if EV_USE_EVENTFDfds [0] = -1;fds [1] = eventfd (0, EFD_NONBLOCK | EFD_CLOEXEC);if (fds [1] < 0 && errno == EINVAL)fds [1] = eventfd (0, 0);if (fds [1] < 0)
# endif{while (pipe (fds))ev_syserr ("(libev) error creating signal/async pipe");fd_intern (fds [0]);}evpipe [0] = fds [0];if (evpipe [1] < 0)evpipe [1] = fds [1]; /* first call, set write fd */else{/* on subsequent calls, do not change evpipe [1] *//* so that evpipe_write can always rely on its value. *//* this branch does not do anything sensible on windows, *//* so must not be executed on windows */dup2 (fds [1], evpipe [1]);close (fds [1]);}fd_intern (evpipe [1]);ev_io_set (&pipe_w, evpipe [0] < 0 ? evpipe [1] : evpipe [0], EV_READ);ev_io_start (EV_A_ &pipe_w);ev_unref (EV_A); /* watcher should not keep loop alive */}
}

? ? ? ? 因?yàn)槎〞r(shí)器并非是由事件觸發(fā)而執(zhí)行,而是由于事件沒有觸發(fā)導(dǎo)致等待超時(shí)而執(zhí)行。所以backend_poll函數(shù)指針調(diào)用時(shí),不可以一直等待下去,而要傳遞超時(shí)時(shí)間。從而讓libev中利用“永遠(yuǎn)等不到的事件”相關(guān)的監(jiān)視器有機(jī)會(huì)執(zhí)行。

? ? ? ? 利用等待超時(shí)這個(gè)思路非常有意思。但是又面臨另一個(gè)問題,超時(shí)時(shí)間的選擇?比如我們現(xiàn)在有兩個(gè)定時(shí)器:2秒一次和3秒一次,那么超時(shí)時(shí)間該設(shè)置成多少呢?如果設(shè)置成2秒超時(shí),那么3秒一次的定時(shí)器將被延期1秒執(zhí)行(需要等待到第二個(gè)周期)。如果設(shè)置為3秒超時(shí),2秒一次的定時(shí)器也將被延期1秒執(zhí)行。如果設(shè)置成1秒超時(shí),則超時(shí)導(dǎo)致循環(huán)的次數(shù)增多……這種固定超時(shí)的方案怎么都不太好。那么libev是如何解決這個(gè)問題的呢?

? ? ? ? libev在實(shí)現(xiàn)的內(nèi)部不會(huì)有“定時(shí)”這樣的概念,也就是說每次事件等待的時(shí)長是不確定的。這也是為什么各個(gè)IO模型需要暴露backend_poll方法的原因——需要每次指定超時(shí)的時(shí)間。

? ? ? ? 那這個(gè)超時(shí)時(shí)間怎么計(jì)算?每個(gè)需要使用等待超時(shí)功能觸發(fā)的監(jiān)視器,都會(huì)在一個(gè)結(jié)構(gòu)中保存下次觸發(fā)的時(shí)間。以上面例子為例,并且假設(shè)沒有其他事件的干擾,假如現(xiàn)在時(shí)間是12:00:00,則2秒一次定時(shí)器監(jiān)視器(后稱2秒監(jiān)視器)的“下次執(zhí)行時(shí)間”為12:00:02;3秒一次的定時(shí)器監(jiān)視器(后稱3秒監(jiān)視器)的“下次執(zhí)行時(shí)間”為12:00:03。那么本次等待的時(shí)間是離當(dāng)前時(shí)間最近的2秒監(jiān)視器“下次執(zhí)行時(shí)間”減去當(dāng)前時(shí)間,即12:00:02-12:00:00=2秒。等到時(shí)間為12:00:02時(shí),2秒定時(shí)器會(huì)被執(zhí)行,并且其“下次執(zhí)行時(shí)間”修改成12:00:04。假設(shè)2秒定時(shí)器和本次循環(huán)中邏輯的執(zhí)行時(shí)間消耗了0.5秒,此時(shí)時(shí)鐘已經(jīng)走到12:00:02.5。此時(shí)離現(xiàn)在最近的“下次執(zhí)行時(shí)間”是3秒監(jiān)視器,則下次循環(huán)的等待時(shí)間是12:00:03-12:00:02.5=0.5秒。于是12:00:03時(shí),3秒監(jiān)視器會(huì)被執(zhí)行。

? ? ? ? 上面例子解釋了libev超時(shí)時(shí)間選擇的基本原理。當(dāng)然實(shí)際實(shí)現(xiàn)比這個(gè)稍微復(fù)雜一點(diǎn),因?yàn)樗紤]相對(duì)時(shí)間定時(shí)器、絕對(duì)時(shí)間定時(shí)器、其他一些用戶設(shè)置的事件以及各種IO模型的默認(rèn)等待時(shí)間。

/* calculate blocking time */{ev_tstamp waittime  = 0.;ev_tstamp sleeptime = 0.;/* remember old timestamp for io_blocktime calculation */ev_tstamp prev_mn_now = mn_now;/* update time to cancel out callback processing overhead */time_update (EV_A_ 1e100);/* from now on, we want a pipe-wake-up */pipe_write_wanted = 1;ECB_MEMORY_FENCE; /* make sure pipe_write_wanted is visible before we check for potential skips */if (expect_true (!(flags & EVRUN_NOWAIT || idleall || !activecnt || pipe_write_skipped))){waittime = MAX_BLOCKTIME;if (timercnt){ev_tstamp to = ANHE_at (timers [HEAP0]) - mn_now;if (waittime > to) waittime = to;}#if EV_PERIODIC_ENABLEif (periodiccnt){ev_tstamp to = ANHE_at (periodics [HEAP0]) - ev_rt_now;if (waittime > to) waittime = to;}
#endif/* don't let timeouts decrease the waittime below timeout_blocktime */if (expect_false (waittime < timeout_blocktime))waittime = timeout_blocktime;/* at this point, we NEED to wait, so we have to ensure *//* to pass a minimum nonzero value to the backend */if (expect_false (waittime < backend_mintime))waittime = backend_mintime;/* extra check because io_blocktime is commonly 0 */if (expect_false (io_blocktime)){sleeptime = io_blocktime - (mn_now - prev_mn_now);if (sleeptime > waittime - backend_mintime)sleeptime = waittime - backend_mintime;if (expect_true (sleeptime > 0.)){ev_sleep (sleeptime);waittime -= sleeptime;}}}#if EV_FEATURE_API++loop_count;
#endifassert ((loop_done = EVBREAK_RECURSE, 1)); /* assert for side effect */backend_poll (EV_A_ waittime);

? ? ? ? 基于上面的分析,對(duì)于問題2:“符合條件的監(jiān)視器”是否可以表述為“本次觸發(fā)事件對(duì)應(yīng)的監(jiān)視器”?答案依然是否定的。因?yàn)槎〞r(shí)器監(jiān)視器對(duì)應(yīng)的事件永遠(yuǎn)也不會(huì)被等待到,而它被執(zhí)行只是因?yàn)闀r(shí)間到了。 ? ? ?

總結(jié)

以上是生活随笔為你收集整理的libev源码解析——定时器原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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