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.mzph.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…