天天看點

linux的程序樹

linux的樹形程序結構為管理程序提供了很大的友善,不管是核心對程序的管理還是使用者對程序的管理都受惠不少,對于審計也是很有用的,不管運作的什麼程序,都會有一條不會斷的線索将之綁在系統内部。整個系統的情況就是首先核心啟動,接着核心線程0派生出init,然後init派生出萬物,有點像上帝造人...不過如果你是撒旦,那麼你可以将程序脫離出這個線索,隻和排程的相關連結清單或者樹聯系,這樣你的程序就成了完全的不受控體,同時也成了真正的孤兒,再沒有人收養的孤兒。

     backtrace或者gdb coredump可以得到函數調用堆棧,馮氏機器上,堆棧都是線性堆積的,而程序之間卻可以分時運作(并發機器和馮諾依曼機器是不同的兩種類型,目前的OS實作都是在馮氏機器上模拟并發的,這就是分時),是以如果将硬體機器看作一個平台的話,所有的程序則組成了一棵樹,類似backtrace,可以借助作業系統的支援得到目前程序的調用程序,也就是父程序,在linux上,利用proc檔案系統很友善的實作。以下的代碼可以得到一個shell腳本的調用者的程序pid和程序名字,同樣的代碼可以在c中實作,隻是将$$換成getpid即可

pid=$$

name=`cat /proc/$pid/stat |awk '{print $2}'`

ppid=`cat /proc/$pid/stat |awk  '{print $4}'`

pname=`cat /proc/$ppid/stat |awk '{print $2}'`

#至此name表示目前shell的名稱,而pname則是調用者的名稱,pid為目前shell的pid,ppid為其調用者的pid(注意,若使用這幾行代碼,shell腳本的第一行最好是嚴格的#!/bin/bash,否則腳本将由bash逐行執行,父程序的名稱也就一直都是bash)。如何能像pstree指令那樣列出系統所有程序的關系,strace pstree之後發現,pstree其實就是讀取的/proc檔案系統的資訊,然後自己将之組織在了一起,linux核心在/proc/pid/stat檔案中導出了不少有用的資訊,其中就包含有父程序的資訊,參見關于/proc/pid/stat的核心函數(位于fs/proc/array.c): 

static int do_task_stat(struct task_struct *task, char * buffer, int whole)

{

    ...

    ppid = pid_alive(task) ? task->group_leader->real_parent->tgid : 0;

    res = sprintf(buffer,"%d (%s) %c %d %d %d %d %d %lu %lu /

%lu %lu %lu %lu %lu %ld %ld %ld %ld %d 0 %llu %lu %ld %lu %lu %lu %lu %lu /

%lu %lu %lu %lu %lu %lu %lu %lu %d %d %lu %lu %llu/n",

        task->pid,   //導出自己的pid

        tcomm,         //導出程序名字

        state,         //導出程序狀态

        ppid,         //導出父程序pid

        ...

        );

}

是以我們隻需要查找/proc/pid/stat的第四個字段即可,也就有了上面的腳本。接下來看一下pstree的原理,既然能找到任意程序父程序并且linux下的所有程序都是同根的,那麼隻需要将所有程序連接配接起來即可,我們有等價的兩種方式,這些方式中都毫不吝啬的使用了大數組,絲毫不在乎空間複雜度,旨在揭示原理而不考慮别的:

方式1:int proc[32768][32768]; //橫向為程序pid,縱向為程序的父程序的pid

方式2:int proc[32768]; //數組元素的索引是程序的pid,其值是父程序的pid

int main(int argc, char **argv)

        FILE *fp, *fpp;

        FILE *fp = popen ("ls /proc/ |egrep '^[0-9]+$'", "r");

        fread (buf, 1024, 1, fp);

        foreach (i, buf) {

                fpp = popen("cat /proc/i/stat|awk '{print $4}'", "r");

                read (ppid);

                方式1:proc[i][ppid] = 1;

                方式2:proc[i] = ppid;

        }

        return 0;

如此,所有的程序就聯系了起來。最後看一下導出程序狀态的位圖也是不錯的:

static const char *task_state_array[] = {  

    "R (running)",        /*  0 */

    "S (sleeping)",        /*  1 */

    "D (disk sleep)",    /*  2 */

    "T (stopped)",        /*  4 */

    "T (tracing stop)",    /*  8 */

    "Z (zombie)",        /* 16 */

    "X (dead)"        /* 32 */

};

//将程序狀态設計成簡單的向左移位擁有好多好處,最重要的一點是,程序狀态的轉換過程就是狀态機的轉換過程,簡單的移位可以使得程序狀态排他,轉換簡便,向左移位移位着同樣可以不影響其它的向右移位

static inline const char * get_task_state(struct task_struct *tsk)

    unsigned int state = (tsk->state & (TASK_RUNNING |   //每個程序每一時刻僅可處于一個狀态,這就是排他性

                        TASK_INTERRUPTIBLE |

                        TASK_UNINTERRUPTIBLE |

                        TASK_STOPPED |

                        TASK_TRACED)) |

            (tsk->exit_state & (EXIT_ZOMBIE |

                        EXIT_DEAD));

    const char **p = &task_state_array[0];

    while (state) {

        p++;

        state >>= 1;   //state中的1相對最右邊的位置的差決定了程序狀态時第幾個元素

    }

    return *p;

 本文轉自 dog250 51CTO部落格,原文連結:http://blog.51cto.com/dog250/1271779

繼續閱讀