主頁 > 移動端開發 > 在Linux中每秒更新時鐘的最佳方法

在Linux中每秒更新時鐘的最佳方法

2021-10-15 09:30:12 移動端開發

當我們談論在 Linux 中每秒更新一個時鐘時,我認為類似于以下代碼的東西是我想到的。

while :; do date  %T; sleep 1; done

這段代碼總是困擾著我,因為有一個無限回圈每秒運行兩個命令,這意味著背景關系切換會導致處理器使用率出現輕微峰值。

考慮到這一點,我想知道:這真的是最好的方法嗎?有沒有更聰明的方法來做到這一點?例如,如果我想用像 C 這樣的低級語言重現這個,那么唯一的方法是否仍然是一個無限回圈,一個printf顯示時鐘和一個一秒sleep也就是說,有沒有辦法避免這種背景關系切換并以更智能的方式使用CPU?

uj5u.com熱心網友回復:

你不想完全避免背景關系切換,你想讓內核在 99% 的秒內運行其他東西,它沒有運行/usr/bin/date以將時間格式化為字串并將write(2)格式化為標準輸出。(或者讓這個 CPU 內核進入睡眠狀態,以節省電力。但這實際上不算作背景關系切換,因為軟體從不更改頁表或保存/恢復 FP 暫存器。即使是系統呼叫也完全進入內核保存/恢復整數暫存器,并且在沒有硬體修復的 Intel CPU 上啟用軟體 Meltdown 緩解實際上會更改頁表,但是。Spectre 緩解清除分支預測歷史的成本更高。)

(如果您沒有在 Linux 文本控制臺上運行它,如 ctrl alt F2,則需要背景關系切換到您的終端模擬器或 sshd 或任何控制偽終端主端的任何東西。僅在后一種情況將寫入視頻 RAM 實際上發生在由 進行的write(0, buf, len)系統呼叫中date,即在該行程的背景關系中。)

如果您想最小化背景關系切換(以及一般的系統呼叫),您需要在單個行程中進行睡眠和寫入。但這在 bash 中是不可能的;它沒有內置睡眠。(Bash確實必須printf '%(%T)T\n' $EPOCHSECONDS列印當前時間,但忙于等待會很糟糕)。你想用 C 撰寫一個程式,它只做睡眠和時間列印。

使用固定 1 秒延遲的回圈將累積錯誤,因為它在date啟動和退出之后才開始下一秒,并且 shell 已經分叉/執行/usr/bin/sleep下一次迭代(加上sleep可執行檔案中的啟動開銷)。


無需撰寫自己的 C 程式,您可以通過 using 將watch -p -t --exec降低到每秒一次 fork/exec(以及一堆其他系統呼叫),它以間隔直接使用 fork/exec 而不是/bin/sh -c.

  • -t 告訴它不要列印標題(包括時間)
  • -p(精確)查詢當前時間clock_gettime并使用nanosleep,避免誤差累積,每次都瞄準同一目標時間。(默認設定是在命令運行之間的固定時間間隔內休眠,無論花費多長時間。)

我們可以跟蹤它的系統呼叫,看看它做了什么。(我使用了更短的睡眠間隔,所以我不必讓它坐那么久。)請注意,clock_gettime它沒有出現,strace因為它沒有進入內核;glibc 包裝器呼叫 vDSO 實作。內核匯出的代碼(映射到每個用戶空間行程)讀取內核匯出的資料:由內核的定時器中斷更新的粗略時間,以及用于rdtsc從當前粗略時間插入偏移量的比例因子/偏移量,因為現代 x86-64 系統具有可從用戶空間訪問的精確恒定頻率計數器

watch實際上列印在“替代”螢屏上,所以當它退出時輸出從你的終端消失;輸出的那部分是為了示例目的而偽造的。其余部分是從終端模擬器復制/粘貼的,并添加了## 注釋。 )

  # use strace -f ...  to trace into child processes, and see all the syscalls from date
$ strace -o foo.tr    watch -p -t -n 0.5 --exec   date  %T
22:31:54
control-C

$ less foo.tr
... startup stuff from watch, including some terminal-size ioctl

pipe([3, 4])                            = 0
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f0f744fba10) = 3832377
   # Linux implements fork() in terms of clone(2)
close(4)                                = 0
fcntl(3, F_GETFL)                       = 0 (flags O_RDONLY)
newfstatat(3, "", {st_mode=S_IFIFO|0600, st_size=0, ...}, AT_EMPTY_PATH) = 0
   # (IDK why it's doing an fstat on the pipe FD)
read(3, "22:16:45\n", 4096)             = 9
read(3, "", 4096)                       = 0
   # reads from the pipe until EOF
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=3832377, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
close(3)                                = 0
   # then closes it
wait4(3832377, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 3832377
rt_sigaction(SIGTSTP, {sa_handler=SIG_IGN, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f0f7453ada0}, {sa_handler=0x7f0
f746f4790, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f0f7453ada0}, 8) = 0
   # and waits for the child PID
write(1, "\33[?1049h\33[22;0;0t\33[1;42r\33(B\33[m\33["..., 46) = 46
   # clears the screen and moves cursor to the top left
write(1, "22:16:45\33[42;134H", 17)     = 17
   # and copies what it read from the pipe earlier.
rt_sigaction(SIGTSTP, {sa_handler=0x7f0f746f4790, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f0f7453ada0}, NULL, 8) =
 0

## There's a clock_gettime() somewhere, probably here,
##  but the vDSO implementation avoids entering the kernel so strace doesn't see it.
clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=0, tv_nsec=498451000}, NULL) = 0
  # After calculating the exact time until the next event
  # tell the kernel we're done until then

  # Then the cycle starts over again when it wakes
pipe([3, 4])                            = 0
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f0f744fba10) = 3832378
close(4)                                = 0
fcntl(3, F_GETFL)                       = 0 (flags O_RDONLY)
...

watchwithout-t將列印當前時間作為其標題的一部分。所以如果這是你想要的,你就不再需要date了。

不過,這并不有一個選項,運行任何程式。如果當前時區發生變化,它每次都會統計 /etc/localtime 。

您可以使用/bin/true,但這仍然需要分叉/執行并運行其動態聯結器啟動開銷。或者您可以使用watch --exec /non-existant并讓它execve每次都列印錯誤。但即便如此,它仍然會在嘗試執行之前分叉,創建一個新的 PID 并對其進行背景關系切換。

uj5u.com熱心網友回復:

我懷疑有沒有辦法避免背景關系切換——或者如果有辦法,它會比涉及sleep.

以您為代表的技術的真正問題

while :; do date  %T; sleep 1; done

是他們失去了時間。例如,如果我運行這個修改,合并我自己的dateexpr程式,除其他外,它具有使用亞秒的能力:

while :; do dateexpr  %H:%M:%.2S now; sleep 1; done

,這是我看到的:

10:13:48.40
10:13:49.41
10:13:50.43
10:13:51.44
10:13:52.46
10:13:53.47
10:13:54.49
10:13:55.50

所以看起來“背景關系切換”——啟動每個sleep和/datedateexpr行程的開銷——需要 10-20 毫秒。

我寫了一個程式(用 C 語言)來解決這個問題。它持續監視時間,并計算出一個略小于一秒的睡眠時間值,這樣它就可以每秒準確地呼叫一次子命令。它看起來像這樣:

$ synchro dateexpr  %H:%M:%.2S now
10:17:11.01
10:17:12.01
10:17:13.01
10:17:14.01
10:17:15.01
10:17:16.01
10:17:17.01

在啟動被呼叫的行程時仍然有 10 毫秒的錯誤,但至少它不會累積。

但是為了完成它的作業,我的synchro程式不得不進行更多的系統呼叫,所以實際上有更多的背景關系切換,而不是更少。

但是,當然,一般來說sleep,當您想暫停一段時間時,呼叫類似的方法是正確的做法,因為您明確地放棄了控制權,并且作業系統知道它根本不必安排您的行程運行,因此,您在睡覺時對系統的其余部分施加的負載最小。是的,涉及到幾個背景關系切換,但它們似乎很小,付出的代價很小,而且正如我所說,我認為您無法繞過它們。

我想知道是否有一種方法可以完全在用戶空間中運行時鐘或計時器,也許這也是您要問的。但我懷疑有沒有辦法,因為沒有什么 [腳注 1] 你可以在用戶空間中得到任何關于時間或時鐘的資訊——這些資訊都在內核中,這意味著它需要一個系統打電話給它。

(當然,我在這里專門考慮在傳統的多任務作業系統下運行的行程。如果您正在為帶有 RTC 的微處理器撰寫嵌入式代碼,那么毫無疑問您可以完全按照自己的意愿去做,而無需在全部。)

有一種可能性很小,至少在某些(也許現在大多數?)Linux 版本下,有一種稱為vDSO的機制,它使某些系統呼叫能夠在用戶空間中執行,而無需背景關系切換。接受這種特殊處理的系統呼叫的首要候選物件是gettimeofday和 相關的。因此,在使用 vDSO 的系統上,您可以撰寫一個帶有忙等待回圈的程式,重復呼叫gettimeofdaytimeclock_gettime,如果那些也使用 vDSO)直到到達所需的時間,并且由于 vDSO,您將在沒有背景關系切換的情況下執行此操作。但當然,忙著等待是一個幾乎無可救藥的可怕想法,所以我不是認真推薦這個。(這就是我在本答案開頭說的“如果有辦法,它會比涉及 的技術更浪費sleep。”的意思。)


Footnote 1. I said there's "nothing you can get your hands on in user space that gives you any information about time or clocks", but that's not quite true. As the comments from Peter Cordes remind us, Intel processors, at least, give us the "Time Stamp Counter" and the rdtsc instruction to read it. This is a potentially vital — but also hugely problematic! — tool for writing certain high-precision timing applications, but I've never used it so I won't try to explain it or its caveats.

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/315040.html

標籤:C linux 贝壳 操作系统 低级

上一篇:如何在linux上鏈接mach-o格式的目標檔案?

下一篇:如何使用awk在txt檔案的開頭插入一列?

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more