歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

Linux下使用獨立PID namespace防止誤殺進程

一段錯誤的代碼

首先看一段錯誤的代碼:

#!/bin/bash

SLICE=100;
slppid=1;
pidfile=/var/run/vpnrulematch.pid

# 停止之前的sleep
kill_prev() {
        pid=$1;
        /bin/kill -0 $pid;exist=$?
        ppid=$(/bin/cat /proc/$pid/status|/usr/bin/awk -F ' ' '/PPid/{print $2}');
        if [ "$exist" == 0 ] && [ "$ppid" == $$ ]; then
                /bin/kill $pid;
        fi
}
echo $$ >$pidfile
# 循環處理睡眠
while true; do
        NOSTATE=0;
        /bin/sleep $SLICE &
        slppid=$!;
        wait
        ...
done

以上代碼的本意是在接收到信號的時候,停止先前的sleep,重新開始新的sleep。看看那個繁雜的kill_prev操作,之所以繁雜是因為做了“防誤殺”處理,只有在該進程id指示的進程存在並且是sleep,而且還是本腳本的子進程的時候才進行kill操作。初看起來這沒有任何問題,很嚴密,但是注意那個if判斷和kill操作之間的間隙,如果在那段時間sleep完成了,並且系統中有一個新的進程恰好在那時開始運行,並且占據了剛才sleep進程的PID,該進程會馬上被誤殺!即使Linux的PID分配策略是盡可能的往後遞增以防止這種現象,然而這還是受制於允許的PID的總數量,如果PID最大只能是10,那麼這就會很容易發生!

那該怎麼辦?答案就是將該腳本以及它的子進程等相關的PID和系統中其它進程的PID隔離開來,但是Linux可以做到嗎?可以做到,使用namespace即可。

關於命名空間

所謂的命名空間其實就是一個編址空間(廢話,等於沒說!!),一樣東西要想被識別必須要被編址,比如快遞員按照你的地址找到你的家,這個家庭地址就是一個編址,所有的已有的以及還未使用的潛在家庭地址組成了一個命名空間。一個命名空間一般只服務於一種動作,不同的命名空間之間是不能交互的。

一樣東西可以在不同的命名空間被命名編址,比如蓋烏斯.尤利烏斯.凱撒和Gaius Julius Caesar指的是同一個人,然而卻是處在不同命名空間中的,你在意大利找到一個人,對他說蓋烏斯.尤利烏斯.凱撒,他可能就不知道你在說什麼,這就是說,你不能垮命名空間進行尋址;如果在中國,生了一個小孩,給他取名字Gaius Julius Caesar,那麼它和蓋烏斯.尤利烏斯.凱撒並沒有任何關聯,這就是說,不同命名空間的相同名字之間是沒有任何關系的;但是如果你精通古羅馬歷史,並且同時精通中文和意大利語,那麼你馬上就能將蓋烏斯.尤利烏斯.凱撒和Gaius Julius Caesar聯系起來,並且可能會有意給自己兒子取名字為自己的偶像Gaius Julius Caesar,這就是說,在更高的層次上,可以做到跨命名空間的交互。

Linux的PID namespace結構以及實現

Linux的2.6內核引入了命名空間namespace,後來將PID也用ns實現了,這也許是為了更好的支持虛擬化吧。本質上一個進程可以屬於不同的命名空間。Linux將PID namespace組織成了一個tree,子命名空間對父命名空間是可見的,反過來,父命名空間對子命名空間則不可見,Linux對PID namespace的實現如下圖所示:

通過引入一個pid結構體和task_struct進行關聯,所有的關於PID命名空間的實現全部在這個pid結構體中:

struct pid
{
    atomic_t count;
    unsigned int level;
    /* lists of tasks that use this pid */
    struct hlist_head tasks[PIDTYPE_MAX];
    struct rcu_head rcu;
    struct upid numbers[1];
};

Copyright © Linux教程網 All Rights Reserved