了解你自己的程序

前言

因為在做nodejs程序的性能分析的時候,了解到了Perf和FlameGraph這兩個神奇的工具,接著就知道了Brendan D. Gregg這個大神,跪著拜讀了他的博客和他寫的System Performance。從前寫程序和調(diào)優(yōu)只知道從設(shè)計的思路去思考,讀完大神的文章,感覺真的給自己打開了一個全新的世界。

把自己的程序看做一個黑盒,它運行的時候到底占多少內(nèi)存,多少CPU?這個問題看起來不難,那么它多少時間在等待I/O,多少時間在計算,如果CPU是多核,它是否很好的平衡了負載?它訪問文件系統(tǒng)頻率多少,每次的相應(yīng)時間多少?它運行中的熱點是哪里,瓶頸是哪里?

再復(fù)雜點,它在運行時內(nèi)存是否足夠,page cache和CPU cache命中率如何?有沒有導(dǎo)致系統(tǒng)發(fā)生swap等非常耗時的操作?現(xiàn)在如果程序運行不穩(wěn)定,如何去觀察定位和調(diào)優(yōu)?

目前為止我還在每天跪著閱讀大神的文章中,這篇筆記會按一定順序記錄我的很多理解。

Perf

Perf是一個神奇的工具,主要用于事件監(jiān)測。
每當(dāng)linux內(nèi)核調(diào)用某一個函數(shù)時,可以視作一個事件,Perf可以記錄這些事件發(fā)生的時間和內(nèi)核調(diào)用棧。

基本用法

perf command [options] [execute]

舉個例子:

perf stat -e sched:sched_switch -a sleep 5

統(tǒng)計5s之內(nèi),操作系統(tǒng)一共調(diào)用了多少個sched_switch。對linux熟悉的朋友應(yīng)該知道這個表示進程切換。下面我的虛擬機里返回的結(jié)果

Performance counter stats for 'system wide':

             1,170      sched:sched_switch                                          

       5.001593514 seconds time elapsed

也就是5秒鐘之內(nèi)發(fā)生了1170次進程切換。

這里解釋下這個命令,perf stat是統(tǒng)計事件次數(shù),-e sched:sched_switch表示統(tǒng)計sched:sched_switch事件,然后本來應(yīng)該只統(tǒng)計sleep 5這個process內(nèi)部的事件的,加上-a表示統(tǒng)計整個系統(tǒng)內(nèi)的事件,由于sleep 5要在5s后結(jié)束,所以這個命令的實際功能就是統(tǒng)計了系統(tǒng)5s內(nèi)發(fā)生的sched:sched_switch事件個數(shù)。

再舉個栗子:

perf record -e block:block_rq_complete --filter 'nr_sector > 200'

block_rq_complete是塊設(shè)備請求完成的事件,該條命令會統(tǒng)計所有涉及到200扇區(qū)以上的設(shè)備I/O事件。
命令:

perf record -e page-faults -ag

則會統(tǒng)計所有缺頁中斷。

事件列表

調(diào)用perf list (注意root權(quán)限可以看到更多)可以查看當(dāng)前支持的事件列表。

List of pre-defined events (to be used in -e):

  alignment-faults                                   [Software event]
  bpf-output                                         [Software event]
  context-switches OR cs                             [Software event]
  cpu-clock                                          [Software event]
  cpu-migrations OR migrations                       [Software event]
  dummy                                              [Software event]
  emulation-faults                                   [Software event]
  major-faults                                       [Software event]
  minor-faults                                       [Software event]
  page-faults OR faults                              [Software event]
  task-clock                                         [Software event]

  L1-dcache-load-misses                              [Hardware cache event]
  L1-dcache-loads                                    [Hardware cache event]
  L1-dcache-stores                                   [Hardware cache event]
  L1-icache-load-misses                              [Hardware cache event]
  branch-load-misses                                 [Hardware cache event]
  branch-loads                                       [Hardware cache event]
  dTLB-load-misses                                   [Hardware cache event]
  dTLB-loads                                         [Hardware cache event]
  dTLB-store-misses                                  [Hardware cache event]
  dTLB-stores                                        [Hardware cache event]
  iTLB-load-misses                                   [Hardware cache event]
  iTLB-loads                                         [Hardware cache event]

  cycles-ct OR cpu/cycles-ct/                        [Kernel PMU event]
  cycles-t OR cpu/cycles-t/                          [Kernel PMU event]
  el-abort OR cpu/el-abort/                          [Kernel PMU event]
  el-capacity OR cpu/el-capacity/                    [Kernel PMU event]

Perf可以監(jiān)控的事件類型很多,甚至連L1 cache miss這種都可以。

Perf 命令

  • perf stat
    stat命令用于簡單統(tǒng)計次數(shù)

    • 統(tǒng)計PID進程的事件
      perf stat -p PID
    • 統(tǒng)計整個系統(tǒng)的事件
      perf stat -a sleep [seconds]
    • 統(tǒng)計command內(nèi)的事件
      perf stat [command]
  • perf record & report/script
    stat命令只會統(tǒng)計事件發(fā)生次數(shù),如果想查看更詳細的信息,比如事件發(fā)生時的堆棧,就需要用到record命令了。
    perf record把統(tǒng)計結(jié)果放到當(dāng)前目錄內(nèi)perf.data文件,用perf report/script命令可以解析展示統(tǒng)計結(jié)果。

    • 以固定頻率對程序進行抽樣,并且記錄堆棧
      perf record -F [freq] [command] [-p PID] -g -- sleep [sec]
      //-g 表示統(tǒng)計stack, sec表示統(tǒng)計時長
      例如
     perf record -F 99 -a -g -- sleep 2
     //統(tǒng)計兩秒內(nèi)的默認事件 (cpu clock事件)
     perf script
    
     swapper     0 [000] 19822.660852:   10101010 cpu-clock: 
                 7fff810665d6 native_safe_halt ([kernel.kallsyms])
                 7fff8103ad7e default_idle ([kernel.kallsyms])
                 7fff8103b58f arch_cpu_idle ([kernel.kallsyms])
                 7fff810c5faa default_idle_call ([kernel.kallsyms])
                 7fff810c6311 cpu_startup_entry ([kernel.kallsyms])
                 7fff818239dc rest_init ([kernel.kallsyms])
                 7fff81f5d011 start_kernel ([kernel.kallsyms])
                 7fff81f5c339 x86_64_start_reservations ([kernel.kallsyms])
                 7fff81f5c485 x86_64_start_kernel ([kernel.kallsyms])
    
     swapper     0 [001] 19822.660853:   10101010 cpu-clock: 
                 7fff810665d6 native_safe_halt ([kernel.kallsyms])
                 7fff8103ad7e default_idle ([kernel.kallsyms])
                 7fff8103b58f arch_cpu_idle ([kernel.kallsyms])
                 7fff810c5faa default_idle_call ([kernel.kallsyms])
                 7fff810c6311 cpu_startup_entry ([kernel.kallsyms])
                 7fff810536e4 start_secondary ([kernel.kallsyms])
    
     swapper     0 [001] 19822.671003:   10101010 cpu-clock: 
                 7fff810665d6 native_safe_halt ([kernel.kallsyms])
                 7fff8103ad7e default_idle ([kernel.kallsyms])
                 7fff8103b58f arch_cpu_idle ([kernel.kallsyms])
                 7fff810c5faa default_idle_call ([kernel.kallsyms])
                 7fff810c6311 cpu_startup_entry ([kernel.kallsyms])
                 7fff810536e4 start_secondary ([kernel.kallsyms])
    
     swapper     0 [000] 19822.671003:   10101010 cpu-clock: 
                 7fff810665d6 native_safe_halt ([kernel.kallsyms])
                 7fff8103ad7e default_idle ([kernel.kallsyms])
                 7fff8103b58f arch_cpu_idle ([kernel.kallsyms])
                 7fff810c5faa default_idle_call ([kernel.kallsyms])
    

由于我統(tǒng)計期間沒有做任何事,所以每次時鐘中斷發(fā)生時,CPU都停留在native_safe_halt函數(shù)這里。

nodejs對perf的支持

由于nodejs等虛機語言通常采用了JIT技術(shù)在運行時改寫函數(shù),其堆棧符號表會在運行時變動。
為了能讓perf命令監(jiān)測運行過程,nodejs提供了--perf_basic_prof參數(shù),當(dāng)加上此參數(shù)運行時,node會在/tmp目錄下生成perf-PID.map文件,里面給出了地址到函數(shù)名稱的映射。
perf record通過-p PID抽樣程序時,會自動去/tmp目錄下找對應(yīng)的map文件并加載。

FlameGraph

查看perf script的報告仍然不夠直觀,大神給出了一個工具,可以將perf統(tǒng)計結(jié)果轉(zhuǎn)化成SVG圖,并且會將相同的堆棧合并,這樣可以很直觀的看出來程序在那些調(diào)用上花費了大量時間。

FlameGraph

Markdown好像不支持SVG,只能截個圖表示效果,實際的SVG是可以交互的了解具體細節(jié)。
這樣對程序的運行過程會有非常直觀的了解。

用FlameGraph調(diào)查nodejs程序內(nèi)存使用

接下來是一個用FlameGraph來檢查nodejs程序的例子。

  1. 啟動nodejs程序,帶上 --perf-basic-prof 選項
    這里我啟動了一個簡單的deeplearning程序。
    node --perf-basic-prof main.js &
    進程ID為25586,于是node會在/tmp/目錄下生成/tmp/perf-25586.map文件,其實文件內(nèi)容就是地址和函數(shù)名的映射表。
  2. 啟動perf監(jiān)控程序
    這里我監(jiān)控所有的缺頁中斷程序
    perf record -e kmem:mm_page_alloc -g -p 25586
    監(jiān)控了一段時間后就Ctrl+c斷開監(jiān)控,此時目錄下生成了perf.data文件。
  3. 輸出perf記錄
    調(diào)用perf script把結(jié)果存在一個臨時文件中
    perf script > node.tmp
  4. 調(diào)用Flamegraph工具將其生成SVG熱點圖
    stackcollapse-perf.pl node.tmp | flamegraph.pl > flamegrapsh.svg
  5. 用瀏覽器打開svg就可以看到熱點圖了


    flame_graph

可以查看具體細節(jié):


detail

可以看到Rect.junc函數(shù)導(dǎo)致了大量的ProcessOldToNewSlot,緊接著導(dǎo)致了大量的缺頁中斷,接下來就可以調(diào)研下ProcessOldToNewSlot是什么,以及如何可以避免這種情況了。

ftrace

ftrace是linux提供的一個tracing工具,同perf一樣可以監(jiān)控很多系統(tǒng)的事件。
ftrace
Dtrace & Systemtap
==========
比起Perf,Dtrace和Systemtap更為強大,它們除了可以檢測事件之外,還可以在事件發(fā)生時運行指定的命令去調(diào)查更詳細的信息,比如函數(shù)參數(shù)等。
Dtrace貌似在ubuntu上的支持并不好,接下來我會花些時間去學(xué)習(xí)Systemtap。

Linux系統(tǒng)提供的監(jiān)測工具

vmstat

vmstat提供了關(guān)于系統(tǒng)虛擬內(nèi)存的使用統(tǒng)計信息。
用法是

vmstat [options] [delay [count]]

delay表示刷新頻率,count表示統(tǒng)計次數(shù)。
重點是options

options

  • a
    統(tǒng)計active和inactive memory,正被程序引用的page為active。
  • m, slabs
    統(tǒng)計slab系統(tǒng)信息
  • s
    展示一些計數(shù)信息,
  • d
    統(tǒng)計硬盤信息

vmstat 統(tǒng)計的信息包括

  • 進程

    • r
      狀態(tài)為RUNNABLE的進程,運行中或者等待CPU。
    • b
      狀態(tài)為UNINTERRUPTIBLE SLEEP的進程,一般是在等待I/O操作。
  • 內(nèi)存

    • swpd
      使用的swap內(nèi)存
    • free
      空閑內(nèi)存數(shù)量
    • buff
      被用作buffers的內(nèi)存數(shù)量
    • cache
      被用作cache的內(nèi)存數(shù)量
    • inact
    • active
      inactive/active內(nèi)存數(shù)量
  • Swap

    • si
      從disk swap進來的內(nèi)存數(shù)量(每秒)
    • so
      換出到disk的內(nèi)存數(shù)量(每秒)
  • I/O

    • bi
      每秒收到的blocks數(shù)量
    • bo
      每秒發(fā)送出去的blocks數(shù)量
  • system

    • in
      每秒收到的interrupt數(shù)量,包括時鐘中斷
    • cs
      每秒的context switch數(shù)量
  • cpu
    這里統(tǒng)計的是百分比,按CPU核數(shù)平均之后的結(jié)果

    • us
      非內(nèi)核態(tài)時間占比
    • sy
      內(nèi)核態(tài)時間占比
    • id
      空閑時間占比,注意這里包括了wa時間
    • wa
      等待I/O時間占比
    • st
      Time stolen from virtual machine
  • Disk Mode

    • Reads
      total: 完成的Read總數(shù)
      merged: 被merge的Read次數(shù)
      sectors: 完成的Read sector數(shù)量
      ms: 花費在read上的時間總數(shù)
    • Writes
      total: 完成的Write總數(shù)
      merged: 被合并的Write次數(shù)
      sectors: 完成的write扇區(qū)總數(shù)
      ms: 花費在write上的時間總數(shù)
    • IO
      cur: 正在進行的I/O
      s: 花費在I/O上的時間

使用vmstat的時候,有幾個很重要的概念需要理清楚:

  • active/inactive memory
    active memory指的是被某個process使用在的memory。
    inactive memory指的是被曾經(jīng)運行的process使用的memory
  • buffer/cache
    有關(guān)buffer和cache的區(qū)別,我到現(xiàn)在還沒有完全弄清楚,就目前的理解而言,buffer是供給I/O來保存?zhèn)鬏敂?shù)據(jù)塊的page,而cache是用來做文件內(nèi)容緩存的page。

iostat

iostat統(tǒng)計了系統(tǒng)運行的一些io數(shù)據(jù)。

iostat [options] [interval [count]]

重點仍然是options

options

  • c
    展示CPU統(tǒng)計報告
  • d
    展示device統(tǒng)計報告
  • x
    展示擴展統(tǒng)計信息(很有用)
  • z
    忽略不活躍device

iostat展示的信息包括

  • CPU報告
    • %user
      平均后的用戶態(tài)時間占比
    • %system
      平均后的內(nèi)核態(tài)時間占比
    • %iowait
      io等待時間的占比
    • %idle
      空閑時間占比
  • Device報告
    • Device:
      device name
    • tps:
      transfers per second,每秒傳輸請求次數(shù)
    • Blk_read/s (kB_read/s, MB_read/s):
      每秒讀取Block數(shù)量或者數(shù)據(jù)量
    • Blk_wrtn/s (kB_wrtn/s, MB_wrtn/s):
      每秒寫入的Block數(shù)量或者數(shù)據(jù)量
    • rrqm/s:
      每秒merged read request數(shù)量
    • wrqm/s:
      每秒merged write request數(shù)量
    • r/s:
      merge之后的每秒read request數(shù)量
    • w/s:
      merge之后的每秒write request數(shù)量
    • rsec/s (rkB/s, rMB/s):
      每秒讀入數(shù)據(jù)量
    • wsec/s (wkB/s, wMB/s):
      每秒寫入數(shù)據(jù)量
    • avgrq-sz:
      平均每個request的size,一般以sector為單位,每個sector是512字節(jié)
      所以一般來說:(avgrq-sz * 0.5 * (r/s + w/s) = rkB/s + wkB/s)
    • avgqu-sz:
      device的request queue的平均長度
    • await:
      平均I/O時間,從發(fā)出request到request完成
    • r_await: w_await:
      r/w 平均等待時間
    • %util:
      I/O時間占比,也就是整個系統(tǒng)有多少的時間是處于I/O中,當(dāng)100%時,該device就接近飽和了

mpstat

vmstat,iostat給出的都是根據(jù)CPU核數(shù)平均之后的數(shù)據(jù),mpstat可以統(tǒng)計per kernel的數(shù)據(jù)。

vmstat [options] [delay [count]]

options

  • A
    顯示每個核的統(tǒng)計信息
  • I
    顯示中斷統(tǒng)計
  • u
    顯示CPU統(tǒng)計數(shù)據(jù)
    一般的用法是
    mpstat -p ALL [interval]

mpstat報告內(nèi)容

  • CPU統(tǒng)計信息
    • CPU
      CPU核序號
    • %usr
    • %sys
    • %iowait
    • %irq
      CPU服務(wù)硬件中斷時間占比
    • %soft
      CPU服務(wù)軟中斷占比

uptime

uptime主要是以15,5,1分鐘為單位統(tǒng)計了過去這段時間CPU的負荷。

free

free是查看當(dāng)前內(nèi)存使用量的工具,它可以查看:

  • total
  • used
  • free
  • shared
  • buff/cache
  • available
    total = used + free + buff/cache
    ps

ps命令會按進程為單位顯示一些統(tǒng)計數(shù)據(jù)。事實上ps命令的實現(xiàn)就是去proc文件系統(tǒng)查詢對應(yīng)的數(shù)據(jù)。

  • %CPU
  • %MEM
  • VSZ
    virtual size
  • RSS
    resident set size

pmap

proc文件系統(tǒng)

proc文件系統(tǒng)是linux提供的內(nèi)核文件系統(tǒng),在linux源碼Documentation/filesystems/proc.txt里有詳細介紹。

proc根目錄

proc/[PID]目錄

cmdline

cmdline給出了該process的運行命令。

/usr/lib/at-spi2-core/at-spi2-registryd^@--use-gnome-session^@

以^@(null)分割

cwd

指向當(dāng)前工作目錄的軟鏈接

environ

環(huán)境變量,同cmdline一樣是null分隔的字符串

exe

指向執(zhí)行程序的軟鏈接

fd

文件描述符目錄,里面是全部file descriptor

maps

maps文件描述了程序的線性地址映射列表,看這個文件可以了解到程序當(dāng)前的地址空間分布

mem

root

stat

stat文件很有意思,就是一列數(shù)字,具體的含義需要去查文檔。

statm

statm提供了進程的內(nèi)存統(tǒng)計數(shù)據(jù),包括:

  • total memory
  • resident set size
  • shared pages

status

status提供了很多進程的監(jiān)測數(shù)據(jù),其中比較有用的有:

  • FDSize
    當(dāng)前的file descriptor數(shù)量
  • VmSize
    進程線性空間大小
  • VmRss
    進程實際占用物理內(nèi)存大小
  • VmPeak
    進程線性空間大小峰值
    <em>
    當(dāng)進程向linux系統(tǒng)請求一些內(nèi)存空間的時候,linux系統(tǒng)并不會立刻給進程分配物理頁面,它只是做了一個mark,增加了一下進程的線性空間(vm_area_struct),表示這個進程又多了一塊可訪問地址,這些值會被統(tǒng)計在VmSize里,因此VmSize表示進程邏輯上的內(nèi)存大小。
    當(dāng)進程真正訪問到請求的地址時,linux才會因為page fault去給進程真正分配物理page,這個實際分配的大小記錄在VmRss里。同樣,當(dāng)進程內(nèi)存不夠用時,系統(tǒng)可能將其它進程的物理page斷開然后swap到交換設(shè)備上,這時候,其它進程的VmRss是減小的,參見stackoverflow上的回答。
    </em>

pagemap

meminfo

meminfo里有內(nèi)存的很多信息

diskstats

diskstats文件包含了以下數(shù)據(jù):
1 - major number
2 - minor mumber
3 - device name
4 - reads completed successfully
5 - reads merged
6 - sectors read
7 - time spent reading (ms)
8 - writes completed
9 - writes merged
10 - sectors written
11 - time spent writing (ms)
12 - I/Os currently in progress
13 - time spent doing I/Os (ms)
14 - weighted time spent doing I/Os (ms)
根據(jù)這些數(shù)據(jù)可以猜想iostat沒準(zhǔn)就是通過/proc/diskstats文件來計算監(jiān)測數(shù)據(jù)的,strace iostat果然證明了這個猜想

loadavg

看名字也能看出來大概是說啥了

性能分析方法:

CPU

CPU分析太細級別的不一定有特別大的意義,統(tǒng)計工具有:

  • uptime
    觀察CPU load average = number of [runnable, uninterruptable] processes

  • vmstat
    vmstat提供了user,system以及id的時間比率,以及r(run queue)的長度

  • mpstat
    mpstat -P ALL可以觀測每一個CPU核的統(tǒng)計數(shù)據(jù),如果很不均勻,可以考慮多線程來提高CPU利用率

  • pidstat
    pidstat根據(jù)CPU或者進程來統(tǒng)計使用情況。

  • time
    /usr/bin/time,注意不是直接的time,加上-v可以顯示一個程序的統(tǒng)計信息。包括

    • Majo (I/O) page faults
    • Minor (reclaiming a frame) page faults
    • Swaps

    要理解上述參數(shù)可以參閱, Minor page faults表示程序訪問了可以復(fù)用的page,而Major page faults表示程序訪問了需要通過I/O調(diào)入的page。
    一個簡單的實驗就是調(diào)用兩次/usr/bin/time -v firefox,第一次調(diào)用啟動時間較長,400多個major page faults, 35481個minor,第二次調(diào)用就快多了,而且是0個major page faults, 34434個minor。因為linux page cache系統(tǒng)緩存了firefox的執(zhí)行文件內(nèi)容。

  • htop
    htop也是一個實時監(jiān)測工具,可以用來初步判斷問題所在。

  • getdelay.c
    linux源代碼里Document目錄下有一個getdelay.c,編譯后可以通過-p PID的方式來讀取某一個程序的delay信息,包括:

    • Scheduler Latency
      進程花了多少時間等待CPU調(diào)度
    • Block I/O
      進程花了多少時間等待I/O完成
    • Swapping
      進程花了多少時間等待頁面調(diào)入
    • Memory reclaim
      進程花了多少時間等待cache頁面分配
  • profiling
    通過perf,systemtap等profiling工具,可以查看CPU熱點,分析CPU耗費在哪里。
    perf sched還可以統(tǒng)計scheduler latency,也就是進程切換導(dǎo)致的延時。(這個值我現(xiàn)在讀的有點疑問)

B神提到user:system CPU時間比反應(yīng)了這個程序的類型,高user time說明是計算密集型。
在B神的業(yè)務(wù)服務(wù)器上(IO密集型,大概100K syscall每秒),一般負載系數(shù)在2到8(線程數(shù)/cpu核數(shù)),user/system時間比大概是60/40。

Memory

Memory監(jiān)測一般是跟使用語言綁定,不過也有一些從系統(tǒng)層級觀測memory使用情況的工具。

概念

了解Memory工具前最好先了解linux系統(tǒng)里的幾個概念:

  • Main Memory
    也就是物理內(nèi)存
  • Virtual Memory
    進程所看到的線性地址內(nèi)存
  • Resident Memory
    有實際物理內(nèi)存對應(yīng)的virtual memory
  • Anonymous Memory
    沒有文件系統(tǒng)page對應(yīng)的內(nèi)存,一般就是進程的數(shù)據(jù)部分,包括stack和heap
  • Paging
    主存和存儲設(shè)備之間的page轉(zhuǎn)移
  • Anonymous Paging
    Anonymous Paging意味著把進程的stack或者heap swap出去,當(dāng)再次運行的時候又會需要把它們從磁盤上讀回來,這是非常hurting的現(xiàn)象
  • Page States
    • Unallocated
    • Allocated, unmapped
    • Allocated, mapped to main memory
    • Allocated, mapped to swap device
  • Linux Page System
    當(dāng)一個page request來的時候,linux按照以下順序分配內(nèi)存page
    • Free List
      free page列表
    • Page Cache
      從文件系統(tǒng)用作cache的page中分配
    • Swapping
      kswapd系統(tǒng)線程通過swap一些page出去來騰出空間
    • OOM Killer
      殺死一些進程來空出空間
    • Page回收
      Linux將page分為以下幾類:
      1. 不可回收頁:
        空閑頁,保留頁(PG_reserved),內(nèi)核分配頁,進程內(nèi)核態(tài)堆棧頁,臨時鎖定頁(PG_locked)
      2. 可交換頁
        用戶態(tài)anonymous頁(堆棧,堆),回收時將內(nèi)容保存到交換區(qū)
      3. 可同步頁
        用戶態(tài)地址空間(映射文件),有對應(yīng)磁盤文件頁,回收時需要做同步操作

檢測工具及方法

  • vmstat
    • swpd
      總共swap出去的memory
    • free
      當(dāng)前free memory
    • si, so
      swapped in swapped out memory,這兩個值反應(yīng)了系統(tǒng)memory壓力
  • slabtop
    slabtop可以了解linux kernel slab系統(tǒng)的內(nèi)存使用情況
  • Systemtap, perf
    這些tracer可以監(jiān)控系統(tǒng)事件了解內(nèi)存使用情況

File System

同樣,首先需要大致了解一些File system的概念

  • File system

  • Page Cache
    Linux使用統(tǒng)一的Page Cache系統(tǒng)來做磁盤和塊設(shè)備的Cache。Page Cache中的page內(nèi)包含的內(nèi)容可能有:

    1. 普通文件的內(nèi)容,page cache通過文件inode中的address_space結(jié)構(gòu)對應(yīng)起來
    2. 目錄內(nèi)容,linux像處理普通文件一樣處理目錄文件
    3. 從塊設(shè)備直接讀出的內(nèi)容(繞過文件系統(tǒng))
    4. 用戶process被swap out的內(nèi)容,雖然被swap out,但是有些內(nèi)容會被先cache起來
    5. 特殊的文件系統(tǒng)文件,比如shm文件系統(tǒng)
  • Linux系統(tǒng)是如何讀取一個文件的(非Direct I/O, Memory Mapping, Asynchronous)

    1. read調(diào)用,最后到generic_file_read(filep, buf, count, ppos),參數(shù)分別表示文件句柄,存放內(nèi)容的數(shù)組,讀取內(nèi)容數(shù)量和起始偏移量。
    2. generic_file_read初始化一個iovec,存放buf和count和一個kiocb,用來控制I/O過程,然后call __generic_file_aio_read:
    3. 檢查緩沖區(qū)有效
    4. 建立一個read_descriptor_t,表示讀取操作狀態(tài)
    5. do_generic_file_read(filp, ppos, read_descriptor_t, &file_read_actor)
    6. do_generic_file_read會執(zhí)行實際的拷貝,過程如下:
    7. 取得filep->f_mapping,address_space對象
    8. 將文件看過頁數(shù)組,算出起止序號
    9. 循環(huán)讀入頁,(read_page方法):
    * 處理預(yù)讀頁
    * 在頁緩存中尋找頁,找不到則申請空白頁
    * 在頁緩存中找到頁的話,讀取結(jié)束
    * 調(diào)用address_space->readpage方法讀取數(shù)據(jù)到頁緩存
    * 調(diào)用file_read_actor把數(shù)據(jù)拷貝到用戶緩存
    
    1. 修改文件inode的update_atime,mark this inode dirty
      <em>
      read_page方法:
      普通文件read_page: 首先計算文件在磁盤上的塊號,如果是連續(xù)的,發(fā)出一個block I/O請求,否則用一次一塊的方法讀取。
      塊設(shè)備文件read_page: 將page看做塊緩沖區(qū),逐塊讀取。
      </em>
  • Linux系統(tǒng)如何寫入一個文件

    1. generic_file_write(filep, buf, count, ppos)
    2. 獲取文件inode->i_sem信號量用以控制同步寫入
    3. 創(chuàng)建kiocb, iovec,調(diào)用__generic_file_aio_write_block:
    • 首先在page cache中搜索對應(yīng)頁
    • 如果沒有對應(yīng)頁,新建一個頁框,調(diào)用address_space->prepare_write
    • 拷貝寫入內(nèi)容到頁中,調(diào)用address_space->commit_write,標(biāo)記頁dirty
    1. 釋放inode->i_sem
    2. 如果需要sync,調(diào)用address_space的writepages方法
    3. 到這一步,write操作已經(jīng)返回了。標(biāo)記為dirty的page最終寫到磁盤上,則是延遲執(zhí)行的,調(diào)用address_space->writepages方法
  • 內(nèi)存映射mmap
    內(nèi)存映射簡單的說,就是直接把文件內(nèi)容讀入page cache,并通過修改進程的mm_struct中的vm_area_struct來讓一部分線性空間指向page cache中的page的物理地址。這樣進程如果只需要讀取,就相對read調(diào)用少了一次往user buffer里拷貝的過程,但如果進程要把數(shù)據(jù)復(fù)制出來的話,其實跟直接調(diào)用read區(qū)別不大。
    同樣,文件內(nèi)容讀取也是延后的,直到進程訪問地址產(chǎn)生page fault時,操作系統(tǒng)才會去讀入文件內(nèi)容到對應(yīng)page frame。
    所以修改內(nèi)存數(shù)據(jù)會直接修改page cache中的page內(nèi)容,導(dǎo)致page為dirty,操作系統(tǒng)之后會將臟page寫入磁盤,更新本地文件。

  • Direct I/O
    Linux還提供一個Direct I/O,數(shù)據(jù)直接從設(shè)備傳遞到用戶空間,繞過文件緩存系統(tǒng),好處是少了一次數(shù)據(jù)從系統(tǒng)內(nèi)核到用戶空間的拷貝。壞處是用戶空間的頁將會被鎖定不能換出,這是與linux系統(tǒng)內(nèi)存使用理念相悖的。

檢測工具

Network

  • netstat -s
  • netstat -i
  • tcpdump
  • stap/perf
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容