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

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

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

[[417793]]

在 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。但是這還遠遠不夠……

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

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

 

圖片是用 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 設置的!這就很坑了!

 

針對性查找之后,發現網上也有網友測試,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 下高精度延時 1000us 精確延時,精度 10us。 

 

責任編輯:龐桂玉 來源: Linux中國
相關推薦

2011-04-25 14:51:59

Linux任務切換TSS

2011-12-01 11:09:48

AMDx86服務器英特爾

2009-06-18 09:11:03

微軟Windows 7下載

2011-04-21 10:49:28

Linux時間定時器

2012-09-19 09:51:45

Windows Serx86服務器選型

2021-06-07 15:20:22

Linux X861MB內存BIOS

2014-12-24 09:41:05

x86C#

2013-01-31 10:04:20

x86服務器虛擬化

2011-02-20 22:23:43

X86虛擬化XenServer

2011-12-19 10:55:58

云計算中國電信

2011-11-10 09:26:48

Solaris 11

2009-08-28 14:38:33

2010-02-04 16:27:24

Android X86

2010-05-07 17:47:12

Unix Solari

2019-03-22 08:25:20

x86PythonARM

2020-09-23 12:42:08

Linux

2020-10-13 10:51:10

Linux內核

2021-07-07 11:35:17

Linux內存段尋址

2011-12-14 13:02:05

Power虛擬機X86平臺服務器

2010-04-29 17:50:15

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 精品国产一区二区国模嫣然 | 久久视频精品 | 午夜精品一区二区三区在线播放 | 亚洲精品乱码久久久久久9色 | 久久亚洲综合 | 黄色大片网站 | 性色网站| 日韩av一区二区在线观看 | 欧美成人a | 国产精品久久久久久久久久久久久 | 91在线看网站 | 99精品欧美一区二区蜜桃免费 | 欧美一区二区三区国产精品 | 欧美成人精品一区二区三区 | 久久久蜜臀国产一区二区 | 国产中文| 精品日本中文字幕 | 丝袜美腿一区二区三区 | 少妇性l交大片免费一 | 成人一级片在线观看 | 黄色片在线看 | 国产精品高潮呻吟久久aⅴ码 | 久久久久国产一区二区三区不卡 | 99九九视频 | 在线视频亚洲 | 91一区二区在线观看 | 亚洲永久免费 | 黄色毛片在线播放 | 91在线成人 | 久久久国产精品一区 | 国产激情视频网站 | 九色在线视频 | 国产成人精品久久久 | 国产日韩一区二区 | 成人在线一区二区 | av网站免费在线观看 | 日韩看片| 成人av片在线观看 | 久久精品91 | 一级aaaaaa毛片免费同男同女 | 亚洲一区二区不卡在线观看 |