轉自:https://www.cnblogs.com/arnoldlu/p/10040802.html
關鍵詞:sar、sadc、ksar、/proc/stat、/proc/cpuinfo、/proc/meminfo、/proc/diskstats。
在之前有簡單介紹過sar/ksar,最近在使用中感覺需要再深入了解一下。
ksar/sar從內核采集數據,並輸出可讀性數據。分析相關源碼,有助於知道數據來龍去脈。--------------------------------------1. sar源碼概覽
ksar顯示sar/sadc獲取的數據,並圖形化顯示。數據從內核節點,到sadc/sar轉換,再到ksar顯示。----------------------------2. ksar處理流程
對照ksar每張圖表,然后sar/sadc對應的采集轉換,再到內核每個數據項含義解析。--------------------------------------------3. ksar解讀
期望是從ksar上的圖表能對應到內核的代碼,明白這些圖表數據根源。
1. sar源碼概覽
sar作為sysstat一部分,相關的工具還包括sadc、sa1、sa2。
sa1負責收集並存儲每天系統動態信息到一個二進制的文件中,sa1是sadc所涉及的程序前端程序。通常由計划任務工具cron來調用。打開sa1文件不難看出就是調用sadc今次那個采集數據。
sa2就是調用sar命令,將當日二進制日志文件數據存儲到文本文件中。
sadc是系統動態數據收集工具,收集的數據被寫入一個二進制文件中,它是sar工具后端。
sar負責解析sadc保存的數據,並顯示出來。
當使用sar進行數據統計的時候,通過pstree `pidof sar`,可以看出sar調用了sadc。
sar───sadc
1.1 sadc信息采樣
sadc入口在sadc.c中,主要是解析參數、啟動一個interval alarm、rw_sa_stat_loop()讀取數據。
通過alarm觸發SIGALRM實現周期性讀取,SIGINT停止讀取。
int main(int argc, char **argv)
{
int opt = 0;
char ofile[MAX_FILE_LEN], sa_dir[MAX_FILE_LEN];
int stdfd = 0, ofd = -1;
int restart_mark;
long count = 0;
/* Get HZ */
get_HZ();
/* Compute page shift in kB */
get_kb_shift();
ofile[0] = sa_dir[0] = comment[0] = '\0';
...-----------------------------------------------------------解析參數並進行配置。
/* Set a handler for SIGALRM */
memset(&alrm_act, 0, sizeof(alrm_act));
alrm_act.sa_handler = alarm_handler;
sigaction(SIGALRM, &alrm_act, NULL);
alarm(interval);------------------------------------------啟動一個alarm,超時未interval,並通過在alarm_handler()中重新起一個alarm實現周期性處理。
/* Main loop */
rw_sa_stat_loop(count, stdfd, ofd, ofile, sa_dir);
#ifdef HAVE_SENSORS
/* Cleanup sensors */
sensors_cleanup();
#endif /* HAVE_SENSORS */
/* Free structures */
sa_sys_free();
return 0;
}
rw_sa_stat_loop()是整個sadc的核心循環,這里從個sysfs讀取信息,並提取關鍵信息,然后保存。
void rw_sa_stat_loop(long count, int stdfd, int ofd, char ofile[], char sa_dir[]) { int do_sa_rotat = 0; unsigned int save_flags; char new_ofile[MAX_FILE_LEN] = ""; struct tm rectime = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL}; /* Set a handler for SIGINT */ memset(&int_act, 0, sizeof(int_act)); int_act.sa_handler = int_handler; sigaction(SIGINT, &int_act, NULL); /* Main loop */ do { reset_stats(); ... /* Read then write stats */ read_stats();--------------------------------------------------------遍歷act[]中所有的struct activity,進行采樣。 if (stdfd >= 0) { save_flags = flags; flags &= ~S_F_LOCK_FILE; write_stats(stdfd);----------------------------------------------通過標准輸出文件打印信息。 flags = save_flags; } /* If the record type was R_LAST_STATS, tag it R_STATS before writing it */ record_hdr.record_type = R_STATS; if (ofile[0]) { write_stats(ofd);------------------------------------------------將結果寫到指定文件中。 } ... fflush(stdout); if (count > 0) { count--; } if (count) { /* Wait for a signal (probably SIGALRM or SIGINT) */ pause();----------------------------------------------------------此處和alarm()配合達到周期性采樣的效果。 } if (sigint_caught)----------------------------------------------------如果收到SIGINT信號,提前終止采樣。 /* SIGINT caught: Stop now */ break; ... } while (count);------------------------------------------------------------達到總采樣數,同樣停止采樣。 /* Close file descriptors if they have actually been used */ CLOSE(stdfd); CLOSE(ofd); }
read_stats()是核心采集數據函數,核心數據結構式act[]。
void read_stats(void)
{
int i;
__nr_t cpu_nr = act[get_activity_position(act, A_CPU, EXIT_IF_NOT_FOUND)]->nr;
record_hdr.uptime0 = 0;
if (cpu_nr > 2) {
read_uptime(&(record_hdr.uptime0));
}
for (i = 0; i < NR_ACT; i++) {
if (IS_COLLECTED(act[i]->options)) {---------------------------------------遍歷所有act[],如果act[]中對應的options包含AO_COLLECTED則進行f_read()。
/* Read statistics for current activity */ (*act[i]->f_read)(act[i]);
}
}
if (cpu_nr == 1) {
record_hdr.uptime0 = record_hdr.uptime;
}
}
act[]存放了所有統計事件的struct activity。
struct activity *act[NR_ACT] = { &cpu_act, &pcsw_act, &irq_act, &swap_act, &paging_act, &io_act, &memory_act, &huge_act, &ktables_act, &queue_act, &serial_act, &disk_act, /* <network> */ &net_dev_act, &net_edev_act, &net_nfs_act, &net_nfsd_act, &net_sock_act, &net_ip_act, &net_eip_act, &net_icmp_act, &net_eicmp_act, &net_tcp_act, &net_etcp_act, &net_udp_act, &net_sock6_act, &net_ip6_act, &net_eip6_act, &net_icmp6_act, &net_eicmp6_act, &net_udp6_act, &fchost_act, &softnet_act, /* AO_CLOSE_MARKUP */ /* </network> */ /* <power-management> */ &pwr_cpufreq_act, &pwr_fan_act, &pwr_temp_act, &pwr_in_act, &pwr_wghfreq_act, &pwr_usb_act, /* AO_CLOSE_MARKUP */ /* </power-management> */ &filesystem_act };
1.2 sar顯示統計信息
read_sadc_stat_bunch()讀取統計信息,write_stats()調用每個struct activity的f_print()函數,write_stats_avg()調用每個struct activity的f_print_avg()函數。
f_print()和f_print_avg()或從文件中解析字符串,或啟動sadc采樣,然后再解析。
2 ksar處理流程
2.1 ksar介紹
ksar資源:ksar安裝文件。
ksar中看到的圖標是結果,這些數據是通過sadc采集,sar解析出來的。
sadc是通過讀取sysfs/procfs節點來獲取信息,這些節點都是內核提供的統計信息。
sar -o temp.bin 1 600--------------------------------------------------sar將采樣數據保存在temp.bin中。
LC_ALL=C sar -A -f temp.bin > sar.txt----------------------------將保存的采樣數據temp.bin,轉換成更可讀性強的文本文件。
所以ksar的每一張圖標,都對應了內核統計信息。
sar是數據搬運整理工具,ksar是圖形化工具。
下面對每張圖標從ksar,到sar/sadc,最終到內核中每個數據。
2.2 ksar操作
2.2.1 數據導入
通過Data->Append from a file...從txt中加載數據,還有其他兩種數據來源方式。

2.2.2 數據導出
如果要將圖表導出,可以在每張圖標下面選擇Export PNG之類。
或者通過Export->Export to PDF...,選擇指定選項。

3. ksar解析
下面結合ksar圖表來逐項解析。
3.1 CPU信息
cpu_act讀取/proc/stat節點,解析其中cpu信息,包括合計cpu信息以及單個cpu信息。
/proc/stat中的信息包括一個合計以及多個cpu單獨統計信息。
從/proc/stat中讀取的信息都是從啟動以來的累計時間,在圖標中顯示的是一個時間段的差值。
然后計算不同模塊耗時占比。

void read_stat_cpu(struct stats_cpu *st_cpu, int nbr,
unsigned long long *uptime, unsigned long long *uptime0)
{
FILE *fp;
struct stats_cpu *st_cpu_i;
struct stats_cpu sc;
char line[8192];
int proc_nb;
if ((fp = fopen(STAT, "r")) == NULL) {----------------------------------------------------/proc/stat
fprintf(stderr, _("Cannot open %s: %s\n"), STAT, strerror(errno));
exit(2);
}
while (fgets(line, sizeof(line), fp) != NULL) {
if (!strncmp(line, "cpu ", 4)) {------------------------------------------------------統計總cpu不同類別耗時,詳細信息參考/proc/stat。這里的信息和內核中一一對應。
memset(st_cpu, 0, STATS_CPU_SIZE);
sscanf(line + 5, "%llu %llu %llu %llu %llu %llu %llu %llu %llu %llu",
&st_cpu->cpu_user,
&st_cpu->cpu_nice,
&st_cpu->cpu_sys,
&st_cpu->cpu_idle,
&st_cpu->cpu_iowait,
&st_cpu->cpu_hardirq,
&st_cpu->cpu_softirq,
&st_cpu->cpu_steal,
&st_cpu->cpu_guest,
&st_cpu->cpu_guest_nice);
*uptime = st_cpu->cpu_user + st_cpu->cpu_nice +
st_cpu->cpu_sys + st_cpu->cpu_idle +
st_cpu->cpu_iowait + st_cpu->cpu_hardirq +
st_cpu->cpu_steal + st_cpu->cpu_softirq;
}
else if (!strncmp(line, "cpu", 3)) {
...
}
fclose(fp);
}
3.2 進程創建及切換
pcsw_act讀取/proc/stat節點,解析其中的ctxt和processes信息。
同樣的/proc/stat中,統計信息是啟動以來的累計值,圖標中現實的不同時間段的差值。
proc/s表示每秒創建的進程數目,cswch/s表示每秒進程切換次數。

void read_stat_pcsw(struct stats_pcsw *st_pcsw)
{
FILE *fp;
char line[8192];
if ((fp = fopen(STAT, "r")) == NULL)
return;
while (fgets(line, sizeof(line), fp) != NULL) {
if (!strncmp(line, "ctxt ", 5)) {--------------------------------------------ctxt是所有CPU的進程切換次數。
/* Read number of context switches */
sscanf(line + 5, "%llu", &st_pcsw->context_switch);
}
else if (!strncmp(line, "processes ", 10)) {---------------------------------是整個系統創建進程的次數,total_forks,
/* Read number of processes created since system boot */
sscanf(line + 10, "%lu", &st_pcsw->processes);
}
}
fclose(fp);
}
3.3 swap信息
swap_act讀取/proc/vmstat節點,解析其中的pswpin和pswpout兩個信息。
pswpin/s表示系統每秒從swap分區讀入的頁面數量,即移除掉swap;pswpout/s表示系統每秒寫到swap分區的頁面數量,即產生swap。

void read_vmstat_swap(struct stats_swap *st_swap)
{
FILE *fp;
char line[128];
if ((fp = fopen(VMSTAT, "r")) == NULL)
return;
while (fgets(line, sizeof(line), fp) != NULL) {
if (!strncmp(line, "pswpin ", 7)) {-------------------------------對應PSWPIN,表示從swap分區讀入頁面。
/* Read number of swap pages brought in */
sscanf(line + 7, "%lu", &st_swap->pswpin);
}
else if (!strncmp(line, "pswpout ", 8)) {-------------------------對應PSWPOUT,表示將page寫入到swap分區。
/* Read number of swap pages brought out */
sscanf(line + 8, "%lu", &st_swap->pswpout);
}
}
fclose(fp);
}
3.4 頁面統計信息
paging_act統計/proc/vmstat中頁面交換統計信息。
Paging包括四張圖標,分別統計/proc/vmstat中的PGPGIN、PGPGOUT、PGFAULT、PGMAJFAULT等等信息。

pgpgin/s表示系統每秒從磁盤中paged多少KB;pgpgout/s則表示到磁盤中多少KB。
fault/s表示系統每秒產生的頁面異常數目,包括major和minor;majflt/s則表示頁面異常是從磁盤中產生的。
pgfree/s表示每秒放入到free list的頁面數量;pgscank/s、pgscand/s都表示掃描的頁面數量,只不過前者表示kswapd掃描結果,后者表示直接掃描;pgsteal/s都表示從pagecache和swapcache中回收的頁面數量。
%vmeff是pgsteal/pgscan表示頁面回收的效率。
看fault/s和pgfree/s存在一定關系,前者表示內存申請,后者表示內存釋放,存在先后關系。但是單位不一致,前者是次數,后者是頁面。
void read_vmstat_paging(struct stats_paging *st_paging)
{
FILE *fp;
char line[128];
unsigned long pgtmp;
if ((fp = fopen(VMSTAT, "r")) == NULL)
return;
st_paging->pgsteal = 0;
st_paging->pgscan_kswapd = st_paging->pgscan_direct = 0;
while (fgets(line, sizeof(line), fp) != NULL) {
if (!strncmp(line, "pgpgin ", 7)) {------------------------------------------------pgpgin和pgpgout分別對應PGPGIN和PGPGOUT,在submit_io()中更新。指內存和塊設備志堅page數目,這里的page指的是disk sector。pgpgin表示從塊設備讀入,pgpgout寫入到跨設備。
/* Read number of pages the system paged in */
sscanf(line + 7, "%lu", &st_paging->pgpgin);
}
else if (!strncmp(line, "pgpgout ", 8)) {
/* Read number of pages the system paged out */
sscanf(line + 8, "%lu", &st_paging->pgpgout);
}
else if (!strncmp(line, "pgfault ", 8)) {-------------------------------------------在vm_event_item[]中對應PGFAULT,在handle_mm_fault()中更新。統計產生page fault的信息。
/* Read number of faults (major+minor) made by the system */
sscanf(line + 8, "%lu", &st_paging->pgfault);
}
else if (!strncmp(line, "pgmajfault ", 11)) {---------------------------------------對應PGMAJFAULT,表示從磁盤而不是從內存中獲取數據。
/* Read number of faults (major only) made by the system */
sscanf(line + 11, "%lu", &st_paging->pgmajfault);
}
else if (!strncmp(line, "pgfree ", 7)) {--------------------------------------------對應PGFREE,統計釋放的頁面次數。
/* Read number of pages freed by the system */
sscanf(line + 7, "%lu", &st_paging->pgfree);
}
else if (!strncmp(line, "pgsteal_", 8)) {-------------------------------------------統計PGSTEAL_KSWAPD和PGSTEAL_DIRECT信息,表示系統回收的kswapd和直接回收的頁面數目。
/* Read number of pages stolen by the system */
sscanf(strchr(line, ' '), "%lu", &pgtmp);
st_paging->pgsteal += pgtmp;
}
else if (!strncmp(line, "pgscan_kswapd", 13)) {--------------------------------------統計PGSCAN_KSWAPD信息,表示從系統啟動到現在kswapd后台進程掃描的頁面數。
/* Read number of pages scanned by the kswapd daemon */
sscanf(strchr(line, ' '), "%lu", &pgtmp);
st_paging->pgscan_kswapd += pgtmp;
}
else if (!strncmp(line, "pgscan_direct", 13)) {--------------------------------------統計PGSCAN_DIRECT和PGSCAN_DIRECT_THROTTLE,統計世界回收頁面數。
/* Read number of pages scanned directly */
sscanf(strchr(line, ' '), "%lu", &pgtmp);
st_paging->pgscan_direct += pgtmp;
}
}
fclose(fp);
}
3.5 IO統計信息
io_act從/proc/diskstats讀取IO統計信息。

上面的tps、rtps、wtps、bread/s、bwrtn/s分別對應采樣結果dk_drive、dk_drive_rio、dk_drive_wio、dk_drive_rblk、dk_drive_wblk。
其中tps為rtps和wtps之和,表示完成讀寫次數;bread/s和bwrtn/s表示讀、寫扇區數目。
這5個參數都來源於diskstats_show()函數。
void read_diskstats_io(struct stats_io *st_io)
{
FILE *fp;
char line[256];
char dev_name[MAX_NAME_LEN];
unsigned int major, minor;
unsigned long rd_ios, wr_ios, rd_sec, wr_sec;
if ((fp = fopen(DISKSTATS, "r")) == NULL)
return;
while (fgets(line, sizeof(line), fp) != NULL) {
if (sscanf(line, "%u %u %s %lu %*u %lu %*u %lu %*u %lu",-----------scanf的%u之間的星號,表示跳過此輸入。
&major, &minor, dev_name,
&rd_ios, &rd_sec, &wr_ios, &wr_sec) == 7) {
if (is_device(dev_name, IGNORE_VIRTUAL_DEVICES)) {--------------虛擬設備沒有/sys/block/<device>/device,此特性用以判斷dev_name對應的設備是否是真實的塊設備。
st_io->dk_drive += (unsigned long long) rd_ios + (unsigned long long) wr_ios;
st_io->dk_drive_rio += rd_ios;
st_io->dk_drive_rblk += rd_sec;
st_io->dk_drive_wio += wr_ios;
st_io->dk_drive_wblk += wr_sec;
}
}
}
fclose(fp);
}
/proc/diskstats來源於內核的diskstats_show()
static int diskstats_show(struct seq_file *seqf, void *v) { struct gendisk *gp = v; struct disk_part_iter piter; struct hd_struct *hd; char buf[BDEVNAME_SIZE]; int cpu; /* if (&disk_to_dev(gp)->kobj.entry == block_class.devices.next) seq_puts(seqf, "major minor name" " rio rmerge rsect ruse wio wmerge " "wsect wuse running use aveq" "\n\n"); */ disk_part_iter_init(&piter, gp, DISK_PITER_INCL_EMPTY_PART0); while ((hd = disk_part_iter_next(&piter))) { cpu = part_stat_lock(); part_round_stats(cpu, hd); part_stat_unlock(); seq_printf(seqf, "%4d %7d %s %lu %lu %lu " "%u %lu %lu %lu %u %u %u %u\n", MAJOR(part_devt(hd)), MINOR(part_devt(hd)), disk_name(gp, hd->partno, buf), part_stat_read(hd, ios[READ]),-------------------------------------------------成功完成讀的次數。 part_stat_read(hd, merges[READ]),----------------------------------------------合並讀次數,為了效率可能會合並相鄰的讀和寫。 part_stat_read(hd, sectors[READ]),---------------------------------------------讀扇區的次數。 jiffies_to_msecs(part_stat_read(hd, ticks[READ])),-----------------------------讀花的時間,這里是所有讀操作所花費的毫秒數。 part_stat_read(hd, ios[WRITE]),------------------------------------------------成功完成寫的次數。 part_stat_read(hd, merges[WRITE]),---------------------------------------------合並寫次數。 part_stat_read(hd, sectors[WRITE]),--------------------------------------------寫扇區次數。 jiffies_to_msecs(part_stat_read(hd, ticks[WRITE])),----------------------------寫花的時間。 part_in_flight(hd), jiffies_to_msecs(part_stat_read(hd, io_ticks)), jiffies_to_msecs(part_stat_read(hd, time_in_queue)) ); } disk_part_iter_exit(&piter); return 0; }
3.6 內存及swap統計信息
memory_act統計/proc/meminfo的內存和swap信息。
kbmemfree是所有free內存大小,kbavail是扣除保留內存,加上部分pagecache和可回收內存;kbmemused是目前系統內存使用量,不包括內核使用量。
%memused是內存使用占總內存百分比。
kbcommit是當前場景系統需要使用到的內存量,實際上可能並沒有申請這么多內存。這是因為分配的內存只有在使用到時,才會產生缺頁異常。
這里的kbcommit和頁面統計信息相呼應,此處kbcommit突然增加,產生了很多page fault。

從圖表中的名稱大概就能找到對應的/proc/meminfo中的字符項。
其中%memused和%commit查看stub_print_memory_stats(),%memused為(MemTotal-MemFree)/MemTotal。
%commit為Commited_AS/(MemTotal+SwapTotal)。
在/proc/meminfo中看到的CommitLimit可能是proc/sys/vm/overcommit_kbytes;或者按照當前可以用內存乘以overcommit_ratio這個比例得到。
在實際使用中,還需要結合overcommit_memory類型來看,參考overcommit_memory和overcommit_ratio。
unsigned long vm_commit_limit(void)
{
unsigned long allowed;
if (sysctl_overcommit_kbytes)
allowed = sysctl_overcommit_kbytes >> (PAGE_SHIFT - 10);
else
allowed = ((totalram_pages - hugetlb_total_pages())
* sysctl_overcommit_ratio / 100);
allowed += total_swap_pages;
return allowed;
}
read_meminfo()從/proc/meminfo中解析數據。
void read_meminfo(struct stats_memory *st_memory)
{
FILE *fp;
char line[128];
if ((fp = fopen(MEMINFO, "r")) == NULL)
return;
while (fgets(line, sizeof(line), fp) != NULL) {
if (!strncmp(line, "MemTotal:", 9)) {
/* Read the total amount of memory in kB */
sscanf(line + 9, "%lu", &st_memory->tlmkb);
}
else if (!strncmp(line, "MemFree:", 8)) {
/* Read the amount of free memory in kB */
sscanf(line + 8, "%lu", &st_memory->frmkb);
}
else if (!strncmp(line, "MemAvailable:", 13)) {
/* Read the amount of available memory in kB */
sscanf(line + 13, "%lu", &st_memory->availablekb);
}
else if (!strncmp(line, "Buffers:", 8)) {
/* Read the amount of buffered memory in kB */
sscanf(line + 8, "%lu", &st_memory->bufkb);
}
else if (!strncmp(line, "Cached:", 7)) {
/* Read the amount of cached memory in kB */
sscanf(line + 7, "%lu", &st_memory->camkb);
}
else if (!strncmp(line, "SwapCached:", 11)) {
/* Read the amount of cached swap in kB */
sscanf(line + 11, "%lu", &st_memory->caskb);
}
else if (!strncmp(line, "Active:", 7)) {
/* Read the amount of active memory in kB */
sscanf(line + 7, "%lu", &st_memory->activekb);
}
else if (!strncmp(line, "Inactive:", 9)) {
/* Read the amount of inactive memory in kB */
sscanf(line + 9, "%lu", &st_memory->inactkb);
}
else if (!strncmp(line, "SwapTotal:", 10)) {
/* Read the total amount of swap memory in kB */
sscanf(line + 10, "%lu", &st_memory->tlskb);
}
else if (!strncmp(line, "SwapFree:", 9)) {
/* Read the amount of free swap memory in kB */
sscanf(line + 9, "%lu", &st_memory->frskb);
}
else if (!strncmp(line, "Dirty:", 6)) {
/* Read the amount of dirty memory in kB */
sscanf(line + 6, "%lu", &st_memory->dirtykb);
}
else if (!strncmp(line, "Committed_AS:", 13)) {
/* Read the amount of commited memory in kB */
sscanf(line + 13, "%lu", &st_memory->comkb);
}
else if (!strncmp(line, "AnonPages:", 10)) {
/* Read the amount of pages mapped into userspace page tables in kB */
sscanf(line + 10, "%lu", &st_memory->anonpgkb);
}
else if (!strncmp(line, "Slab:", 5)) {
/* Read the amount of in-kernel data structures cache in kB */
sscanf(line + 5, "%lu", &st_memory->slabkb);
}
else if (!strncmp(line, "KernelStack:", 12)) {
/* Read the kernel stack utilization in kB */
sscanf(line + 12, "%lu", &st_memory->kstackkb);
}
else if (!strncmp(line, "PageTables:", 11)) {
/* Read the amount of memory dedicated to the lowest level of page tables in kB */
sscanf(line + 11, "%lu", &st_memory->pgtblkb);
}
else if (!strncmp(line, "VmallocUsed:", 12)) {
/* Read the amount of vmalloc area which is used in kB */
sscanf(line + 12, "%lu", &st_memory->vmusedkb);
}
}
fclose(fp);
}
下圖的kbswpfree、kbswpused、kbswpcad通過字面意思即可知其對應的meminfo項為SwapFree、SwapTotal-SwapFree、SwapCached。

3.7 中斷統計信息
中斷你統計信息同樣來自/proc/stat,解析intr字段。第一個是啟動以來所有中斷觸發次數,后面是單個中斷觸發次數。
生成的表格中包含了對應的圖標sum,以及每個中斷統計信息。

void read_stat_irq(struct stats_irq *st_irq, int nbr)
{
FILE *fp;
struct stats_irq *st_irq_i;
char line[8192];
int i, pos;
if ((fp = fopen(STAT, "r")) == NULL)
return;
while (fgets(line, sizeof(line), fp) != NULL) {
if (!strncmp(line, "intr ", 5)) {-------------------------------------------------/proc/stat的intr字段。
/* Read total number of interrupts received since system boot */
sscanf(line + 5, "%llu", &st_irq->irq_nr);
pos = strcspn(line + 5, " ") + 5;
for (i = 1; i < nbr; i++) {
st_irq_i = st_irq + i;
sscanf(line + pos, " %llu", &st_irq_i->irq_nr);
pos += strcspn(line + pos + 1, " ") + 1;
}
}
}
fclose(fp);
}
3.8
ktables_act
#define FDENTRY_STATE "/proc/sys/fs/dentry-state"
#define FFILE_NR "/proc/sys/fs/file-nr"
#define FINODE_STATE "/proc/sys/fs/inode-state"
#define PTY_NR "/proc/sys/kernel/pty/nr"
void read_kernel_tables(struct stats_ktables *st_ktables)
{
FILE *fp;
unsigned int parm;
int rc = 0;
/* Open /proc/sys/fs/dentry-state file */
if ((fp = fopen(FDENTRY_STATE, "r")) != NULL) {
rc = fscanf(fp, "%*d %u",
&st_ktables->dentry_stat);
fclose(fp);
if (rc == 0) {
st_ktables->dentry_stat = 0;
}
}
/* Open /proc/sys/fs/file-nr file */
if ((fp = fopen(FFILE_NR, "r")) != NULL) {
rc = fscanf(fp, "%u %u",
&st_ktables->file_used, &parm);
fclose(fp);
/*
* The number of used handles is the number of allocated ones
* minus the number of free ones.
*/
if (rc == 2) {
st_ktables->file_used -= parm;
}
else {
st_ktables->file_used = 0;
}
}
/* Open /proc/sys/fs/inode-state file */
if ((fp = fopen(FINODE_STATE, "r")) != NULL) {
rc = fscanf(fp, "%u %u",
&st_ktables->inode_used, &parm);
fclose(fp);
/*
* The number of inuse inodes is the number of allocated ones
* minus the number of free ones.
*/
if (rc == 2) {
st_ktables->inode_used -= parm;
}
else {
st_ktables->inode_used = 0;
}
}
/* Open /proc/sys/kernel/pty/nr file */
if ((fp = fopen(PTY_NR, "r")) != NULL) {
rc = fscanf(fp, "%u",
&st_ktables->pty_nr);
fclose(fp);
if (rc == 0) {
st_ktables->pty_nr = 0;
}
}
}
3.9 load和queue統計信息
queue_act從/proc/loadavg獲取load信息,從/proc/stat獲取queue統計信息。

ldavg-1、ldavg-5、ldavg-15分別表示1、5、15分鍾內進程對立中平均進程數目,包括正在運行的進程和准備好等待運行的進程數目。
runq-sz表示運行隊列大小,plist-sz表示所有進程總數nr_threads。
runq-sz大表示等待運行的進程數目較多。
blocked表示處於iowait狀態的進程數目。
void read_loadavg(struct stats_queue *st_queue)
{
FILE *fp;
char line[8192];
int load_tmp[3];
int rc;
if ((fp = fopen(LOADAVG, "r")) == NULL)
return;
/* Read load averages and queue length */
rc = fscanf(fp, "%d.%u %d.%u %d.%u %lu/%u %*d\n",------------------------------可以看出從/proc/loadavg中獲取信息,然后最后一個數據忽略。
&load_tmp[0], &st_queue->load_avg_1,
&load_tmp[1], &st_queue->load_avg_5,
&load_tmp[2], &st_queue->load_avg_15,
&st_queue->nr_running,
&st_queue->nr_threads);
fclose(fp);
if (rc < 8)
return;
st_queue->load_avg_1 += load_tmp[0] * 100;
st_queue->load_avg_5 += load_tmp[1] * 100;
st_queue->load_avg_15 += load_tmp[2] * 100;
if (st_queue->nr_running) {
/* Do not take current process into account */
st_queue->nr_running--;
}
/* Read nr of tasks blocked from /proc/stat */
if ((fp = fopen(STAT, "r")) == NULL)
return;
while (fgets(line, sizeof(line), fp) != NULL) {
if (!strncmp(line, "procs_blocked ", 14)) {--------------------------------從/proc/stat中獲取procs_blocked項,表示處於iowait狀態的進程數目。
/* Read number of processes blocked */
sscanf(line + 14, "%lu", &st_queue->procs_blocked);
break;
}
}
fclose(fp);
}
3.10 塊設備統計信息
disk_act和io_act一樣是從/proc/diskstats中獲取統計信息,

圖標中的四項tps表示塊設備讀寫次數頻率;rkB/s和wkB/s表示塊設備讀寫速率;await表示一次讀或寫耗時。
tps值大說明此段時間讀寫較頻繁。
await值大表示當前讀寫耗時較大,可能存在問題。
__print_funct_t print_disk_stats(struct activity *a, int prev, int curr,
unsigned long long itv)
{
...
for (i = 0; i < a->nr; i++) {
...
printf("%-11s", timestamp[curr]);
cprintf_in(IS_STR, " %9s", dev_name, 0);-----------------------------sdc是設備當前統計信息,sdp是設備前一次統計信息,itv是兩次時間間隔。
cprintf_f(NO_UNIT, 1, 9, 2,
S_VALUE(sdp->nr_ios, sdc->nr_ios, itv));----------------------對應圖標中的tps,nr_ios表示塊設備讀寫次數之和。tps表示塊設備讀寫頻率。
cprintf_f(unit, 2, 9, 2,
S_VALUE(sdp->rd_sect, sdc->rd_sect, itv) / 2,
S_VALUE(sdp->wr_sect, sdc->wr_sect, itv) / 2);-----------------對應圖標的rkB/s和wkB/s,同樣rd_sect和wr_sect可以計算出以KB為單位的讀寫速率。
/* See iostat for explanations */
cprintf_f(unit, 1, 9, 2,
xds.arqsz / 2);
cprintf_f(NO_UNIT, 3, 9, 2,
S_VALUE(sdp->rq_ticks, sdc->rq_ticks, itv) / 1000.0,
xds.await,------------------------------------------------------對應await,在compute_ext_disk_stats()中計算。表示每次讀寫平均耗時。
xds.svctm);
cprintf_pc(DISPLAY_UNIT(flags), 1, 9, 2,
xds.util / 10.0);
printf("\n");
}
}
3.11 CPU頻率
pwr_cpufreq_act從/proc/cpuinfo中獲取頻率信息,CPU Frequency all顯示的是所有CPU頻率的平均值。

從下面代碼分析可知,從關鍵詞processor中獲取CPU號,從cpu MHz獲取CPU頻率。
void read_cpuinfo(struct stats_pwr_cpufreq *st_pwr_cpufreq, int nbr)
{
FILE *fp;
struct stats_pwr_cpufreq *st_pwr_cpufreq_i;
char line[1024];
int nr = 0;
unsigned int proc_nb = 0, ifreq, dfreq;
if ((fp = fopen(CPUINFO, "r")) == NULL)
return;
st_pwr_cpufreq->cpufreq = 0;
while (fgets(line, sizeof(line), fp) != NULL) {
if (!strncmp(line, "processor\t", 10)) {
sscanf(strchr(line, ':') + 1, "%u", &proc_nb);
}
/* Entry in /proc/cpuinfo is different between Intel and Power architectures */
else if (!strncmp(line, "cpu MHz\t", 8) ||
!strncmp(line, "clock\t", 6)) {
sscanf(strchr(line, ':') + 1, "%u.%u", &ifreq, &dfreq);
if (proc_nb < (nbr - 1)) {
st_pwr_cpufreq_i = st_pwr_cpufreq + proc_nb + 1;
st_pwr_cpufreq_i->cpufreq = ifreq * 100 + dfreq / 10;
st_pwr_cpufreq->cpufreq += st_pwr_cpufreq_i->cpufreq;
nr++;
}
else if (!proc_nb && (nbr == 1)) {
st_pwr_cpufreq->cpufreq = ifreq * 100 + dfreq / 10;
}
}
}
fclose(fp);
...
}
3.12 統計串口線信息
serial_act從/proc/tty/driver/serial獲取串口線統計信息。
void read_tty_driver_serial(struct stats_serial *st_serial, int nbr)
{
FILE *fp;
struct stats_serial *st_serial_i;
int sl = 0;
char line[256];
char *p;
if ((fp = fopen(SERIAL, "r")) == NULL)
return;
while ((fgets(line, sizeof(line), fp) != NULL) && (sl < nbr)) {
if ((p = strstr(line, "tx:")) != NULL) {
st_serial_i = st_serial + sl;
sscanf(line, "%u", &st_serial_i->line);
/*
* A value of 0 means an unused structure.
* So increment it to make sure it is not zero.
*/
(st_serial_i->line)++;
/*
* Read the number of chars transmitted and received by
* current serial line.
*/
sscanf(p + 3, "%u", &st_serial_i->tx);
if ((p = strstr(line, "rx:")) != NULL) {
sscanf(p + 3, "%u", &st_serial_i->rx);
}
if ((p = strstr(line, "fe:")) != NULL) {
sscanf(p + 3, "%u", &st_serial_i->frame);
}
if ((p = strstr(line, "pe:")) != NULL) {
sscanf(p + 3, "%u", &st_serial_i->parity);
}
if ((p = strstr(line, "brk:")) != NULL) {
sscanf(p + 4, "%u", &st_serial_i->brk);
}
if ((p = strstr(line, "oe:")) != NULL) {
sscanf(p + 3, "%u", &st_serial_i->overrun);
}
sl++;
}
}
fclose(fp);
}
3.13 獲取網口統計信息
net_dev_act從/proc/net/dev中獲取網口的統計信息,從/sys/class/net/xxx/duplex和/sys/class/net/xxx/speed中獲取雙工和速度信息。

從下面關於/proc/net/dev解釋可知,rxpck對應rx_packets,rxpck/s就是每秒接收packet數;txpck/s就是每個發送packet數目。
rxkB/s是每秒接受多少k字節;txkB/s是每秒發送多少k字節。
rxmcst/s表示每秒接收到的multicast packet數目。
%ifutil跟每秒發送接收的KB數有關,還跟全雙工,單雙工有關,以及設備的速度有關;表示網絡設備使用率。
雙工信息從/sys/class/net/%s/duplex獲取,速度信息從從/sys/class/net/%s/speed獲取。
從此圖看,網絡的使用率並不高,說明網絡不頻繁。
int read_net_dev(struct stats_net_dev *st_net_dev, int nbr)
{
FILE *fp;
struct stats_net_dev *st_net_dev_i;
char line[256];
char iface[MAX_IFACE_LEN];
int dev = 0;
int pos;
if ((fp = fopen(NET_DEV, "r")) == NULL)----------------------------------------------------------------從/proc/net/dev獲取信息。
return 0;
while ((fgets(line, sizeof(line), fp) != NULL) && (dev < nbr)) {
pos = strcspn(line, ":");
if (pos < strlen(line)) {
st_net_dev_i = st_net_dev + dev;
strncpy(iface, line, MINIMUM(pos, MAX_IFACE_LEN - 1));
iface[MINIMUM(pos, MAX_IFACE_LEN - 1)] = '\0';
sscanf(iface, "%s", st_net_dev_i->interface); /* Skip heading spaces */
sscanf(line + pos + 1, "%llu %llu %*u %*u %*u %*u %llu %llu %llu %llu "
"%*u %*u %*u %*u %*u %llu",
&st_net_dev_i->rx_bytes,
&st_net_dev_i->rx_packets,
&st_net_dev_i->rx_compressed,
&st_net_dev_i->multicast,
&st_net_dev_i->tx_bytes,
&st_net_dev_i->tx_packets,
&st_net_dev_i->tx_compressed);
dev++;
}
}
fclose(fp);
return dev;
}
void read_if_info(struct stats_net_dev *st_net_dev, int nbr)
{
FILE *fp;
struct stats_net_dev *st_net_dev_i;
char filename[128], duplex[32];
int dev, n;
for (dev = 0; dev < nbr; dev++) {
st_net_dev_i = st_net_dev + dev;
/* Read speed info */
sprintf(filename, IF_DUPLEX, st_net_dev_i->interface);----------------------------------------------從/sys/class/net/%s/duplex獲取信息。
if ((fp = fopen(filename, "r")) == NULL)
/* Cannot read NIC duplex */
continue;
n = fscanf(fp, "%31s", duplex);
fclose(fp);
if (n != 1)
/* Cannot read NIC duplex */
continue;
if (!strcmp(duplex, K_DUPLEX_FULL)) {
st_net_dev_i->duplex = C_DUPLEX_FULL;
}
else if (!strcmp(duplex, K_DUPLEX_HALF)) {
st_net_dev_i->duplex = C_DUPLEX_HALF;
}
else
continue;
/* Read speed info */
sprintf(filename, IF_SPEED, st_net_dev_i->interface);--------------------------------------------------從/sys/class/net/%s/speed獲取信息。
if ((fp = fopen(filename, "r")) == NULL)
/* Cannot read NIC speed */
continue;
n = fscanf(fp, "%u", &st_net_dev_i->speed);
fclose(fp);
if (n != 1) {
st_net_dev_i->speed = 0;
}
}
}
3.13.1 /proc/net/dev
/proc/net/dev節點在dev_proc_net_init()中創建,對應的fops是dev_seq_fops。
這里重點看一下dev_seq_show():
static int dev_seq_show(struct seq_file *seq, void *v)
{
if (v == SEQ_START_TOKEN)
seq_puts(seq, "Inter-| Receive "
" | Transmit\n"
" face |bytes packets errs drop fifo frame "
"compressed multicast|bytes packets errs "
"drop fifo colls carrier compressed\n");
else
dev_seq_printf_stats(seq, v);
return 0;
}
static void dev_seq_printf_stats(struct seq_file *seq, struct net_device *dev)
{
struct rtnl_link_stats64 temp;
const struct rtnl_link_stats64 *stats = dev_get_stats(dev, &temp);
seq_printf(seq, "%6s: %7llu %7llu %4llu %4llu %4llu %5llu %10llu %9llu "
"%8llu %7llu %4llu %4llu %4llu %5llu %7llu %10llu\n",
dev->name, stats->rx_bytes, stats->rx_packets,
stats->rx_errors,
stats->rx_dropped + stats->rx_missed_errors,
stats->rx_fifo_errors,
stats->rx_length_errors + stats->rx_over_errors +
stats->rx_crc_errors + stats->rx_frame_errors,
stats->rx_compressed, stats->multicast,
stats->tx_bytes, stats->tx_packets,
stats->tx_errors, stats->tx_dropped,
stats->tx_fifo_errors, stats->collisions,
stats->tx_carrier_errors +
stats->tx_aborted_errors +
stats->tx_window_errors +
stats->tx_heartbeat_errors,
stats->tx_compressed);
}
3.14 網絡錯誤統計信息
net_edev_act從/proc/net/dev網絡錯誤統計信息。

網絡錯誤信息數據來源和網絡信息一樣,都來自於/proc/net/dev。
關鍵結構體也是struct rtnl_link_stats64。
void read_net_edev(struct stats_net_edev *st_net_edev, int nbr)
{
FILE *fp;
struct stats_net_edev *st_net_edev_i;
static char line[256];
char iface[MAX_IFACE_LEN];
int dev = 0;
int pos;
if ((fp = fopen(NET_DEV, "r")) == NULL)
return;
while ((fgets(line, sizeof(line), fp) != NULL) && (dev < nbr)) {
pos = strcspn(line, ":");
if (pos < strlen(line)) {
st_net_edev_i = st_net_edev + dev;
strncpy(iface, line, MINIMUM(pos, MAX_IFACE_LEN - 1));
iface[MINIMUM(pos, MAX_IFACE_LEN - 1)] = '\0';
sscanf(iface, "%s", st_net_edev_i->interface); /* Skip heading spaces */
sscanf(line + pos + 1, "%*u %*u %llu %llu %llu %llu %*u %*u %*u %*u "
"%llu %llu %llu %llu %llu",
&st_net_edev_i->rx_errors,------------------對應rx_erros,表示接受到的bad packet。
&st_net_edev_i->rx_dropped,-----------------對應rx_dropped+rx_missed_errors。
&st_net_edev_i->rx_fifo_errors,-------------對應rx_fifo_errors。
&st_net_edev_i->rx_frame_errors,------------對應rx_length_errors+rx_over_errros。
&st_net_edev_i->tx_errors,
&st_net_edev_i->tx_dropped,
&st_net_edev_i->tx_fifo_errors,
&st_net_edev_i->collisions,-----------------對應collisions
&st_net_edev_i->tx_carrier_errors);---------包括tx_carrier_errors+tx_aborted_errors+tx_window_errors+tx_heartbeat_errors。
dev++;
}
}
fclose(fp);
}
獲取網絡設備統計信息的核心數據結構式struct rtnl_link_stat64。
struct rtnl_link_stats64 { __u64 rx_packets; /* total packets received */ __u64 tx_packets; /* total packets transmitted */ __u64 rx_bytes; /* total bytes received */ __u64 tx_bytes; /* total bytes transmitted */ __u64 rx_errors; /* bad packets received */ __u64 tx_errors; /* packet transmit problems */ __u64 rx_dropped; /* no space in linux buffers */ __u64 tx_dropped; /* no space available in linux */ __u64 multicast; /* multicast packets received */ __u64 collisions; /* detailed rx_errors: */ __u64 rx_length_errors; __u64 rx_over_errors; /* receiver ring buff overflow */ __u64 rx_crc_errors; /* recved pkt with crc error */ __u64 rx_frame_errors; /* recv'd frame alignment error */ __u64 rx_fifo_errors; /* recv'r fifo overrun */ __u64 rx_missed_errors; /* receiver missed packet */ /* detailed tx_errors */ __u64 tx_aborted_errors; __u64 tx_carrier_errors; __u64 tx_fifo_errors; __u64 tx_heartbeat_errors; __u64 tx_window_errors; /* for cslip etc */ __u64 rx_compressed; __u64 tx_compressed; __u64 rx_nohandler; /* dropped, no handler found */ };
3.15 NFS客戶端統計信息
net_nfs_act讀取/proc/net/rpc/nfs來分析作為nfs客戶端的統計信息。
void read_net_nfs(struct stats_net_nfs *st_net_nfs)
{
FILE *fp;
char line[256];
unsigned int getattcnt = 0, accesscnt = 0, readcnt = 0, writecnt = 0;
if ((fp = fopen(NET_RPC_NFS, "r")) == NULL)
return;
memset(st_net_nfs, 0, STATS_NET_NFS_SIZE);
while (fgets(line, sizeof(line), fp) != NULL) {
if (!strncmp(line, "rpc ", 4)) {
sscanf(line + 4, "%u %u",
&st_net_nfs->nfs_rpccnt, &st_net_nfs->nfs_rpcretrans);
}
else if (!strncmp(line, "proc3 ", 6)) {
sscanf(line + 6, "%*u %*u %u %*u %*u %u %*u %u %u",
&getattcnt, &accesscnt, &readcnt, &writecnt);
st_net_nfs->nfs_getattcnt += getattcnt;
st_net_nfs->nfs_accesscnt += accesscnt;
st_net_nfs->nfs_readcnt += readcnt;
st_net_nfs->nfs_writecnt += writecnt;
}
else if (!strncmp(line, "proc4 ", 6)) {
sscanf(line + 6, "%*u %*u %u %u "
"%*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %u %u",
&readcnt, &writecnt, &accesscnt, &getattcnt);
st_net_nfs->nfs_getattcnt += getattcnt;
st_net_nfs->nfs_accesscnt += accesscnt;
st_net_nfs->nfs_readcnt += readcnt;
st_net_nfs->nfs_writecnt += writecnt;
}
}
fclose(fp);
}
3.16 NFS服務端統計信息
net_nfsd_act
#define NET_RPC_NFSD "/proc/net/rpc/nfsd"
void read_net_nfsd(struct stats_net_nfsd *st_net_nfsd)
{
FILE *fp;
char line[256];
unsigned int getattcnt = 0, accesscnt = 0, readcnt = 0, writecnt = 0;
if ((fp = fopen(NET_RPC_NFSD, "r")) == NULL)
return;
memset(st_net_nfsd, 0, STATS_NET_NFSD_SIZE);
while (fgets(line, sizeof(line), fp) != NULL) {
if (!strncmp(line, "rc ", 3)) {
sscanf(line + 3, "%u %u",
&st_net_nfsd->nfsd_rchits, &st_net_nfsd->nfsd_rcmisses);
}
else if (!strncmp(line, "net ", 4)) {
sscanf(line + 4, "%u %u %u",
&st_net_nfsd->nfsd_netcnt, &st_net_nfsd->nfsd_netudpcnt,
&st_net_nfsd->nfsd_nettcpcnt);
}
else if (!strncmp(line, "rpc ", 4)) {
sscanf(line + 4, "%u %u",
&st_net_nfsd->nfsd_rpccnt, &st_net_nfsd->nfsd_rpcbad);
}
else if (!strncmp(line, "proc3 ", 6)) {
sscanf(line + 6, "%*u %*u %u %*u %*u %u %*u %u %u",
&getattcnt, &accesscnt, &readcnt, &writecnt);
st_net_nfsd->nfsd_getattcnt += getattcnt;
st_net_nfsd->nfsd_accesscnt += accesscnt;
st_net_nfsd->nfsd_readcnt += readcnt;
st_net_nfsd->nfsd_writecnt += writecnt;
}
else if (!strncmp(line, "proc4ops ", 9)) {
sscanf(line + 9, "%*u %*u %*u %*u %u "
"%*u %*u %*u %*u %*u %u "
"%*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %u "
"%*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %u",
&accesscnt, &getattcnt, &readcnt, &writecnt);
st_net_nfsd->nfsd_getattcnt += getattcnt;
st_net_nfsd->nfsd_accesscnt += accesscnt;
st_net_nfsd->nfsd_readcnt += readcnt;
st_net_nfsd->nfsd_writecnt += writecnt;
}
}
fclose(fp);
}
3.17 網絡socket統計信息
net_sock_act讀取/proc/net/sockstat統計TCP和UDP信息。

/proc/net/sockstat的數據來源於sockstat_seq_show(),這里面統計了當前系統socket的使用情況。
static int sockstat_seq_show(struct seq_file *seq, void *v) { struct net *net = seq->private; unsigned int frag_mem; int orphans, sockets; local_bh_disable(); orphans = percpu_counter_sum_positive(&tcp_orphan_count); sockets = proto_sockets_allocated_sum_positive(&tcp_prot); local_bh_enable(); socket_seq_show(seq);---------------------------------------------------------從sockets_ in_use中獲取每個CPU在使用中的socket數目。顯示內容sockets: used xxx。tw表示TimeWait的socket數目。 seq_printf(seq, "TCP: inuse %d orphan %d tw %d alloc %d mem %ld\n", sock_prot_inuse_get(net, &tcp_prot), orphans, atomic_read(&tcp_death_row.tw_count), sockets, proto_memory_allocated(&tcp_prot));------------------------------------第一個參數表示TCP協議使用的socket數目。 seq_printf(seq, "UDP: inuse %d mem %ld\n", sock_prot_inuse_get(net, &udp_prot), proto_memory_allocated(&udp_prot));------------------------------------第一個參數表示UDP協議使用的socket數目。 seq_printf(seq, "UDPLITE: inuse %d\n", sock_prot_inuse_get(net, &udplite_prot)); seq_printf(seq, "RAW: inuse %d\n", sock_prot_inuse_get(net, &raw_prot));----------------------------------RAW類型socket使用數目。 frag_mem = ip_frag_mem(net); seq_printf(seq, "FRAG: inuse %u memory %u\n", !!frag_mem, frag_mem); return 0; }
Sockets圖表中的數據totsck、tcpsck、udpsck、rawsck、tcp-tw、ip-frag分別來源於read_net_sock()函數解析。
可見totsck表示系統所有Socket數目,其他子類表示不同類型socket數目。
void read_net_sock(struct stats_net_sock *st_net_sock) { FILE *fp; char line[96]; char *p; if ((fp = fopen(NET_SOCKSTAT, "r")) == NULL) return; while (fgets(line, sizeof(line), fp) != NULL) { if (!strncmp(line, "sockets:", 8)) { /* Sockets */ sscanf(line + 14, "%u", &st_net_sock->sock_inuse); } else if (!strncmp(line, "TCP:", 4)) { /* TCP sockets */ sscanf(line + 11, "%u", &st_net_sock->tcp_inuse); if ((p = strstr(line, "tw")) != NULL) { sscanf(p + 2, "%u", &st_net_sock->tcp_tw); } } else if (!strncmp(line, "UDP:", 4)) { /* UDP sockets */ sscanf(line + 11, "%u", &st_net_sock->udp_inuse); } else if (!strncmp(line, "RAW:", 4)) { /* RAW sockets */ sscanf(line + 11, "%u", &st_net_sock->raw_inuse); } else if (!strncmp(line, "FRAG:", 5)) { /* FRAGments */ sscanf(line + 12, "%u", &st_net_sock->frag_inuse); } } fclose(fp); }
3.18 huge頁面統計信息
huge_act通過讀取/proc/meminfo獲取huge頁面統計信息。
void read_meminfo_huge(struct stats_huge *st_huge)
{
FILE *fp;
char line[128];
unsigned long szhkb = 0;
if ((fp = fopen(MEMINFO, "r")) == NULL)
return;
while (fgets(line, sizeof(line), fp) != NULL) {
if (!strncmp(line, "HugePages_Total:", 16)) {
/* Read the total number of huge pages */
sscanf(line + 16, "%lu", &st_huge->tlhkb);
}
else if (!strncmp(line, "HugePages_Free:", 15)) {
/* Read the number of free huge pages */
sscanf(line + 15, "%lu", &st_huge->frhkb);
}
else if (!strncmp(line, "Hugepagesize:", 13)) {
/* Read the default size of a huge page in kB */
sscanf(line + 13, "%lu", &szhkb);
}
}
fclose(fp);
/* We want huge pages stats in kB and not expressed in a number of pages */
st_huge->tlhkb *= szhkb;
st_huge->frhkb *= szhkb;
}
3.19 softnet統計信息
softnet_act通過/proc/net/softnet_stat獲取統計信息,
void read_softnet(struct stats_softnet *st_softnet, int nbr)
{
FILE *fp;
struct stats_softnet *st_softnet_i;
char line[1024];
unsigned int proc_nb = 1;
/* Open /proc/net/softnet_stat file */
if ((fp = fopen(NET_SOFTNET, "r")) == NULL)
return;
/*
* Init a structure that will contain the values for CPU "all".
* CPU "all" doesn't exist in /proc/net/softnet_stat file, so
* we compute its values as the sum of the values of each CPU.
*/
memset(st_softnet, 0, sizeof(struct stats_softnet));
while ((fgets(line, sizeof(line), fp) != NULL) && (proc_nb < nbr)) {
st_softnet_i = st_softnet + proc_nb++;
sscanf(line, "%x %x %x %*x %*x %*x %*x %*x %*x %x %x",
&st_softnet_i->processed,
&st_softnet_i->dropped,
&st_softnet_i->time_squeeze,
&st_softnet_i->received_rps,
&st_softnet_i->flow_limit);
st_softnet->processed += st_softnet_i->processed;
st_softnet->dropped += st_softnet_i->dropped;
st_softnet->time_squeeze += st_softnet_i->time_squeeze;
st_softnet->received_rps += st_softnet_i->received_rps;
st_softnet->flow_limit += st_softnet_i->flow_limit;
}
fclose(fp);
}
/proc/net/softnet_stat數據來源於softnet_seq_show()。
static int softnet_seq_show(struct seq_file *seq, void *v) { struct softnet_data *sd = v; unsigned int flow_limit_count = 0; #ifdef CONFIG_NET_FLOW_LIMIT struct sd_flow_limit *fl; rcu_read_lock(); fl = rcu_dereference(sd->flow_limit); if (fl) flow_limit_count = fl->count; rcu_read_unlock(); #endif seq_printf(seq, "%08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n", sd->processed, sd->dropped, sd->time_squeeze, 0, 0, 0, 0, 0, /* was fastroute */ 0, /* was cpu_collision */ sd->received_rps, flow_limit_count); return 0; }

