linux 内核中的pid和前缀树

前言:

写这个文章的初衷是因为今天手写了一个字典树,然后写字典树以后忽然想到了之前看的技术文章,linux kernel 之前的pid 申请方式已经从 bitmap 变成了 基数树,所以打算写文章再回顾一下这种数据结构算法

一、内核中pid的申请

旧的内核中pid的申请就不多说了,是使用的是bitmap去实现的,新版的换为了基数树。申请pid的内核代码在:

struct pid *alloc_pid(struct pid_namespace *ns, pid_t *set_tid,size_t set_tid_size)
{
.......
}

继续看代码,申请函数为:

if (tid) {nr = idr_alloc(&tmp->idr, NULL, tid,tid + 1, GFP_ATOMIC);/** If ENOSPC is returned it means that the PID is* alreay in use. Return EEXIST in that case.*/if (nr == -ENOSPC)nr = -EEXIST;} else {int pid_min = 1;/** init really needs pid 1, but after reaching the* maximum wrap back to RESERVED_PIDS*/if (idr_get_cursor(&tmp->idr) > RESERVED_PIDS)pid_min = RESERVED_PIDS;/** Store a null pointer so find_pid_ns does not find* a partially initialized PID (see below).*/nr = idr_alloc_cyclic(&tmp->idr, NULL, pid_min,pid_max, GFP_ATOMIC);}

进程最大的pid号为:


int pid_max = PID_MAX_DEFAULT;/** This controls the default maximum pid allocated to a process*/
#define PID_MAX_DEFAULT (CONFIG_BASE_SMALL ? 0x1000 : 0x8000)

这里我发现了一个问题,linux 最大的pid 是 0x8000 而不是 0xFFFF,这是为了和老的内核兼容,我们可以通过

cat /proc/sys/kernel/pid_max

查询当前内核的最大pid。

申请pid 函数我们聚焦在

idr_alloc 和 idr_alloc_cyclic

再进去看 idr_alloc_cyclic 的实现

int idr_alloc_cyclic(struct idr *idr, void *ptr, int start, int end, gfp_t gfp)
{u32 id = idr->idr_next;int err, max = end > 0 ? end - 1 : INT_MAX;if ((int)id < start)id = start;err = idr_alloc_u32(idr, ptr, &id, max, gfp);if ((err == -ENOSPC) && (id > start)) {id = start;err = idr_alloc_u32(idr, ptr, &id, max, gfp);}if (err)return err;idr->idr_next = id + 1;return id;
}

具体实现pid 申请的函数在:

int idr_alloc_u32(struct idr *idr, void *ptr, u32 *nextid,unsigned long max, gfp_t gfp)
{struct radix_tree_iter iter;void __rcu **slot;unsigned int base = idr->idr_base;unsigned int id = *nextid;if (WARN_ON_ONCE(!(idr->idr_rt.xa_flags & ROOT_IS_IDR)))idr->idr_rt.xa_flags |= IDR_RT_MARKER;id = (id < base) ? 0 : id - base;radix_tree_iter_init(&iter, id);slot = idr_get_free(&idr->idr_rt, &iter, gfp, max - base);if (IS_ERR(slot))return PTR_ERR(slot);*nextid = iter.index + base;/* there is a memory barrier inside radix_tree_iter_replace() */radix_tree_iter_replace(&idr->idr_rt, &iter, slot, ptr);radix_tree_iter_tag_clear(&idr->idr_rt, &iter, IDR_FREE);return 0;
}

我们需要重点分析这个函数,重要的数据结构radix_tree_iter,我们看一下这个数据结构:

这是一个基数树的迭代器

/*** struct radix_tree_iter - radix tree iterator state** @index:	index of current slot* @next_index:	one beyond the last index for this chunk* @tags:	bit-mask for tag-iterating* @node:	node that contains current slot** This radix tree iterator works in terms of "chunks" of slots.  A chunk is a* subinterval of slots contained within one radix tree leaf node.  It is* described by a pointer to its first slot and a struct radix_tree_iter* which holds the chunk's position in the tree and its size.  For tagged* iteration radix_tree_iter also holds the slots' bit-mask for one chosen* radix tree tag.*/
struct radix_tree_iter {unsigned long	index;unsigned long	next_index;unsigned long	tags;struct radix_tree_node *node;
};

迭代器中有重要的数据结构

#define radix_tree_node		xa_node

也就是替换后 变为了

struct xa_node*

我们再继续看 xa_node* 这个数据结构,这是基数树的节点

/** @count is the count of every non-NULL element in the ->slots array* whether that is a value entry, a retry entry, a user pointer,* a sibling entry or a pointer to the next level of the tree.* @nr_values is the count of every element in ->slots which is* either a value entry or a sibling of a value entry.*/
struct xa_node {unsigned char	shift;		/* Bits remaining in each slot */unsigned char	offset;		/* Slot offset in parent */unsigned char	count;		/* Total entry count */unsigned char	nr_values;	/* Value entry count */struct xa_node __rcu *parent;	/* NULL at top of tree */struct xarray	*array;		/* The array we belong to */union {struct list_head private_list;	/* For tree user */struct rcu_head	rcu_head;	/* Used when freeing node */};void __rcu	*slots[XA_CHUNK_SIZE];union {unsigned long	tags[XA_MAX_MARKS][XA_MARK_LONGS];unsigned long	marks[XA_MAX_MARKS][XA_MARK_LONGS];};
};

这些宏:

#ifndef XA_CHUNK_SHIFT
#define XA_CHUNK_SHIFT		(CONFIG_BASE_SMALL ? 4 : 6)
#endif
#define XA_CHUNK_SIZE		(1UL << XA_CHUNK_SHIFT)

XA_CHUNK_SIZE 是 64 或者16.

#define XA_MAX_MARKS		3

对应的CHUNK是6个位或者 4个位

然后我们看到pid最大是0x8000, 4 *4 为 16 个 槽,这意味着基数树高最大为3((16 / 6 ) + 1)

#if defined __x86_64__ && !defined __ILP32__
# define __WORDSIZE	64
#else
# define __WORDSIZE	32
#define __WORDSIZE32_SIZE_ULONG		0
#define __WORDSIZE32_PTRDIFF_LONG	0
#endif#ifndef BITS_PER_LONG
# define BITS_PER_LONG __WORDSIZE
#endif

BITS_PER_LONG 为64 或者32

#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))/** The IDR API does not expose the tagging functionality of the radix tree* to users.  Use tag 0 to track whether a node has free space below it.*/
#define IDR_FREE	0

带入计算就是:

(64 + 64 -1) / 64 = 1

对应的宏

#define XA_MAX_MARKS		3
#define XA_MARK_LONGS		DIV_ROUND_UP(XA_CHUNK_SIZE, BITS_PER_LONG)

初始化基数树的迭代器:

/*** radix_tree_iter_init - initialize radix tree iterator** @iter:	pointer to iterator state* @start:	iteration starting index* Returns:	NULL*/
static __always_inline void __rcu **
radix_tree_iter_init(struct radix_tree_iter *iter, unsigned long start)
{/** Leave iter->tags uninitialized. radix_tree_next_chunk() will fill it* in the case of a successful tagged chunk lookup.  If the lookup was* unsuccessful or non-tagged then nobody cares about ->tags.** Set index to zero to bypass next_index overflow protection.* See the comment in radix_tree_next_chunk() for details.*/iter->index = 0;iter->next_index = start;return NULL;
}

主要看idr_get_free,这个函数要看懂

shift -= RADIX_TREE_MAP_SHIFT;

shift 最大是16, 每次我们移动是6位,对应的是CHUNKSIZE

if (!tag_get(node, IDR_FREE, offset)) {offset = radix_tree_find_next_bit(node, IDR_FREE,offset + 1);start = next_index(start, node, offset);if (start > max || start == 0)return ERR_PTR(-ENOSPC);while (offset == RADIX_TREE_MAP_SIZE) {offset = node->offset + 1;node = node->parent;if (!node)goto grow;shift = node->shift;}child = rcu_dereference_raw(node->slots[offset]);}

每次循环如果循环到tag不是IDR_FREE,就会继续找下一个tag,直到找到是IDR_FREE的solt。

在这里退出循环

else if (!radix_tree_is_internal_node(child))break;

最后返回空闲槽

	iter->index = start;if (node)iter->next_index = 1 + min(max, (start | node_maxindex(node)));elseiter->next_index = 1;iter->node = node;set_iter_tags(iter, node, offset, IDR_FREE);return slot;

上面的start 就应该是要申请的pid,我们最后看next_index

/** The maximum index which can be stored in a radix tree*/
static inline unsigned long shift_maxindex(unsigned int shift)
{return (RADIX_TREE_MAP_SIZE << shift) - 1;
}static inline unsigned long node_maxindex(const struct radix_tree_node *node)
{return shift_maxindex(node->shift);
}static unsigned long next_index(unsigned long index,const struct radix_tree_node *node,unsigned long offset)
{return (index & ~node_maxindex(node)) + (offset << node->shift);
}

所以start 就是要申请的pid。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/106575.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【学习笔记】minIO分布式文件服务系统

MinIO 一、概述 1.1 minIO是什么&#xff1f; MinIO是专门为海量数据存储、人工智能、大数据分析而设计的对象存储系统。&#xff08;早前流行的还有FastDFS&#xff09; 据官方介绍&#xff0c;单个对象最大可存储5T&#xff0c;非常适合存储海量图片、视频、日志文件、备…

java.sql.SQLFeatureNotSupportedException解决方法

使用MyBatis访问数据库查询数据时报错&#xff1a; Caused by: java.sql.SQLFeatureNotSupportedExceptionat com.alibaba.druid.pool.DruidPooledResultSet.getObject(DruidPooledResultSet.java:1771)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun…

03在命令行环境中创建Maven版的Java工程,了解pom.xml文件的结构,了解Java工程的目录结构并编写代码,执行Maven相关的构建命令

创建Maven版的Java工程 Maven工程的坐标 数学中使用x、y、z三个向量可以在空间中唯一的定位一个点, Maven中也可以使用groupId,artifactId,version三个向量在Maven的仓库中唯一的定位到一个jar包 groupId: 公司或组织域名的倒序, 通常也会加上项目名称代表公司或组织开发的一…

JDBC操作BLOB类型字段

JDBC中Statement接口本身不能直接操作BLOB数据类型 操作BLOB数据类型需要使用PreparedStatement或者CallableStatement(存储过程) 这里演示通过PreparedStatement操作数据库BLOB字段 设置最大传入字节 一般是4M 可以通过以下命令修改 set global max_allowed_packet1024*1…

网页在线打开PDF_网站中在线查看PDF之pdf.js

一、pdf.js简介 PDF.js 是一个使用 HTML5 构建的便携式文档格式查看器。 pdf.js 是社区驱动的&#xff0c;并由 Mozilla 支持。我们的目标是为解析和呈现 PDF 创建一个通用的、基于 Web 标准的平台。 pdf.js 将 PDF 文档转换为 HTML5 Canvas 元素&#xff0c;并使用 JavaScr…

Puppeteer结合测试工具jest使用(四)

Puppeteer结合测试工具jest使用&#xff08;四&#xff09; Puppeteer结合测试工具jest使用&#xff08;四&#xff09;一、简介二、与jest结合使用&#xff0c;集成到常规测试三、支持其他的几种四、总结 一、简介 Puppeteer是一个提供自动化控制Chrome或Chromium浏览器的Node…

MyBatis(中)

目录 1、动态sql&#xff1a; 1、if标签&#xff1a; 2、where标签&#xff1a; 3、 trim标签&#xff1a; 4、set标签&#xff1a; 5、choose when otherwise&#xff1a; 6、模糊查询的写法&#xff1a; 7、foreach标签&#xff1a; &#xff08;1&#xff09;批量删除…

施耐德Unity通过Modbus控制变频器

硬件设备 PLC: Unity Premium (CPU:TSX P57154) 通讯卡: TSX SCP 114 连接电缆: TSX SCP CM 4030 VSD: ATV 58 硬件连接 Unity Premium (CPU: TSX P57154)本身不带Modbus接口&#xff0c;因此&#xff0c;采用TSX SCP 114扩展一个Modbus接口。TSX SCP 114是一个RS-485接…

【已解决】No Python at ‘D:\Python\python.exe‘

起因&#xff0c;我把我的python解释器&#xff0c;重新移了个位置&#xff0c;导致我在Pycharm中的爬虫项目启动&#xff0c;结果出现这个问题。 然后&#xff0c;从网上查到了这篇博客: 【已解决】No Python at ‘D:\Python\python.exe‘-CSDN博客 但是&#xff0c;按照上述…

8.Covector Transformation Rules

上一节已知&#xff0c;任意的协向量都可以写成对偶基向量的线性组合&#xff0c;以及如何通过计算基向量穿过的协向量线来获得协向量分量&#xff0c;且看到 协向量分量 以 与向量分量 相反的方式进行变换。 现要在数学上确认协向量变换规则是什么。 第一件事&#xff1a;…

前端小案例 | 一个带切换的登录注册界面(静态)

文章目录 &#x1f4da;HTML&#x1f4da;CSS&#x1f4da;JS &#x1f4da;HTML <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-sc…

紫光同创FPGA实现UDP协议栈网络视频传输,基于YT8511和RTL8211,提供4套PDS工程源码和技术支持

目录 1、前言免责声明 2、相关方案推荐我这里已有的以太网方案紫光同创FPGA精简版UDP方案紫光同创FPGA带ping功能UDP方案 3、设计思路框架OV7725摄像头配置及采集OV5640摄像头配置及采集UDP发送控制视频数据组包数据缓冲FIFOUDP协议栈详解RGMII转GMII动态ARPUDP协议IP地址、端口…

【深度学习 | Transformer】释放注意力的力量:探索深度学习中的 变形金刚,一文带你读通各个模块 —— Positional Encoding(一)

&#x1f935;‍♂️ 个人主页: AI_magician &#x1f4e1;主页地址&#xff1a; 作者简介&#xff1a;CSDN内容合伙人&#xff0c;全栈领域优质创作者。 &#x1f468;‍&#x1f4bb;景愿&#xff1a;旨在于能和更多的热爱计算机的伙伴一起成长&#xff01;&#xff01;&…

kettle应用-从数据库抽取数据到excel

本文介绍使用kettle从postgresql数据库中抽取数据到excel中。 首先&#xff0c;启动kettle 如果kettle部署在windows系统&#xff0c;双击运行spoon.bat或者在命令行运行spoon.bat 如果kettle部署在linux系统&#xff0c;需要执行如下命令启动 chmod x spoon.sh nohup ./sp…

视频监控系统/安防视频平台EasyCVR广场视频细节优化

安防视频监控系统/视频云存储/安防监控EasyCVR视频汇聚平台基于云边端智能协同&#xff0c;支持海量视频的轻量化接入与汇聚、转码与处理、全网智能分发、视频集中存储等。安防视频汇聚平台EasyCVR拓展性强&#xff0c;视频能力丰富&#xff0c;可实现视频监控直播、视频轮播、…

华为9.20笔试 复现

第一题 丢失报文的位置 思路&#xff1a;从数组最小索引开始遍历 #include <iostream> #include <vector> using namespace std; // 求最小索引值 int getMinIdx(vector<int> &arr) {int minidx 0;for (int i 0; i < arr.size(); i){if (arr[i] …

spring boot Rabbit高级教程

消息可靠性 生产者重试机制 首先第一种情况&#xff0c;就是生产者发送消息时&#xff0c;出现了网络故障&#xff0c;导致与MQ的连接中断。 为了解决这个问题&#xff0c;SpringAMQP提供的消息发送时的重试机制。即&#xff1a;当RabbitTemplate与MQ连接超时后&#xff0c;…

【git】500 Whoops, something went wrong on our end.

在访问公的的git 时出现了500错误提示. 500 Whoops, something went wrong on our end. 哎呀&#xff0c;我们这边出了问题。 TMD 出了什么问题了&#xff1f;&#xff1f;&#xff1f;一脸懵逼。 登录git 服务器。 查看git的状态。 命令&#xff1a; gitlab-ctl statu…

互联网Java工程师面试题·Java 总结篇·第一弹

目录 1、面向对象的特征有哪些方面&#xff1f; 2、访问修饰符 public,private,protected,以及不写&#xff08;默认&#xff09;时的区别&#xff1f; 3、String 是最基本的数据类型吗&#xff1f; 4、float f3.4;是否正确&#xff1f; 5、short s1 1; s1 s1 1;有错吗…

华为OD机考算法题:开心消消乐

题目部分 题目开心消消乐难度易题目说明给定一个 N 行 M 列的二维矩阵&#xff0c;矩阵中每个位置的数字取值为 0 或 1&#xff0c;矩阵示例如&#xff1a; 1 1 0 0 0 0 0 1 0 0 1 1 1 1 1 1 现需要将矩阵中所有的 1 进行反转为 0&#xff0c;规则如下&#xff1a; 1) 当点击一…