在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