激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

Linux|Centos|Ubuntu|系統進程|Fedora|注冊表|Bios|Solaris|Windows7|Windows10|Windows11|windows server|

服務器之家 - 服務器系統 - Linux - x86 Linux 下實現 10us 誤差的高精度延時

x86 Linux 下實現 10us 誤差的高精度延時

2021-08-16 23:02Linux中國董文會 Linux

在 Linux 下實現高精度延時,網上所能找到的大部分方法只能實現 50us 左右的延時精度。今天讓我們來看下嘉友創信息科技的董文會是如何解決這個問題的,將延時精度提升到 10us。

x86 Linux 下實現 10us 誤差的高精度延時

在 Linux 下實現高精度延時,網上所能找到的大部分方法只能實現 50us 左右的延時精度。今天讓我們來看下嘉友創信息科技的董文會是如何解決這個問題的,將延時精度提升到 10us。

問題描述

最近在開發一個項目,需要用到高精度的延時機制,設計需求是 1000us 周期下,誤差不能超過 1%(10us)

由于項目硬件方案是用英特爾的 x86 處理器,熟悉 Linux 硬件的人都知道這個很難實現。當時評估方案時候有些草率,直接采用了 “PREEMPT_RT 補丁+內核 hrtimer+信號通知” 的方式來評估。當時驗證的結果也很滿意,于是興沖沖的告訴領導說方案可行,殊不知自己挖了一個巨大的坑……

實際項目開始的時候,發現這個方案根本行不通,有兩個原因:

  • 信號通知只能通知到進程,而目前移植的方案無法做到被通知的進程中無其他線程。這樣高頻的信號發過來,其他線程基本上都會被干掉。(補充說明:這里特指的是內核驅動通知到應用層,在用戶層中是有專門的函數可以通知不同線程的。并且這個問題經過研究,可以通過設置線程的 sigmask 來解決,但是依舊無法改變方案行不通的結論)
  • 這也是主要原因,項目中需要用的 Ethercat 的同步周期雖然可以在程序開始時固定,但是實際運行時的運行周期是需要動態調整的,調整范圍在 5us 以內。這樣一來,動態調整 hrtimer 的開銷就變得無法忽略了,換句話說,我們需要的是一個延時機制,而不是定時器。

所以這個方案被否定了。

解決思路

既然信號方式不行,那只能通過其他手段來分析。總結下來我大致進行了如下的嘗試:

1、sleep方案的確定

嘗試過 usleepnanosleepclock_nanosleepcond_timedwaitselect 等,最終確定用 clock_nanosleep,選它的原因并不是因為它支持 ns 級別的精度。因為經過測試發現,上述幾個調用在周期小于 10000us 的情況下,精度都差不多,誤差主要都來自于上下文切換的開銷。選它的主要原因是因為它支持 TIME_ABSTIME 選項,即支持絕對時間。這里舉個簡單的例子,解釋一下為什么要用絕對時間:

  1. while(1){
  2. do_work();
  3. sleep(1);
  4. do_post();
  5. }

假設上面這個循環,我們目的是讓 do_post 的執行以 1s 的周期執行一次,但是實際上,不可能是絕對的 1s,因為 sleep() 只能延時相對時間,而目前這個循環的實際周期是 do_work 的開銷 + sleep(1) 的時間。所以這種開銷放在我們需求的場景中,就變得無法忽視了。而用 clock_nanosleep 的好處就是一方面它可以選擇時鐘源,其次就是它支持絕對時間喚醒,這樣我在每次 do_work 之前都設置一下 clock_nanosleep 下一次喚醒時的絕對時間,那么 clock_nanosleep 實際執行的時間其實就會減去 do_work 的開銷,相當于是鬧鐘的概念。

2、改用實時線程

將重要任務的線程改成實時線程,調度策略改成 FIFO,優先級設到最高,減少被搶占的可能性。

3、設置線程的親和性

對應用下所有的線程進行規劃,根據負載情況將幾個負載比較重的任務線程分別綁定到不同的 CPU 核上,這樣減少切換 CPU 帶來的開銷。

4、減少不必要的sleep調用

由于很多任務都存在 sleep 調用,我用 strace 命令分析了整個系統中應用 sleep 調用的比例,高達 98%,這種高頻次休眠+喚醒帶來的開銷勢必是不可忽略的。所以我將 main 循環中的 sleep 改成了循環等待信號量的方式,因為 pthread 庫中信號量的等待使用了 futex,它使得喚醒線程的開銷會小很多。其他地方的 sleep 也盡可能的優化掉。這個效果其實比較明顯,能差不多減少 20us 的誤差

5、絕招

從現有應用中剝離出最小任務,減少所有外界任務的影響。

經過上述五點,1000us 的誤差從一開始的 ±100us,控制到了 ±40us。但是這還遠遠不夠……

黔驢技窮的我開始漫長的搜索研究中……

這期間也發現了一些奇怪的現象,比如下面這張圖。

 

x86 Linux 下實現 10us 誤差的高精度延時

圖片是用 Python 對抓包工具的數據進行分析生成的,參考性不用質疑。縱軸代表實際這個周期所耗費的時間。可以發現很有意思的現象:

  1. 每隔一定周期,會集中出現規模的誤差抖動
  2. 誤差不是正態分布,而是頻繁出現在 ±30us 左右的地方
  3. 每次產生較大的誤差時,下個周期一定會出現一次反向的誤差,而且幅度大致相同(這點從圖上看不出來,通過其他手段分析的)。

簡單描述一下就是假設這個周期的執行時間是 980us,那下個周期的執行時間一定會在 1020us 左右。

第 1 點和第 2 點可以經過上面的 4 條優化措施消除,第 3 點沒有找到非常有效的手段,我的理解可能內核對這種誤差是知曉的并且有意在彌補,如果有知道相關背后原理的大神歡迎分享一下。

針對這個第三點奇怪的現象我也嘗試做了手動的干預,比如設一個閾值,當實際程序執行的誤差大于這個閾值時,我就在設置下一個周期的喚醒時間時,手動減去這個誤差,但是運行效果卻大跌眼鏡,更差了……

柳暗花明

在嘗試了 200 多次參數調整,被這個問題卡了一個多禮拜之后,偶然發現了一篇戴爾的技術文檔《Controlling Processor C-State Usage in Linux》,受到這篇文章的啟發,終于解決了這個難題。

隨后經過一番針對性的查找終于摸清了來龍去脈:

原來英特爾的 CPU 為了節能,有很多功耗模式,簡稱 C-state。

 

< 如顯示不全,請左右滑動 >

模式

名字

作用

CPU

C0

操作狀態

CPU完全打開

所有CPU

C1

停止

通過軟件停止 CPU 內部主時鐘;總線接口單元和 APIC 仍然保持全速運行

486DX4及以上

C1E

增強型停止

通過軟件停止 CPU 內部主時鐘并降低 CPU 電壓;總線接口單元和 APIC 仍然保持全速運行

所有socket 775 CPU

C1E

停止所有CPU內部時鐘

Turion 64、65-nm Athlon X2和Phenom CPU

C2

停止授予

通過硬件停止 CPU 內部主時鐘;總線接口單元和 APIC 仍然保持全速運行

486DX4及以上

C2

停止時鐘

通過硬件停止CPU內部和外部時鐘

僅限486DX4、Pentium、Pentium MMX、K5、K6、K6-2、K6-III

C2E

擴展的停止授予

通過硬件停止 CPU 內部主時鐘并降低 CPU 電壓;總線接口單元和 APIC 仍然保持全速運行

Core 2 Duo和更高版本(僅限Intel)

C3

睡眠

停止所有CPU內部時鐘

Pentium II、Athlon以上支持,但Core 2 Duo E4000和E6000上不支持

C3

深度睡眠

停止所有CPU內部和外部時鐘

Pentium II以上支持,但Core 2 Duo E4000、E6000和Turion 64上不支持

C3

AltVID

停止所有CPU內部時鐘和降低CPU電壓

AMD Turion 64

C4

更深入的睡眠

降低CPU電壓

Pentium M以上支持,但Core 2 Duo E4000、E6000和Turion 64上不支持

C4E/C5

增強的更深入的睡眠

大幅降低CPU電壓并關閉內存高速緩存

Core Solo、Core Duo和45-nm移動版Core 2 Duo支持

C6

深度電源關閉

將 CPU 內部電壓降低至任何值,包括 0 V

僅45-nm移動版Core 2 Duo支持

 

 

圖表來自 DELL

當程序運行的時候,CPU 是在 C0 狀態,但是一旦操作系統進入休眠,CPU 就會用 Halt 指令切換到 C1 或者 C1E 模式,這個模式下操作系統如果進行喚醒,那么上下文切換的開銷就會變大!

這個選項按道理 BIOS 是可以關掉的,但是坑的地方就在于版本相對較新的 Linux 內核版本,默認是開啟這個狀態的,并且是無視 BIOS 設置的!這就很坑了!

 

x86 Linux 下實現 10us 誤差的高精度延時

針對性查找之后,發現網上也有網友測試,2.6 版本的內核不會默認開啟這個,但是 3.2 版本的內核就會開啟,而且對比測試發現,這兩個版本內核在相同硬件的情況下,上下文切換開銷可以相差 10 倍,前者是 4us,后者是 40-60us。

解決辦法

1、永久修改

可以修改 Linux 的引導參數,修改 /etc/default/grub 文件中的 GRUB_CMDLINE_LINUX_DEFAULT 選項,改成下面的內容:

  1. intel_idle.max_cstate=0 processor.max_cstate=0 idle=poll

然后使用 update-grub 命令使參數生效,重啟即可。

2、動態修改

可以通過向 /dev/cpu_dma_latency 這個文件中寫值,來調整 C1/C1E 模式下上下文切換的開銷。我選擇寫 0 直接關閉。當然你也可以選擇寫一個數值,這個數值就代表上下文切換的開銷,單位是 us。比如你寫 1,那么就是設置開銷為 1us。當然這個值是有范圍的,這個范圍在 /sys/devices/system/cpu/cpuX/cpuidle/stateY/latency 文件中可以查到,X 代表具體哪個核,Y 代表對應的 idle_state。

至此,這個性能問題就得到了完美的解決,目前穩定測試的性能如下圖所示:

 

x86 Linux 下實現 10us 誤差的高精度延時

實現了 x86 Linux 下高精度延時 1000us 精確延時,精度 10us。

原文鏈接:https://linux.cn/article-13686-1.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 国产1区视频 | 国产拍拍拍三级费视频在线观看 | 一级黄色影片在线观看 | 精品国产一级毛片 | h色在线观看 | 亚洲生活片 | 91高清观看 | 久久国产精品久久久久久 | 国产麻豆交换夫妇 | 精品一区二区三区免费毛片 | 黄在线观看在线播放720p | 欧美综合在线观看视频 | 国产三级国产精品国产普男人 | 天天撸日日夜夜 | 精品国产高清一区二区三区 | 婷婷中文字幕一区二区三区 | 免费人成在线播放 | 久草在线高清 | 福利国产视频 | 色妞色视频一区二区三区四区 | 一区二区三区小视频 | 欧美激情视频一区二区免费 | 免费观看一级黄色片 | 夜间福利视频 | 久久精品视频一区 | 激情免费视频 | 黄污免费网站 | 中文字幕一区久久 | 精品成人免费一区二区在线播放 | 国产精品久久久久久久久久10秀 | 欧美高清一级片 | 成人免费观看在线视频 | 久久综合一区二区 | 手机在线看片国产 | 欧美成人毛片 | 亚洲特黄 | 久久蜜臀一区二区三区av | 国产成人在线视频播放 | 一级外国毛片 | 欧美综合在线观看视频 | 一区小视频 |