歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> Linux技術

微型Top源碼剖析

簡單來說就是通過讀取/proc下面的文件來進行,然後進行操作的。

感覺也就那回事 分別讀取了 /proc/stat /proc/loadavg /proc/meminfo

還有就是/proc/進程ID/stat這幾個文件 全部都是純文本操作

其中

/proc/loadavg可以讀取5、10、15分鐘內CPU的平均均衡、運行的進程數、進程總數、最後一次運行的進程ID

/proc/stat 可以獲取用戶態(不含ROOT身份)運行時間、用戶態(ROOT身份)運行時間、核心態運行時間、磁盤IO和非磁盤IO等待的時間還有什麼軟硬中斷時候運行的時間。。。據說最新版本的還有在虛擬機中系統運行的時間和虛擬機中CPU運行時間這兩個東西

/proc/meminfo 可以讀取內存上限、剩余內存、給驅動開發用的Buffer大小、交換區大小、RAM中替換出去在磁盤上數據的大小等等

/proc/進程ID/stat這個文件更變態,基本上所有進程的狀態信息都在裡面。進程ID,名字,狀態,父進程ID,線程組號,會話組號ID 還有什麼終端設備號、缺頁次數….虛擬空間大小….駐留的虛擬中間大小…還有什麼SP指針 PC指針 運行在哪個CPU上 結束時候給父進程發送什麼信號 被交換的頁數 優先級 甚至調度策略 信號位圖之類的 都完完全全在上面了.

感覺也就那回事,,,有以下想不到的或者是收獲

1.用statfs這個函數來做預處理,看文件系統有沒有裝載,沒有裝載就直接退出。

2.else 後面跟while循環 老外不是else {while{}} 而是 else while 寫在同一行看起來很漂亮

3.代碼命名

4.老外會在代碼中不自覺使用{}並且把代碼寫進去,好處一可以限制變量的生命周期和作用域(及時釋放內存)

就這麼多了….

主循環裡面執行

[code]
int main()
{
    struct statics stat;
    machine_init(&stat);//stat已經指向全局靜態數組
    struct system_info info;//
    get_system_info(&info);//讀取loadavg文件 查看最近CPU平均負載
    struct top_proc proc;

    for(;;)
    {
        printf("Used CPU:%.1f%%\n",(float)info.cpustates[0]/10);
        printf("Nice CPU:%.1f%%\n",(float)info.cpustates[1]/10);
        printf("System CPU:%.1f%%\n",(float)info.cpustates[2]/10);
        printf("Idle CPU:%.1f%%\n",(float)info.cpustates[3]/10);

        printf("total memroy:%d\n", info.memory[0]);
        printf("free memroy:%d\n", info.memory[1]);
        printf("buffers:%d\n", info.memory[2]);
        printf("cached:%d\n", info.memory[3]);
        printf("total swap:%d\n", info.memory[4]);
        printf("free swap:%d\n", info.memory[5]);

        sleep(2);
        printf("..................................\n");
        get_system_info(&info);

        read_one_proc_stat( (pid_t)7443, &proc);//獲取進程狀態

        struct top_proc *p = &proc;

        printf("%s\n",format_next_process(p));
    }
    return 0;
}
其中的三個結構體statics system_info 為:

[code]struct statics
{
    char **procstate_names;
    char **cpustate_names;
    char **memory_names;
#ifdef ORDER
    char **order_names;
#endif
};
struct system_info
{
    int    last_pid;
    double load_avg[3];
    int    p_total;
    int    p_active;     /* number of procs considered "active" */
    int    *procstates;
    int    *cpustates;
    int    *memory;
};

struct top_proc
{
    pid_t pid;
    uid_t uid;
    char name[64];
    int pri, nice;
    unsigned long size, rss;    /* in k */
    int state;
    unsigned long time;
    double pcpu, wcpu;
};
然後回到main中,看到第一個函數machine_init,其主要功能是對statics 進行初始化,讓其指向一些全局的靜態的東西。

[code]int machine_init(struct statics * statics)//進入目錄 設置好全局的變量
{
    //確定文件系統已經安裝
    {
        struct statfs sb;
        if (statfs(PROCFS, &sb) < 0 || sb.f_type != PROC_SUPER_MAGIC)//PROC_SUPER_MAGIC用於認知文件系統,宏的具體數字為0x9fa0
        {
            fprintf(stderr, " proc filesystem not mounted on " PROCFS "\n");
            return -1;
        }
    }

    /* chdir to the proc filesystem to make things easier */
    chdir(PROCFS);//進入 proc

    /* fill in the statics information */
    //指向全局的靜態數組信息
    statics->procstate_names = procstatenames;
    statics->cpustate_names = cpustatenames;
    statics->memory_names = memorynames;

    /* all done! */
    return 0;
}
get_system_info(&info);//讀取loadavg文件 查看最近CPU平均負載

[code]void get_system_info(struct system_info *info)
{
    char buffer[4096+1];
    int fd, len;
    char *p;
    int i;
/*
/proc # cat loadavg
1.0   1.00 0.93 2/19 301
lavg_1 (1.0) 1-分鐘平均負載
lavg_5 (1.00) 5-分鐘平均負載
lavg_15(0.93) 15-分鐘平均負載
nr_running (2) 在采樣時刻,運行隊列的任務的數目,與/proc/stat的procs_running表示相同意思
nr_threads (19) 在采樣時刻,系統中活躍的任務的個數(不包括運行已經結束的任務)
last_pid(301) 最大的pid值,包括輕量級進程,即線程。
*/
    /* get load averages */
    {
        fd = open("loadavg", O_RDONLY);
        len = read(fd, buffer, sizeof(buffer)-1);
        close(fd);
        buffer[len] = '\0';
        //strtod將字符串轉換成浮點數   
        info->load_avg[0] = strtod(buffer, &p);//5分鐘內的平均負載
        info->load_avg[1] = strtod(p, &p);//10分鐘內的平均負載
        info->load_avg[2] = strtod(p, &p);//15分鐘內的平均負載
        p = skip_token(p);          /* skip 是正在運行的進程數  分母是進程總數*/   
        p = skip_ws(p);             //最後一個是最近運行的進程ID號
        if (*p)
            info->last_pid = atoi(p);
        else
            info->last_pid = -1;
    }

    /* get the cpu time info */
    {
        fd = open("stat", O_RDONLY);
        len = read(fd, buffer, sizeof(buffer)-1);
        close(fd);
        buffer[len] = '\0';
        /*
        cpu  5484 160 6158 212335 16934 126 190 0 0
        cpu0 5484 160 6158 212335 16934 126 190 0 0
        intr 233270 194 357 0 2 1108 0 0 0 1 0 0 0 1303 0 0 0 2090 18850 75 266 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1228 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
        ctxt 342349
        btime 1459309158
        processes 6089
        procs_running 1
        procs_blocked 0
        softirq 338409 0 151659 572 208 19816 0 948 0 536 164670
        */
        p = skip_token(buffer);         /* "cpu" */  //跳過CPU
        cp_time[0] = strtoul(p, &p, 0);//nptr字符串根據參數base來轉換成無符號的長整型數。
        //user (5484) 從系統啟動開始累計到當前時刻,用戶態的CPU時間(單位:jiffies) ,不包含 nice值為負進程。1jiffies=0.01秒
        cp_time[1] = strtoul(p, &p, 0);
        //nice (160) 從系統啟動開始累計到當前時刻,nice值為負的進程所占用的CPU時間(單位:jiffies)
        cp_time[2] = strtoul(p, &p, 0);
        //system (6158) 從系統啟動開始累計到當前時刻,核心時間(單位:jiffies)
        cp_time[3] = strtoul(p, &p, 0);
        //idle (212335) 從系統啟動開始累計到當前時刻,除硬盤IO等待時間以外其它等待時間(單位:jiffies)

        //iowait (16934) 從系統啟動開始累計到當前時刻,硬盤IO等待時間(單位:jiffies) ,
        //irq (126) 從系統啟動開始累計到當前時刻,硬中斷時間(單位:jiffies)
        //softirq (190) 從系統啟動開始累計到當前時刻,軟中斷時間(單位:jiffies)

        /*
        stealstolen(0):       
        虛擬系統中的運行時間(since 2.6.11)
        guest(0) :
        //虛擬CPU  分配給虛擬機的CPU
        which is the time spent running a virtual  CPU  for  guest operating systems under the control of the Linux kernel(since 2.6.24)
        */
        /* convert cp_time counts to percentages */
        percentages(4, cpu_states, cp_time, cp_old, cp_diff);
    }

    /* get system wide memory usage */
    {
        char *p;

        fd = open("meminfo", O_RDONLY);
        len = read(fd, buffer, sizeof(buffer)-1);
        close(fd);
        buffer[len] = '\0';

        p = buffer;
        p = skip_token(p);//跳過
        memory_stats[0] = strtoul(p, &p, 10); /* 內存上限 */ ///10為進制數 

        p = strchr(p, '\n');//extern char *strchr(const char *s,char c);查找字符串s中首次出現字符c的位置。
        p = skip_token(p);
        memory_stats[1] = strtoul(p, &p, 10); /* 剩余內存 */

        p = strchr(p, '\n');
        p = skip_token(p);
        memory_stats[2] = strtoul(p, &p, 10);
        //用來給塊設備做的緩沖大小(只記錄文件系統的metadata以及 tracking in-flight pages,就是說 buffers是用來存儲,
        //目錄裡面有什麼內容,權限等等。)

        p = strchr(p, '\n');
        p = skip_token(p);
        memory_stats[3] = strtoul(p, &p, 10); /* cached memory */
//Cached: 用來給文件做緩沖大小(直接用來記憶我們打開的文件). 它不包括SwapCached
        for(i = 0; i< 8 ;i++) {
            p++;
            p = strchr(p, '\n');///跳過8個
        }

        p = skip_token(p);
        memory_stats[4] = strtoul(p, &p, 10); /* total swap */
        // SwapTotal: 交換空間的總和
        p = strchr(p, '\n');
        p = skip_token(p);
        memory_stats[5] = strtoul(p, &p, 10); /* free swap */
        //SwapFree: 從RAM中被替換出暫時存在磁盤上的空間大小
    }
/*
  MemTotal: 所有可用RAM大小 (即物理內存減去一些預留位和內核的二進制代碼大小)

    MemFree: LowFree與HighFree的總和

    Buffers: 用來給塊設備做的緩沖大小(只記錄文件系統的metadata以及 tracking in-flight pages,就是說 buffers是用來存儲,目錄裡面有什麼內容,權限等等。)

    Cached: 用來給文件做緩沖大小(直接用來記憶我們打開的文件). 它不包括SwapCached

    SwapCached: 已經被交換出來的內存,但仍然被存放在swapfile中。用來在需要的時候很快的被替換而不需要再次打開I/O端口。

    Active: 最近經常被使用的內存,除非非常必要否則不會被移作他用.

    Inactive: 最近不經常被使用的內存,非常用可能被用於其他途徑.

    HighTotal:
    HighFree: 高位內存是指所有在860MB以上的內存空間,該區域主要用於用戶空間的程序或者是緩存頁面。內核必須使用不同的手法使用該段內存,因此它比低位內存要慢一些。

    LowTotal:
    LowFree: 低位可以達到高位內存一樣的作用,而且它還能夠被內核用來記錄一些自己的數據結構。
                   Among many other things, it is where everything from the Slab is
                   allocated.  Bad things happen when you're out of lowmem.

    SwapTotal: 交換空間的總和

    SwapFree: 從RAM中被替換出暫時存在磁盤上的空間大小

    Dirty: 等待被寫回到磁盤的內存大小。

    Writeback: 正在被寫回到磁盤的內存大小。

    Mapped: 影射文件的大小。

    Slab: 內核數據結構緩存

    VmallocTotal: vmalloc內存大小

    VmallocUsed: 已經被使用的虛擬內存大小。

    VmallocChunk: largest contigious block of vmalloc area which is free

    CommitLimit:
    Committed_AS:

*/
    /* set arrays and strings */
    info->cpustates = cpu_states;
    info->memory = memory_stats;
}
/*
/proc/1 # cat stat

3348 (xdg-screensaver) S 1 2790 2790 0 -1 4202560 30551 330530 0 0 19 77 100 391 20 0 1 0 53358 5296128 171 4294967295 
134508544 135361360 3214570048 3214565048 10228772 0 65536 6 65536 0 0 0 17 0 0 0 0 0 0

pid=3348 進程(包括輕量級進程,即線程)號

comm= xdg-screensaver 應用程序或命令的名字

task_state=S 任務的狀態,R:runnign, S:sleeping (TASK_INTERRUPTIBLE), D:disk sleep (TASK_UNINTERRUPTIBLE), T: stopped, T:tracing stop,Z:zombie, X:dead

ppid=1 父進程ID

pgid=2790 線程組號/////////////////

sid=2790 c該任務所在的會話組ID

tty_nr=0 (pts/3) 該任務的tty終端的設備號,INT(0/256)=主設備號,(0-主設備號)=次設備號

tty_pgrp=-1 終端的進程組號,當前運行在該任務所在終端的前台任務(包括shell 應用程序)的PID。

task->flags=4202560進程標志位,查看該任務的特性///////////////////////

min_flt=30551該任務不需要從硬盤拷數據而發生的缺頁(次缺頁)的次數@@@@@@@@@@@@@@

cmin_flt=330530 累計的該任務的所有的waited-for進程曾經發生的次缺頁的次數目

maj_flt=0 該任務需要從硬盤拷數據而發生的缺頁(主缺頁)的次數

cmaj_flt=0 累計的該任務的所有的waited-for進程曾經發生的主缺頁的次數目

utime=19 該任務在用戶態運行的時間,單位為jiffies

stime=77 該任務在核心態運行的時間,單位為jiffies

cutime=100 累計的該任務的所有的waited-for進程曾經在用戶態運行的時間,單位為jiffies

cstime=391 累2計的該任務的所有的waited-for進程曾經在核心態運行的時間,單位為jiffies

priority=20 任務的動態優先級

nice=0 任務的靜態優先級

num_threads=1 該任務所在的線程組裡線程的個數

it_real_value=0 由於計時間隔導致的下一個 SIGALRM 發送進程的時延,以 jiffy 為單位.

start_time=53358 該任務啟動的時間,單位為jiffies

vsize=5296128(bytes) 該任務的虛擬地址空間大小

rss=171(page) 該任務當前駐留物理地址空間的大小

Number of pages the process has in real memory,minu 3 for administrative purpose.

這些頁可能用於代碼,數據和棧。
//4294967295
rlim=4294967295=0xFFFFFFFF(bytes) 該任務能駐留物理地址空間的最大值

start_code=134508544=0x8000  該任務在虛擬地址空間的代碼段的起始地址(由連接器決定)

end_code=135361360該任務在虛擬地址空間的代碼段的結束地址

start_stack=3214570048=0Xbeb0ff30該任務在虛擬地址空間的棧的開始地址

kstkesp=3214565048  sp(32 位堆棧指針) 的當前值, 與在進程的內核堆棧頁得到的一致.

kstkeip=10228772 =0X10FD58 指向將要執行的指令的指針, PC(32 位指令指針)的當前值.

pendingsig=0 待處理信號的位圖,記錄發送給進程的普通信號

block_sig=65536 阻塞信號的位圖

sigign=6 忽略的信號的位圖

sigcatch=65536被俘獲的信號的位圖

wchan=0  如果該進程是睡眠狀態,該值給出調度的調用點

nswap=0 被swapped的頁數

cnswap=0 所有子進程被swapped的頁數的和

exit_signal=17 該進程結束時,向父進程所發送的信號

task_cpu(task)=0 運行在哪個CPU上

task_rt_priority=0 實時進程的相對優先級別

task_policy=0 進程的調度策略,0=非實時進程,1=FIFO實時進程;2=RR實時進程 
*/
然後是一堆的打印。。。忽略掉。。。再看一個函數是讀取進程信息的函數

[code]void read_one_proc_stat(pid_t pid, struct top_proc *proc)
{
    char buffer[4096], *p;

    /* grab the proc stat info in one go */
    {
        int fd, len;

        sprintf(buffer, "%d/stat", pid);

        fd = open(buffer, O_RDONLY);//打開%d/stat
        len = read(fd, buffer, sizeof(buffer)-1);
        close(fd);

        buffer[len] = '\0';
    }

    proc->uid = proc_owner(pid);///

    /* parse out the status */

    p = buffer;
    p = strchr(p, '(')+1;           /* skip pid */
    {
        char *q = strrchr(p, ')');//跳過pid和進程名稱
        int len = q-p;
        if (len >= sizeof(proc->name))///進程名字過長 比結構體中長要限制
            len = sizeof(proc->name)-1;
        memcpy(proc->name, p, len);//拷貝進程名字
        proc->name[len] = 0;
        p = q+1;
    }
    //R:runnign, S:sleeping (TASK_INTERRUPTIBLE), D:disk sleep (TASK_UNINTERRUPTIBLE), T: stopped, T:tracing stop,Z:zombie, X:dead
    p = skip_ws(p);
    switch (*p++)
    {
    case 'R': proc->state = 1; break;//R:runnign,
    case 'S': proc->state = 2; break;//S:sleeping TASK_INTERRUPTIBLE
    case 'D': proc->state = 3; break;// D:disk sleep (TASK_UNINTERRUPTIBLE)
    case 'Z': proc->state = 4; break;//Z:zombie僵死進程
    case 'T': proc->state = 5; break;//T: stopped
    case 'W': proc->state = 6; break;//等待???Wait  NO  swap切出交換區
    }

    p = skip_token(p);              /* skip ppid父進程ID */
    p = skip_token(p);              /* skip pgrp 線程組號 */
    p = skip_token(p);              /* skip session該任務所在的會話組ID */
    p = skip_token(p);              /* skip tty該任務的tty終端的設備號 */
    p = skip_token(p);              /* skip tty pgrp 終端的進程組號,當前運行在該任務所在終端的前台任務(包括shell 應用程序)的PID */
    p = skip_token(p);              /* skip flags進程標志位 */
    p = skip_token(p);              /* skip min flt 該任務不需要從硬盤拷數據而發生的缺頁(次缺頁)的次數*/
    p = skip_token(p);              /* skip cmin flt 累計的該任務的所有的waited-for進程曾經發生的次缺頁的次數目*/
    p = skip_token(p);              /* skip maj flt 該任務需要從硬盤拷數據而發生的缺頁(主缺頁)的次數*/
    p = skip_token(p);              /* skip cmaj flt 累計的該任務的所有的waited-for進程曾經發生的主缺頁的次數目*/

    proc->time = strtoul(p, &p, 10);        /* utime在用戶態運行的時間, */
    proc->time += strtoul(p, &p, 10);       /* stime在核心態運行的時間, */

    p = skip_token(p);              /* skip cutime 累計的該任務的所有的waited-for進程曾經在用戶態運行的時間*/
    p = skip_token(p);              /* skip cstime 累計的該任務的所有的waited-for進程曾經在核心態運行的時間,單位為jiffies*/

    proc->pri = strtol(p, &p, 10);      /* priority任務的動態優先級 */
    proc->nice = strtol(p, &p, 10);     /* nice任務的靜態優先級 */
//該任務所在的線程組裡線程的個數   沒有!!!
    p = skip_token(p);              /* skip timeout該任務所在的線程組裡線程的個數 */
    p = skip_token(p);              /* skip it_real_val  由於計時間隔導致的下一個 SIGALRM 發送進程的時延,以 jiffy 為單位*/
    p = skip_token(p);              /* skip start_time  該任務啟動的時間*/

    proc->size = bytetok(strtoul(p, &p, 10));   /* vsize 該任務的虛擬地址空間大小 */
    proc->rss = pagetok(strtoul(p, &p, 10));    /* rss  該任務當前駐留物理地址空間的大小*/

#if 0
    /* for the record, here are the rest of the fields */
    p = skip_token(p);              /* skip rlim 該任務能駐留物理地址空間的最大值*/
    p = skip_token(p);              /* skip start_code 該任務在虛擬地址空間的代碼段的起始地址(由連接器決定)*/
    p = skip_token(p);              /* skip end_code該任務在虛擬地址空間的代碼段的結束地址 */
    p = skip_token(p);              /* skip start_stack 該任務在虛擬地址空間的棧的開始地址*/
    p = skip_token(p);              /* skip sp  sp(32 位堆棧指針) 的當前值, 與在進程的內核堆棧頁得到的一致.*/
    p = skip_token(p);              /* skip pc 指向將要執行的指令的指針, PC(32 位指令指針)的當前值.*/
    p = skip_token(p);              /* skip signal  待處理信號的位圖,記錄發送給進程的普通信號*/
    p = skip_token(p);              /* skip sigblocked  阻塞信號的位圖*/
    p = skip_token(p);              /* skip sigignore 忽略的信號的位圖*/
    p = skip_token(p);              /* skip sigcatch被俘獲的信號的位圖 */
    p = skip_token(p);              /* skip wchan 如果該進程是睡眠狀態,該值給出調度的調用點*/

///////////////新版本系統有的
    p = skip_token(p);              /*  nswap=0 被swapped的頁數 */
    p = skip_token(p);              /* cnswap=0 所有子進程被swapped的頁數的和 */
    p = skip_token(p);              /* exit_signal=0  該進程結束時,向父進程所發送的信號*/
    p = skip_token(p);              /* task_cpu(task)=0 運行在哪個CPU上*/
    p = skip_token(p);              /* task_rt_priority=0 實時進程的相對優先級別*/
    p = skip_token(p);              /*task_policy=0 進程的調度策略,0=非實時進程,1=FIFO實時進程;2=RR實時進程 */
#endif
}

Copyright © Linux教程網 All Rights Reserved