在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

perf 在內核中的實現原理

Linux閱碼場 ? 來源:Linux閱碼場 ? 作者:Linux閱碼場 ? 2022-10-17 09:24 ? 次閱讀

我們在《一文看懂Linux性能分析|perf 原理》一文中介紹過,perf 是基于采樣來對程序進行分析的。采樣的步驟如下:

通過設置一個定時器,定時器的觸發時間可以由用戶設定。

定時器被觸發后,將會調用采集函數收集當前運行環境的數據(如當前正在執行的進程和函數等)。

將采集到的數據寫入到一個環形緩沖區(ring buffer)中。

應用層可以通過內存映射來讀取環形緩沖區中的采樣數據。

上述步驟如下圖所示:

bd863970-4db2-11ed-a3b6-dac502259ad0.png

接下來,我們將會介紹 perf 在 Linux 內核中的實現。

事件

perf 是基于事件進行采樣的,上面所說的定時器就是其中一種事件,被稱為:CPU時鐘事件。除了 CPU 時鐘事件外,perf 還支持多種事件,如:

上下文切換事件:當調度器切換進程時觸發。

缺頁異常事件:當進程訪問還沒有映射到物理內存的虛擬內存地址時觸發。

CPU遷移事件:當進程從一個 CPU 遷移到另一個 CPU 時觸發。

...

由于 perf 支持的事件眾多,所以本文只挑選CPU時鐘事件進行分析。

1. perf_event 結構體

Linux 內核使用perf_event結構體來描述一個事件(如 CPU 時鐘事件),其定義如下(由于 perf_event 結構體過于龐大,所以對其進行簡化):

structperf_event{
...
structlist_headevent_entry;
conststructpmu*pmu;
enumperf_event_active_statestate;
atomic64_tcount;//事件被觸發的次數
...
structperf_event_attrattr;//事件的屬性(由用戶提供)
structhw_perf_eventhw;
structperf_event_context*ctx;//事件所屬的上下文
...
};

我們現在只需關注其中的兩個成員變量:count和ctx。

count:表示事件被觸發的次數。

ctx:表示當前事件所屬的上下文。

count成員變量容易理解,所以就不作詳細介紹了。我們注意到 ctx 成員變量的類型為perf_event_context結構,那么這個結構代表什么?

2. perf_event_context 結構體

因為一個進程可以同時分析多種事件,所以就使用perf_event_context結構來記錄屬于進程的所有事件。我們來看看perf_event_context結構的定義,如下所示:

structperf_event_context{
...
structlist_headevent_list;//連接所有屬于當前上下文的事件
intnr_events;//屬于當前上下文的所有事件的總數
...
structtask_struct*task;//當前上下文屬于的進程
...
};

我們對perf_event_context結構進行了簡化,下面介紹一下各個成員的作用:

event_list:連接所有屬于當前上下文的事件。

nr_events:屬于當前上下文的所有事件的總數。

task:當前上下文所屬的進程。

perf_event_context結構通過event_list字段把所有屬于本上下文的事件連接起來,如下圖所示:

bdbe49fa-4db2-11ed-a3b6-dac502259ad0.png

另外,在進程描述結構體task_struct中,有個指向perf_event_context結構的指針。如下所示:

structtask_struct{
...
structperf_event_context*perf_event_ctxp;
...
};

這樣,內核就能通過進程描述結構體的perf_event_ctxp成員,來獲取屬于此進程的事件列表。

3. pmu 結構體

前面我們說過 perf 支持多種事件,而不同的事件應該有不同的啟用和禁用動作。為了讓不同的事件有不同的啟用和禁用動作,所以內核定義了pmu結構。其定義如下:

structpmu{
int(*enable)(structperf_event*event);
void(*disable)(structperf_event*event);
void(*read)(structperf_event*event);
...
};

下面介紹一下各個字段的作用:

enable:啟用事件。

disable:禁用事件。

read:事件被觸發時的回調。

perf_event結構的pmu成員是一個指向pmu結構的指針。如果當前事件是個 CPU 時鐘事件時,pmu成員將會指向perf_ops_cpu_clock變量。

我們來看看perf_ops_cpu_clock變量的定義:

staticconststructpmuperf_ops_cpu_clock={
.enable=cpu_clock_perf_event_enable,
.disable=cpu_clock_perf_event_disable,
.read=cpu_clock_perf_event_read,
};

也就是說:

當要啟用一個 CPU 時鐘事件時,內核將會調用cpu_clock_perf_event_enable()函數來啟用這個事件。

當要禁用一個 CPU 時鐘事件時,內核將會調用cpu_clock_perf_event_disable()函數來禁用這個事件。

當事件被觸發時,內核將會調用cpu_clock_perf_event_read()函數來進行特定的動作。

啟用事件

前面說過,當要啟用一個 CPU 時鐘事件時,內核會調用cpu_clock_perf_event_enable()函數來啟用它。我們來看看cpu_clock_perf_event_enable()函數的實現,代碼如下:

staticint
cpu_clock_perf_event_enable(structperf_event*event)
{
...
perf_swevent_start_hrtimer(event);

return0;
}

從上面代碼可以看出,cpu_clock_perf_event_enable()函數實際上調用了perf_swevent_start_hrtimer()函數來進行初始化工作。我們再來看看perf_swevent_start_hrtimer()函數的實現:

staticvoid
perf_swevent_start_hrtimer(structperf_event*event)
{
structhw_perf_event*hwc=&event->hw;

// 1. 初始化一個定時器,定時器的回調函數為:perf_swevent_hrtimer()
hrtimer_init(&hwc->hrtimer,CLOCK_MONOTONIC,HRTIMER_MODE_REL);
hwc->hrtimer.function=perf_swevent_hrtimer;

if(hwc->sample_period){
...

//2.啟動定時器
__hrtimer_start_range_ns(&hwc->hrtimer,ns_to_ktime(period),0,
HRTIMER_MODE_REL,0);
}
}

從上面的代碼可知,perf_swevent_start_hrtimer()函數主要完成兩件事情:

初始化一個定時器,定時器的回調函數為:perf_swevent_hrtimer()。

啟動定時器。

這個定時器結構保存在perf_event結構的hwc成員中,我們在以后的文章中將會介紹 Linux 高精度定時器的實現。

當定時器被觸發時,內核將會調用perf_swevent_hrtimer()函數來處理事件。我們再來分析一下perf_swevent_hrtimer()函數的實現:

staticenumhrtimer_restart
perf_swevent_hrtimer(structhrtimer*hrtimer)
{
enumhrtimer_restartret=HRTIMER_RESTART;
structperf_sample_datadata;
structpt_regs*regs;
structperf_event*event;
u64period;

//獲取當前定時器所屬的事件對象
event=container_of(hrtimer,structperf_event,hw.hrtimer);

//前面說過,如果是CPU時鐘事件,將會調用cpu_clock_perf_event_read()函數
event->pmu->read(event);

data.addr=0;
//獲取定時器被觸發時所有寄存器的值
regs=get_irq_regs();

...
if(regs){
if(!(event->attr.exclude_idle&¤t->pid==0)){
//最重要的地方:對數據進行采樣
if(perf_event_overflow(event,0,&data,regs))
ret=HRTIMER_NORESTART;
}
}
...
returnret;
}

perf_swevent_hrtimer()函數最重要的操作就是:調用perf_event_overflow()函數對數據進行采樣與收集。perf_event_overflow()函數在后面將會介紹,我們暫時跳過。

那什么時候會啟用事件呢?答案就是:進程被調度到 CPU 運行時。調用鏈如下:

schedule()
└→ context_switch()
   └→ finish_task_switch()
      └→ perf_event_task_sched_in()
         └→ __perf_event_sched_in()
            └→ group_sched_in()
               └→ event_sched_in()
                  └→ event->pmu->enable()
                     └→ cpu_clock_perf_event_enable()

內核通過調用schedule()函數來完成調度工作。從上面的調用鏈可知,當進程選中被調度到 CPU 運行時,最終會調用cpu_clock_perf_event_enable()函數來啟用這個 CPU 時鐘事件。

啟用事件的過程如下圖所示:

bdd390b2-4db2-11ed-a3b6-dac502259ad0.png

所以,當進程被選中并且被調度運行時,內核會啟用屬于此進程的 perf 事件。不難看出,當進程被調度出 CPU 時(停止運行),內核會禁用屬于此進程的 perf 事件。

數據采樣

最后,我們來看看 perf 是怎么進行數據采樣的。

通過上面的分析,我們知道 perf 最終會調用perf_event_overflow()函數來進行數據采樣。所以我們來看看perf_event_overflow()函數的實現,代碼如下:

int
perf_event_overflow(structperf_event*event,intnmi,
structperf_sample_data*data,
structpt_regs*regs)
{
return__perf_event_overflow(event,nmi,1,data,regs);
}

可以看出,perf_event_overflow()函數只是對__perf_event_overflow()函數的封裝。我們接著來分析__perf_event_overflow()函數的實現:

staticint
__perf_event_overflow(structperf_event*event,intnmi,intthrottle,
structperf_sample_data*data,structpt_regs*regs)
{
...
perf_event_output(event,nmi,data,regs);

returnret;
}

從上面代碼可知,__perf_event_overflow()會調用perf_event_output()函數來進行數據采樣。perf_event_output()函數的實現如下:

staticvoid
perf_event_output(structperf_event*event,intnmi,
structperf_sample_data*data,
structpt_regs*regs)
{
structperf_output_handlehandle;
structperf_event_headerheader;

//進行數據采樣,并且把采樣到的數據保存到data變量中
perf_prepare_sample(&header,data,event,regs);
...

//把采樣到的數據保存到環形緩沖區中
perf_output_sample(&handle,&header,data,event);
...
}

perf_event_output()函數會進行兩個操作:

調用perf_prepare_sample()函數進行數據采樣,并且把采樣到的數據保存到 data 變量中。

調用perf_output_sample()函數把采樣到的數據保存到環形緩沖區中。

我們來看看 perf 是怎么把采樣到的數據保存到環形緩沖區的:

void
perf_output_sample(structperf_output_handle*handle,
structperf_event_header*header,
structperf_sample_data*data,
structperf_event*event)
{
u64sample_type=data->type;
...

//1.保存當前IP寄存器地址(用于獲取正在執行的函數)
if(sample_type&PERF_SAMPLE_IP)
perf_output_put(handle,data->ip);

//2.保存當前進程ID
if(sample_type&PERF_SAMPLE_TID)
perf_output_put(handle,data->tid_entry);

//3.保存當前時間
if(sample_type&PERF_SAMPLE_TIME)
perf_output_put(handle,data->time);
...

//n.保存函數的調用鏈
if(sample_type&PERF_SAMPLE_CALLCHAIN){
if(data->callchain){
intsize=1;

if(data->callchain)
size+=data->callchain->nr;

size*=sizeof(u64);

perf_output_copy(handle,data->callchain,size);
}else{
u64nr=0;
perf_output_put(handle,nr);
}
}
...
}

perf_output_sample()通過調用perf_output_put()函數把用戶感興趣的數據保存到環形緩沖區中。

用戶感興趣的數據是在創建事件時指定的,例如,如果我們對函數的調用鏈感興趣,那么可以在創建事件時指定PERF_SAMPLE_CALLCHAIN標志位。

perf 事件可以通過pref_event_open()系統調用來創建,關于pref_event_open()系統調用的使用,讀者可以自行參考相關的資料

當 perf 把采樣的數據保存到環形緩沖區后,用戶就可以通過mmap()系統調用把環形緩沖區的數據映射到用戶態的虛擬內存地址來進行讀取。由于本文只關心數據采樣部分,所以 perf 的其他實現細節可以參考 perf 的源代碼。

數據采樣的流程如下圖所示:

be04b110-4db2-11ed-a3b6-dac502259ad0.png

總結

本文主要介紹了 perf 的 CPU 時鐘事件的實現原理,另外 perf 除了需要內核支持外,還需要用戶態應用程序支持,例如:把采樣到的原始數據生成可視化的數據或者使用圖形化表現出來。

當然,本文主要是介紹 perf 在內核中的實現,用戶態的程序可以參考 Linux 源碼tools/perf目錄下的源代碼。

當然,perf 是非常復雜的,本文也忽略了很多細節(如果把所有細節都闡明,那么篇幅將會非常長),所以讀者如果有什么疑問也可以留言討論。

審核編輯:彭靜
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 內核
    +關注

    關注

    3

    文章

    1382

    瀏覽量

    40430
  • 數據
    +關注

    關注

    8

    文章

    7145

    瀏覽量

    89591
  • 時鐘
    +關注

    關注

    11

    文章

    1747

    瀏覽量

    131804
  • 代碼
    +關注

    關注

    30

    文章

    4828

    瀏覽量

    69063

原文標題:一文看懂 Linux 性能分析|perf 源碼實現

文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    全球最高性能RISC-V處理器的Perf性能分析工具發布

    探測的性能監控。通過Perf分析工具,用戶可以使用可編程的硬件性能監控計數器監測預定義的硬件事件、預定義的硬件緩存事件和硬件原始事件的性能數據。Perf能針對硬件事件的每個任務、每個內核和每個工作負載的計數器進行采樣。 ? 賽昉
    的頭像 發表于 04-24 14:53 ?1964次閱讀
    全球最高性能RISC-V處理器的<b class='flag-5'>Perf</b>性能分析工具發布

    一文詳解Linux的perf_event

    Linux性能子系統在性能分析中非常有用。以下顯示了這篇文章perf子系統componenet 。
    發表于 10-11 09:04 ?2132次閱讀

    內核的電源管理

    之前介紹的電源管理機制基本都是在Linux實現的,可以看到很復雜,各種框架,明明一個操作非要轉來轉去,而且在內核里面實現,跟內核的各種框架
    的頭像 發表于 11-29 09:33 ?963次閱讀
    微<b class='flag-5'>內核</b><b class='flag-5'>中</b>的電源管理

    全志Tina中使用perf分析CPU使用率

    perf簡介Perf是是內置于Linux內核源碼樹的性能剖析(profiling)工具。不僅可以用于應用程序的性能統計分析,還可以用于內核
    發表于 05-20 14:25

    I.MX8MM開發板Linux如何在內核添加驅動呢

    迅為I.MX8MM開發板編譯驅動到內核,在平時的驅動開發,經常需要在內核配置某種功能,為了方便大家開發和學習,本小節講解如何在內核
    發表于 08-29 17:46

    你知道perf學習-linux自帶性能分析工具怎么用?

    Linux性能調優工具,32內核以上自帶的工具,軟件性能分析。在2.6.31及后續版本的linux內核里,安裝perf非常的容易。
    發表于 05-16 14:54 ?2633次閱讀

    米爾科技改內核調整GPIO在內核啟動階段方案

    米爾用戶在使用i.MX6UL/i.MX6ULL系列產品開發時,需要調整GPIO在內核啟動階段的狀態,這怎么操作呢?
    的頭像 發表于 11-26 16:31 ?2892次閱讀
    米爾科技改<b class='flag-5'>內核</b>調整GPIO<b class='flag-5'>在內核</b>啟動階段方案

    Coolbpf 在perf 事件的增強

    Perf 是內置于 Linux 內核源碼樹的性能剖析(profiling)工具。它基于事件采樣的原理,以性能事件為基礎,支持針對處理器相關性能指標與操作系統相關性能指標的性能剖析。
    的頭像 發表于 10-25 09:00 ?1206次閱讀

    解構內核 perf 框架的實現講解

    perf 框架,前端承接用戶態的各種事件(event)的屬性配置,后端將 event 嫁接到內核的調度、文件系統等框架,底層對接各種 PMU 硬件,所以其必然要建立一個復雜、嚴謹的模型(抽象)系統。
    發表于 01-16 09:49 ?1425次閱讀

    萬字長文解讀Linux內核追蹤機制

    Linux 存在眾多 tracing tools,比如 ftrace、perf,他們可用于內核的調試、提高內核的可觀測性。
    的頭像 發表于 06-11 11:05 ?864次閱讀
    萬字長文解讀Linux<b class='flag-5'>內核</b>追蹤機制

    Linux內核的宏/container_of分析

    今天在看平臺設備實現的時候,看到to_xxx開頭的“函數”。包括在內核也有很多此類的“函數”,其實他們都是container_of的宏。因為內核是鏈表和結構體的世界,因此
    發表于 06-23 14:26 ?449次閱讀
    Linux<b class='flag-5'>內核</b><b class='flag-5'>中</b>的宏/container_of分析

    Linux perf性能、實際應用與案例

    Linux perf(性能分析工具)是一個功能強大且靈活的性能剩余工具,它可以在Linux系統上檢測和調試各種性能問題。Linux內核集成了perf工具,可用于探測內核性能事件、硬件性
    發表于 07-03 10:22 ?719次閱讀

    如何使用perf性能分析工具

    在功能上,perf很強大,可以對眾多的軟硬件事件采樣,還能采集出跟蹤點(trace points)的信息(比如系統調用、TCP/IP事件和文件系統操作。perf的代碼和Linux內核代碼
    的頭像 發表于 11-08 15:36 ?1733次閱讀
    如何使用<b class='flag-5'>perf</b>性能分析工具

    Linux perf 簡要介紹

    的性能剩余工具,它可以在Linux系統上檢測和調試各種性能問題。Linux內核集成了perf工具,可用于探測內核性能事件、硬件性能計數器以及用戶級應用程序性能事件。 perf工具可以用
    的頭像 發表于 11-09 17:06 ?918次閱讀

    如何在內核啟動secondary cpu

    啟動secondary cpu 內核在啟動secondary cpu之前當然需要為其準備好執行環境,因為內核cpu最終都將由調度器管理,故此時調度子系統應該要初始化完成。 同時cpu啟動完成轉交
    的頭像 發表于 12-05 15:46 ?672次閱讀
    如何<b class='flag-5'>在內核</b><b class='flag-5'>中</b>啟動secondary cpu
    主站蜘蛛池模板: 午夜影院0606免费 | 亚洲一区二区三 | 四虎国产精品永久在线 | 在线www| 大片毛片女女女女女女女 | 国产日韩欧美综合色视频在线 | 毛片在线看免费版 | 就要爱综合 | 色视频在线网站 | 国产小视频在线 | 欧美福利视频网站 | 清纯唯美亚洲综合一区 | 欧美网站色 | 国产亚洲精品久久久久久牛牛 | 一区二区三区四区视频 | 日本成本人三级在线观看2018 | 一级毛片成人免费看a | 成人五级毛片免费播放 | 爱我免费视频观看在线www | 国产嫩草影院精品免费网址 | 狂野欧美性色xo影院 | 天天操天天操天天操 | 夜色资源站www国产在线观看 | 美女和帅哥在床上玩的不可描述 | 亚洲视屏一区 | 婷婷色综合久久 | www.狠狠操.com| 色偷偷亚洲天堂 | 色噜噜狠狠狠狠色综合久一 | 欧美三级一区二区 | 久久视频免费看 | 亚洲成在人线影视天堂网 | 四虎网站最新网址 | 97影院理论片手机在线观看 | 久久在草| 四虎影视在线观看 | 四虎影视在线观看 | 天堂网视频 | 日日噜噜夜夜狠狠久久aⅴ 日日噜噜夜夜狠狠久久丁香 | 毛色毛片免费观看 | 99久久综合精品免费 |