標籤:源碼 進程 pid 進程id linux
在Linux/Unix系統中,每個進程都有一個非負整型表示的唯一進程ID。雖然是唯一的,但是進程的ID可以重用。當一個進程終止後,其進程ID就可以再次使用了。大多數Linux/Unix系統採用延遲重用的演算法,使得賦予建立進程ID不同於最近終止進程所使用的ID,這主要是為了防止將新進程誤認為是使用同一ID的某個已終止的先前進程。本文討論了Linux/Unix分配進程ID的方法以及源碼實現。
分配進程ID的方法
在大多數Linux/Unix系統中,產生一個進程ID方法是:從0開始依次連續分配,一直到可以分配的最大的進程ID(不同的系統,這個最大值是不一樣的,比如有些Linux系統是65536)。一旦到達最大值,重新從某個值(不同的系統,這個值也是不一樣的,比如在Mac OS X和HP-UX系統中,這個值是100)開始依次連續尋找那些還沒有被使用的ID。這裡分配進程ID的方法,存在潛在的安全問題。因為可以從系統擷取資訊或者提取處理序間通訊的內容。考慮到安全問題,部分系統可能用其他方法來分配進程ID,比如隨機分配一個進程ID。無論用什麼方法分配進程ID,系統都需要保證每個進程ID是獨一無二的。
Linux系統上分配進程ID的源碼實現
在Linux系統中,核心分配PID的範圍是(RESERVED_PIDS, PID_MAX_DEFAULT),在每個namespace中,PID是依次連續分配的(在不同的namespace的task可以有相同的ID)。一旦ID達到分配到達上限(在pseudo-file /proc/sys/kernel/pid_max中可以查看可以分配的最大進程ID),從頭開始尋找分配PID。以下是相關的原始碼:
struct pid *alloc_pid(struct pid_namespace *ns){/*省略了一些代碼*/for (i = ns->level; i >= 0; i--) { nr = alloc_pidmap(tmp); if (nr < 0)goto out_free; pid->numbers[i].nr = nr; pid->numbers[i].ns = tmp; tmp = tmp->parent;}/*省略了一些代碼*/}static int alloc_pidmap(struct pid_namespace *pid_ns){ int i, offset, max_scan, pid, last = pid_ns->last_pid; struct pidmap *map; pid = last + 1; if (pid >= pid_max) pid = RESERVED_PIDS; /* and later on... */ pid_ns->last_pid = pid; return pid;}
注意在Linux核心中,進程PID實現並不僅僅是一個int標識符號(當然返回給應用程式,PID只是int類型的數值)。相關實現的結構體在/include/linux/pid.h中可以找到。除了ID外,它還包括跟這個ID相關的task列表、引用計數器和一個可以方便尋找的hashed list。
進程ID分配需要注意的事項
1、殭屍進程的PID是暫時不能用的,需要其父進程收集器所有的終止狀態才能使用,也就是說需要調用類似wait()函數後,才能使用。
2、具體實現時,系統可以隨機分配進程PID(當然是保證沒有被其他進程使用),因此在應用程式中,不要依賴於進程PID的分配方式。
3、在使用者空間(user space)可能看到分配的進程ID並不連續,這是因為在應用程式兩個fork之間,核心發送器(scheduling)可能建立了一個進程。事實上,這種情況是經常發生的。
參考資料
《UNIX環境進階編程》(第二版)
http://superuser.com/questions/135007/how-are-pids-generated
http://stackoverflow.com/questions/3446727/how-does-linux-determine-the-next-pid
http://en.wikipedia.org/wiki/Process_identifier