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

x86 Linux 下實(shí)現(xiàn) 10us 誤差的高精度延時(shí)

系統(tǒng) Linux
在 Linux 下實(shí)現(xiàn)高精度延時(shí),網(wǎng)上所能找到的大部分方法只能實(shí)現(xiàn) 50us 左右的延時(shí)精度。今天讓我們來看下嘉友創(chuàng)信息科技的董文會(huì)是如何解決這個(gè)問題的,將延時(shí)精度提升到 10us。

[[417793]]

在 Linux 下實(shí)現(xiàn)高精度延時(shí),網(wǎng)上所能找到的大部分方法只能實(shí)現(xiàn) 50us 左右的延時(shí)精度。今天讓我們來看下嘉友創(chuàng)信息科技的董文會(huì)是如何解決這個(gè)問題的,將延時(shí)精度提升到 10us。

問題描述

最近在開發(fā)一個(gè)項(xiàng)目,需要用到高精度的延時(shí)機(jī)制,設(shè)計(jì)需求是 1000us 周期下,誤差不能超過 1%(10us)

由于項(xiàng)目硬件方案是用英特爾的 x86 處理器,熟悉 Linux 硬件的人都知道這個(gè)很難實(shí)現(xiàn)。當(dāng)時(shí)評估方案時(shí)候有些草率,直接采用了 “PREEMPT_RT 補(bǔ)丁+內(nèi)核 hrtimer+信號(hào)通知” 的方式來評估。當(dāng)時(shí)驗(yàn)證的結(jié)果也很滿意,于是興沖沖的告訴領(lǐng)導(dǎo)說方案可行,殊不知自己挖了一個(gè)巨大的坑……

實(shí)際項(xiàng)目開始的時(shí)候,發(fā)現(xiàn)這個(gè)方案根本行不通,有兩個(gè)原因:

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

所以這個(gè)方案被否定了。

解決思路

既然信號(hào)方式不行,那只能通過其他手段來分析。總結(jié)下來我大致進(jìn)行了如下的嘗試:

1、sleep方案的確定

嘗試過 usleepnanosleepclock_nanosleepcond_timedwaitselect 等,最終確定用 clock_nanosleep,選它的原因并不是因?yàn)樗С?ns 級(jí)別的精度。因?yàn)榻?jīng)過測試發(fā)現(xiàn),上述幾個(gè)調(diào)用在周期小于 10000us 的情況下,精度都差不多,誤差主要都來自于上下文切換的開銷。選它的主要原因是因?yàn)樗С?TIME_ABSTIME 選項(xiàng),即支持絕對時(shí)間。這里舉個(gè)簡單的例子,解釋一下為什么要用絕對時(shí)間:

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

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

2、改用實(shí)時(shí)線程

將重要任務(wù)的線程改成實(shí)時(shí)線程,調(diào)度策略改成 FIFO,優(yōu)先級(jí)設(shè)到最高,減少被搶占的可能性。

3、設(shè)置線程的親和性

對應(yīng)用下所有的線程進(jìn)行規(guī)劃,根據(jù)負(fù)載情況將幾個(gè)負(fù)載比較重的任務(wù)線程分別綁定到不同的 CPU 核上,這樣減少切換 CPU 帶來的開銷。

4、減少不必要的sleep調(diào)用

由于很多任務(wù)都存在 sleep 調(diào)用,我用 strace 命令分析了整個(gè)系統(tǒng)中應(yīng)用 sleep 調(diào)用的比例,高達(dá) 98%,這種高頻次休眠+喚醒帶來的開銷勢必是不可忽略的。所以我將 main 循環(huán)中的 sleep 改成了循環(huán)等待信號(hào)量的方式,因?yàn)?pthread 庫中信號(hào)量的等待使用了 futex,它使得喚醒線程的開銷會(huì)小很多。其他地方的 sleep 也盡可能的優(yōu)化掉。這個(gè)效果其實(shí)比較明顯,能差不多減少 20us 的誤差

5、絕招

從現(xiàn)有應(yīng)用中剝離出最小任務(wù),減少所有外界任務(wù)的影響。

經(jīng)過上述五點(diǎn),1000us 的誤差從一開始的 ±100us,控制到了 ±40us。但是這還遠(yuǎn)遠(yuǎn)不夠……

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

這期間也發(fā)現(xiàn)了一些奇怪的現(xiàn)象,比如下面這張圖。

 

圖片是用 Python 對抓包工具的數(shù)據(jù)進(jìn)行分析生成的,參考性不用質(zhì)疑。縱軸代表實(shí)際這個(gè)周期所耗費(fèi)的時(shí)間。可以發(fā)現(xiàn)很有意思的現(xiàn)象:

  1. 每隔一定周期,會(huì)集中出現(xiàn)規(guī)模的誤差抖動(dòng)
  2. 誤差不是正態(tài)分布,而是頻繁出現(xiàn)在 ±30us 左右的地方
  3. 每次產(chǎn)生較大的誤差時(shí),下個(gè)周期一定會(huì)出現(xiàn)一次反向的誤差,而且幅度大致相同(這點(diǎn)從圖上看不出來,通過其他手段分析的)。

簡單描述一下就是假設(shè)這個(gè)周期的執(zhí)行時(shí)間是 980us,那下個(gè)周期的執(zhí)行時(shí)間一定會(huì)在 1020us 左右。

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

針對這個(gè)第三點(diǎn)奇怪的現(xiàn)象我也嘗試做了手動(dòng)的干預(yù),比如設(shè)一個(gè)閾值,當(dāng)實(shí)際程序執(zhí)行的誤差大于這個(gè)閾值時(shí),我就在設(shè)置下一個(gè)周期的喚醒時(shí)間時(shí),手動(dòng)減去這個(gè)誤差,但是運(yùn)行效果卻大跌眼鏡,更差了……

柳暗花明

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

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

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

 

 

 

< 如顯示不全,請左右滑動(dòng) >

模式

名字

作用

CPU

C0

操作狀態(tài)

CPU完全打開

所有CPU

C1

停止

通過軟件停止 CPU 內(nèi)部主時(shí)鐘;總線接口單元和 APIC 仍然保持全速運(yùn)行

486DX4及以上

C1E

增強(qiáng)型停止

通過軟件停止 CPU 內(nèi)部主時(shí)鐘并降低 CPU 電壓;總線接口單元和 APIC 仍然保持全速運(yùn)行

所有socket 775 CPU

C1E

停止所有CPU內(nèi)部時(shí)鐘

Turion 64、65-nm Athlon X2和Phenom CPU

C2

停止授予

通過硬件停止 CPU 內(nèi)部主時(shí)鐘;總線接口單元和 APIC 仍然保持全速運(yùn)行

486DX4及以上

C2

停止時(shí)鐘

通過硬件停止CPU內(nèi)部和外部時(shí)鐘

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

C2E

擴(kuò)展的停止授予

通過硬件停止 CPU 內(nèi)部主時(shí)鐘并降低 CPU 電壓;總線接口單元和 APIC 仍然保持全速運(yùn)行

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

C3

睡眠

停止所有CPU內(nèi)部時(shí)鐘

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

C3

深度睡眠

停止所有CPU內(nèi)部和外部時(shí)鐘

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

C3

AltVID

停止所有CPU內(nèi)部時(shí)鐘和降低CPU電壓

AMD Turion 64

C4

更深入的睡眠

降低CPU電壓

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

C4E/C5

增強(qiáng)的更深入的睡眠

大幅降低CPU電壓并關(guān)閉內(nèi)存高速緩存

Core Solo、Core Duo和45-nm移動(dòng)版Core 2 Duo支持

C6

深度電源關(guān)閉

將 CPU 內(nèi)部電壓降低至任何值,包括 0 V

僅45-nm移動(dòng)版Core 2 Duo支持

 

 

 

圖表來自 DELL

當(dāng)程序運(yùn)行的時(shí)候,CPU 是在 C0 狀態(tài),但是一旦操作系統(tǒng)進(jìn)入休眠,CPU 就會(huì)用 Halt 指令切換到 C1 或者 C1E 模式,這個(gè)模式下操作系統(tǒng)如果進(jìn)行喚醒,那么上下文切換的開銷就會(huì)變大!

這個(gè)選項(xiàng)按道理 BIOS 是可以關(guān)掉的,但是坑的地方就在于版本相對較新的 Linux 內(nèi)核版本,默認(rèn)是開啟這個(gè)狀態(tài)的,并且是無視 BIOS 設(shè)置的!這就很坑了!

 

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

解決辦法

1、永久修改

可以修改 Linux 的引導(dǎo)參數(shù),修改 /etc/default/grub 文件中的 GRUB_CMDLINE_LINUX_DEFAULT 選項(xiàng),改成下面的內(nèi)容:

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

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

2、動(dòng)態(tài)修改

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

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

 

實(shí)現(xiàn)了 x86 Linux 下高精度延時(shí) 1000us 精確延時(shí),精度 10us。 

 

責(zé)任編輯:龐桂玉 來源: Linux中國
相關(guān)推薦

2011-04-25 14:51:59

Linux任務(wù)切換TSS

2009-06-18 09:11:03

微軟Windows 7下載

2011-12-01 11:09:48

AMDx86服務(wù)器英特爾

2011-04-21 10:49:28

Linux時(shí)間定時(shí)器

2012-09-19 09:51:45

Windows Serx86服務(wù)器選型

2021-06-07 15:20:22

Linux X861MB內(nèi)存BIOS

2014-12-24 09:41:05

x86C#

2013-01-31 10:04:20

x86服務(wù)器虛擬化

2011-11-10 09:26:48

Solaris 11

2009-08-28 14:38:33

2011-12-19 10:55:58

云計(jì)算中國電信

2011-02-20 22:23:43

X86虛擬化XenServer

2020-10-13 10:51:10

Linux內(nèi)核

2010-05-07 17:47:12

Unix Solari

2019-03-22 08:25:20

x86PythonARM

2010-02-04 16:27:24

Android X86

2020-09-23 12:42:08

Linux

2021-07-07 11:35:17

Linux內(nèi)存段尋址

2010-03-20 11:03:13

VMControl虛擬化管理

2009-08-05 09:02:26

Microsoft FIIS 7.0
點(diǎn)贊
收藏

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

主站蜘蛛池模板: 色网站入口 | 午夜影院在线免费观看视频 | 在线永久看片免费的视频 | 国产成人精品久久久 | 成人片免费看 | 国产在线精品区 | 一区二区视频 | 久久精品国产亚洲 | 狠狠操天天操 | 欧美精品久久久久 | 日本一区二区不卡 | 久草在线视频中文 | 欧美一区二区三区久久精品 | 欧美日韩一区在线 | 亚洲欧美精 | 请别相信他免费喜剧电影在线观看 | 91免费入口| 久久综合国产 | 国产黄色精品 | 国产精品久久久久aaaa樱花 | 亚洲色图第一页 | 在线中文一区 | 色综合天天天天做夜夜夜夜做 | 成人毛片视频免费 | 成人h电影在线观看 | 国产精品视频一区二区三区不卡 | 91精品国产色综合久久 | 香蕉一区 | 啪啪av | 日日夜夜视频 | 久久国产精品首页 | 99免费 | 欧美日韩精品 | 91精品国产91久久久久久最新 | 日韩精品一区二区三区在线 | www.99热这里只有精品 | h在线观看 | 男女久久久 | 久久久精品视频免费看 | 亚洲国产免费 | 午夜成人免费视频 |