linux進程pid分配規則,Linux進程pid分配法【轉】

一. 概述

Android系統創建進程,最終的實現還是調用linux fork方法,對于linux系統每個進程都有唯一的 進程ID(值大于0),也有pid上限,默認為32768。 pid可重復利用,當進程被殺后會回收該pid,以供后續的進程pid分配。

上一篇文章Linux進程管理?詳細地介紹了進程fork過程,在copy_process()過程,執行完父進行文件、內存等信息的拷貝,緊接著便是執行alloc_pid()方法去分配pid.

二. 分配法

2.1 copy_process

static?struct?task_struct *copy_process(unsigned?long?clone_flags,?unsigned?long?stack_start,?unsigned?long?stack_size,?int?__user *child_tidptr,?struct?pid *pid,?int?trace,?unsigned?long?tls)?{

...

struct task_struct *p;

if (pid != &init_struct_pid) {

//分配pid[見小節2.2]

pid = alloc_pid(p->nsproxy->pid_ns_for_children);

}

p->pid = pid_nr(pid); //設置pid[見小節2.4]

...

}

2.2 alloc_pid

[-> kernel/kernel/pid.c]

struct pid *alloc_pid(struct pid_namespace *ns)

{

struct pid *pid; //[見小節2.2.1]

enum pid_type type;

int i, nr;

struct pid_namespace *tmp; //[見小節2.2.4]

struct upid *upid;

int retval = -ENOMEM;

//分配pid結構體的內存

pid = kmem_cache_alloc(ns->pid_cachep, GFP_KERNEL);

...

tmp = ns;

pid->level = ns->level;

for (i = ns->level; i >= 0; i--) {

nr = alloc_pidmap(tmp); //分配pid【見小節2.3】

...

pid->numbers[i].nr = nr; //nr保存到pid結構體

pid->numbers[i].ns = tmp;

tmp = tmp->parent;

}

...

get_pid_ns(ns);

atomic_set(&pid->count, 1);

for (type = 0; type < PIDTYPE_MAX; ++type)

INIT_HLIST_HEAD(&pid->tasks[type]); //初始化pid的hlist結構體

upid = pid->numbers + ns->level;

spin_lock_irq(&pidmap_lock);

if (!(ns->nr_hashed & PIDNS_HASH_ADDING))

goto out_unlock;

for ( ; upid >= pid->numbers; --upid) {

//建立pid_hash的關聯關系

hlist_add_head_rcu(&upid->pid_chain,

&pid_hash[pid_hashfn(upid->nr, upid->ns)]);

upid->ns->nr_hashed++;

}

spin_unlock_irq(&pidmap_lock);

return pid;

...

}

2.2.1 pid結構體

[-> kernel/include/linux/pid.h]

struct pid

{

atomic_t count;

unsigned int level;

struct hlist_head tasks[PIDTYPE_MAX]; //見enum pid_type

struct rcu_head rcu;

struct upid numbers[1]; //見結構體upid

};

2.2.2 upid結構體

[-> pid.h]

struct upid

{

int nr;

struct pid_namespace *ns;

struct hlist_node pid_chain;

};

2.2.3 pid_type

[-> pid.h]

enum pid_type

{

PIDTYPE_PID, //進程ID

PIDTYPE_PGID, //進程組ID

PIDTYPE_SID, //會話組ID

PIDTYPE_MAX,

__PIDTYPE_TGID //僅用于__task_pid_nr_ns()

};

2.2.4 pid_namespace結構體

[-> kernel/include/linux/pid_namespace.h]

struct pid_namespace {

struct kref kref;

struct pidmap pidmap[PIDMAP_ENTRIES];

struct rcu_head rcu;

int last_pid;

unsigned int nr_hashed;

struct task_struct *child_reaper;

struct kmem_cache *pid_cachep;

unsigned int level;

struct pid_namespace *parent;

...

struct user_namespace *user_ns;

struct work_struct proc_work;

kgid_t pid_gid;

int hide_pid;

int reboot;

struct ns_common ns;

};

PID命名空間,這是為系統提供虛擬化做支撐的功能。

2.3 alloc_pidmap

[-> kernel/kernel/pid.c]

static int alloc_pidmap(struct pid_namespace *pid_ns)

{

//last_pid為上次分配出去的pid

int i, offset, max_scan, pid, last = pid_ns->last_pid;

struct pidmap *map;

pid = last + 1;

if (pid >= pid_max)

pid = RESERVED_PIDS; //默認為300

offset = pid & BITS_PER_PAGE_MASK; //最高位值置0,其余位不變

map = &pid_ns->pidmap[pid/BITS_PER_PAGE]; //找到目標pidmap

//當offset =0,則掃描一次;

//當offset!=0,則掃描兩次

max_scan = DIV_ROUND_UP(pid_max, BITS_PER_PAGE) - !offset;

for (i = 0; i <= max_scan; ++i) {

if (unlikely(!map->page)) {

void *page = kzalloc(PAGE_SIZE, GFP_KERNEL);

spin_lock_irq(&pidmap_lock);

if (!map->page) {

map->page = page;

page = NULL;

}

spin_unlock_irq(&pidmap_lock);

kfree(page);

if (unlikely(!map->page))

break;

}

//當pidmap還有可用pid時

if (likely(atomic_read(&map->nr_free))) {

do {

//當offset位空閑時返回該pid

if (!test_and_set_bit(offset, map->page)) {

atomic_dec(&map->nr_free); //可用pid減一

set_last_pid(pid_ns, last, pid); //設置last_pid

return pid;

}

//否則,查詢下一個非0的offset值

offset = find_next_offset(map, offset);

根據offset轉換成相應的pid

pid = mk_pid(pid_ns, map, offset);

} while (offset < BITS_PER_PAGE && pid < pid_max);

}

//當上述pid分配失敗,則再次查找offset

if (map < &pid_ns->pidmap[(pid_max-1)/BITS_PER_PAGE]) {

++map;

offset = 0;

} else {

map = &pid_ns->pidmap[0];

offset = RESERVED_PIDS;

if (unlikely(last == offset))

break;

}

pid = mk_pid(pid_ns, map, offset);

}

return -1;

}

pid允許分配的最大值為32767,當pid分配輪過一圈之后則允許分配的最小值為300,也就是說前300個pid是不可再分配的。

相關常量如下:

#define?PAGE_SHIFT 12

#define?PAGE_SIZE (1UL << PAGE_SHIFT)?// 2^12

#define?BITS_PER_PAGE (PAGE_SIZE * 8)?// 2^15

#define?BITS_PER_PAGE_MASK (BITS_PER_PAGE-1)?//2^15-1

#define?PAGE_MASK (~(PAGE_SIZE-1))

2.3.1 pidmap結構體

[-> kernel/include/linux/pid_namespace.h]

struct pidmap {

atomic_t nr_free; //可用pid的個數

void *page; //用于存放位圖

};

pidmap->page的大小為4KB,每一個bit位代表一個進程pid的分配情況,那么4KB*8=32768, 這正好是pid可分配的上限,用nr_free代表該namespace下還有多少可用pid。

2.3.2 find_next_offset

[-> pid.c]

#define?find_next_offset(map, off) \ find_next_zero_bit((map)->page, BITS_PER_PAGE, off)

static?inline?int?mk_pid(struct?pid_namespace *pid_ns,?struct?pidmap *map,?int?off)?{

return (map - pid_ns->pidmap)*BITS_PER_PAGE + off;

}

2.4 pid_nr

[-> kernel/include/linux/pid.h]

static inline pid_t pid_nr(struct pid *pid)

{

pid_t nr = 0;

if (pid)

nr = pid->numbers[0].nr;

return nr;

}

根據pid結構體找到真正的pid數值。

三. 總結

pid分配上限的查詢方式cat /proc/sys/kernel/pid_max,Android系統一般默認為32768。

對于pid<300的情況值允許分配一次,不可再改變。也就是進程pid分配范圍為(300, 32768);

每個pid分配成功,便會把當前的pid設置到last_pid, 那么下次pid的分配便是從last_pid+1開始 往下查找。這就意味著當last_pid+1或者附近的進程,剛被殺并回收該pid,此時再創建新進程,很有可能會復用 pid.

位圖法記錄已分配和未分配pid,由于pid的最大上限為32768,故pidmap采用4KB大小的內存,每一位代表一個進程ID號,正好4K*8=32K= 32768。

標簽:struct,offset,pid,pidmap,Linux,進程,nr,ns

來源: https://www.cnblogs.com/sky-heaven/p/13676322.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/533838.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/533838.shtml
英文地址,請注明出處:http://en.pswp.cn/news/533838.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

sqlserver date類型和字符串比較_基于SQL Server數據庫搭建主從復制實現讀寫分離實戰演練...

一、課程介紹讀寫分離(主從同步)從字面意思就可以理解&#xff0c;就是把對數據庫的讀操作和寫操作分離開。讀寫分離在網站發展初期可以一定程度上緩解讀寫并發時產生鎖的問題&#xff0c;將讀寫壓力分擔到多臺服務器上。讀寫分離的基本原理是讓主數據庫處理事務性增、改、刪操…

linux非標準頭文件,Linux學習:unix的標準化的實現(Linux中各種限制-數據類型-各種標準化頭文件介紹)...

作為Linux的前身&#xff0c;unix標準化是十分重要的。我在這里挑幾個重要的點說明。1&#xff1a;Linux中各種限制。Linux中限制有編譯時限制和運行時限制&#xff0c;另外有一些限制是由于我們的實現不同而不同&#xff0c;因此我們需要調用對應的函數獲取對應的值不同。(eg&…

python怎么導入大小字母_python遍歷小寫英文字母的方法

python遍歷小寫英文字母的方法在c、c等語言中&#xff0c;可以用字符1的for循環來遍歷小寫的26個英文字母&#xff0c;但是由于python語言的特殊性&#xff0c;通過a 1這種代碼并不能成功遍歷&#xff0c;以下是在python中遍歷英文字母的簡潔代碼&#xff1a;import stringfor…

51單片機怎么顯示當前時間_(進階篇)51單片機之按鍵控制蜂鳴器、數碼管、按鍵值移位顯示...

一、實操演示- 按鍵控制蜂鳴器1、圖文詳細獨立按鍵硬件電路蜂鳴器硬件電路2、連接方式&#xff1a;J20的第3號引腳連接到J7引腳&#xff0c;即P15連接J7。J29的第7、8號引腳連接到JP1的第1、2號引腳&#xff0c;即P31連接k1&#xff0c;P30連接k2。下載程序后&#xff0c;觀察現…

linux怎么運行g77,Linux安裝g77編譯器的技巧

在Ubuntu10.10系統中&#xff0c;g77已經被gfortran完全替代了&#xff0c;但并不能完全兼容過去的g77&#xff0c;這樣就不能使用一些用977編譯的程序了。所以我們只能自己再安裝g77了。今天華軍小編給大家展示的是Linux安裝g77編譯器的技巧&#xff0c;精心挑選的內容希望大家…

oracle 結果集已耗盡_java.sql.SQLException: 結果集已耗盡

編寫了jsp誰能幫忙看下有什么問題編寫了jsp 誰能幫忙看下 有什么問題技術交流ResultSet rsnull;String str"select publish_id,publish_name,publish_time,publish_text from comment_tb order by publish_time desc ";query.connect();rsquery.select(str);while(rs…

vs使用未初始化的內存怎么解決_遇到C語言內存錯誤怎么辦?一定要找準這六個原因...

一、沒有為指針分配內存定義了指針變量&#xff0c;但是沒有為指針分配內存&#xff0c;即指針沒有指向一塊合法的內存。淺顯的例子就不舉了&#xff0c;這里舉幾個比較隱蔽的例子。1、結構體成員指針未初始化struct student { char *name; int score; }stu,*pstu; int main() …

linux服務器如何設置雙網卡,linux服務器設置(雙網卡共享上網)

一、網絡拓補結構&#xff1a;服務器&#xff1a;兩網卡的設置&#xff1a;eth0:202.96.168.100 掩碼&#xff1a;255.255.255.0 網關&#xff1a;202.96.168.68 #與 Inte.Net 相聯eth1:192.168.1.1掩碼&#xff1a;255.255.255.0#與局域網相聯客戶機子網段&#xff1a;192.1…

pwn環境搭建_pwndbg、pwntools環境搭建(Unix系統)

目錄[TOC]pwndbg環境搭建項目地址https://github.com/pwndbg/pwndbg搭建過程1、安裝環境基礎gitpythonpython-pip2、安裝過程使用git命令克隆遠程項目到本地。git clone https://github.com/pwndbg/pwndbg進入項目根目錄并執行一鍵安裝腳本cd pwndbg && ./setup.sh該腳…

cad求和插件_黑科技 | 無BIM建模下平面CAD自動生成門窗表

如果你接到的施工圖既不是用天正出的&#xff0c;也不是用revit出的&#xff0c;還得統計門窗表&#xff0c;那么你需要讀完這篇文章。為了能夠讓自己和所有底層同行們從這項無腦又燒腦的機械勞動中解脫&#xff0c;C君近期利用茶余飯后的時間開發了一個小插件&#xff0c;可以…

linux數據庫實例開機啟動,linux下數據庫實例開機自啟動設置

linux下數據庫實例開機自啟動設置 1、修改/oratab [rootorg54 ~]# vi/etc/oratab --把N改為Y&#xff0c;如下提示 # This file is used by ORACLEutilities. It is created by root.sh # and updated by the Database ConfigurationAssistant when creating # a datablinux下數…

panic 蘋果aop_Go Web開發之Revel - 攔截器

一個攔截器是一個框架在調用action方法前或后調用的函數. 它允許一種AOP的形式, 它經常被用于做下面幾種事情:Request loggingError handlingStats keeping在Revel里, 一個攔截器能接受兩種形式:1. 函數攔截器: 一個函數滿足沒有訪問特定的應用程序Controller被調用在應用程序中…

make找不到linux內核函數,linux內核make menuconfig出錯

今天實驗剛從服務器上遷移過來的維護的linux 9260的內核&#xff0c;使用make menuconfig時出錯&#xff0c;報錯為&#xff1a;yongtaoyongtao-desktop:~/public/linux_release/linux-2.6.24$ make menuconfigHOSTCC scripts/kconfig/conf.oHOSTCC scripts/kconfig/kxgettex…

tensorboard ckpt pb 模型的輸出節點_算法工程化系列——模型固化

摘要基于tensorflow訓練的模型一般被保存為ckpt形式的文件&#xff0c;隨著當前深度學習模型網絡越來越大&#xff0c;對應模型也會非常大。當對外提供服務的時候&#xff0c;如果采用ckpt的形式&#xff0c;服務進程被調起來非常困難&#xff0c;且推理服務一般速度也較慢(會達…

深度linux內核升級,深度操作系統 2020.11.11 更新發布:內核升級

原標題&#xff1a;深度操作系統 2020.11.11 更新發布&#xff1a;內核升級IT之家11月11日消息 今日&#xff0c;深度操作系統宣布2020.11.11 更新現已發布。本次更新包括升級內核、Debian 10.6 倉庫以及系統安全性更新。系統安全方面&#xff0c;本次更新修復了 Firefox-ESR 安…

unity 使用mysql實現登錄注冊_用mysql實現登錄注冊功能

1、創建用戶表表結構如下idunameupwdisdelete注意&#xff1a;需要對密碼進行加密。如果使用md5加密&#xff0c;則密碼包含32個字符。如果使用sha1加密&#xff0c;則密碼包含40個字符&#xff0c;這里使用這種方式。md5加密方式&#xff1a;import hashlibpwd 123456my_md5 …

python爬電影_使用Python多線程爬蟲爬取電影天堂資源

最近花些時間學習了一下Python&#xff0c;并寫了一個多線程的爬蟲程序來獲取電影天堂上資源的迅雷下載地址&#xff0c;代碼已經上傳到GitHub上了&#xff0c;需要的同學可以自行下載。剛開始學習python希望可以獲得寶貴的意見。 先來簡單介紹一下&#xff0c;網絡爬蟲的基本實…

打不開磁盤配額linux,九度OJ 1455 珍惜現在,感恩生活 -- 動態規劃(背包問題)...

題目描述&#xff1a;為了挽救災區同胞的生命&#xff0c;心系災區同胞的你準備自己采購一些糧食支援災區&#xff0c;現在假設你一共有資金n元&#xff0c;而市場有m種大米&#xff0c;每種大米都是袋裝產品&#xff0c;其價格不等&#xff0c;并且只能整袋購買。請問&#xf…

erp 維護費 要交嗎_ERP系統維護費

今年8月&#xff0c;SAP中國公司宣布2009年1月1日前將由傳統支持服務轉向企業級支持服務(SAP Enterprise Support)。同時將開始實施漸進式定價方案&#xff0c;并預計在2012年之前&#xff0c;逐漸將所有客戶從現行的SAP Standard/Premium Support的定價協議過渡為SAP Enterpri…

sentinel 端口_Sentinel原理:控制臺是如何獲取到實時數據的

Sentinel 系列教程&#xff0c;現已上傳到 github 和 gitee 中&#xff1a;GitHub&#xff1a;https://github.com/all4you/sentinel-tutorialGitee&#xff1a;https://gitee.com/all_4_you/sentinel-tutorialSentinel 能夠被大家所認可&#xff0c;除了他自身的輕量級&#x…