在研究进程fork()的时候,这个调用了do_fork(),其中do_fork()又涉及到进程的pid分配,这个东西的源代码有许多个版本,而且各自都不是一样的。昨天晚上研究了一晚上,今天从下午研究到现在,总算把内核中pid位图算法有一个整体的把握了。明天早上继续完成这篇博客,现在小小的庆祝一下,明天完成。

    首先我们先看一个程序,这个程序是我从网上找的,这个程序的主要部分也是内核中的代码,下面我们就来分析一下这个程序。
注:原程序有许多bug虫,下面的程序是我修改正确之后的程序,也算是有一点自己的共享吧,另外,原程序没有解释,我的解释估计还算详细吧,不废话了,我们一起来分析一下程序吧。
小知识:进程当中的pid号的分配是从0——32767之间的,其中0—299的进程号是分配给demo(守护进程)的。剩下的pid号是分配给普通进程的。
    要分析这个程序首先我们得明白其中结构体的含义。
struct pidmap这个结构体主要是为了标志pid是不是已经分配出去了。在page这个变量当中表示着,每一个char类型占一个字节—8位,也就是说,为了表示32768个进程号是不是分配出去没有,我们要占用32768/8=4096个字节,这个也是为什么page要申请占用4096个大小的原因。另外,为了表示现在还有多少个pid号没有分配出去,我们还可以使用的话,使用nr_free来表示。
typedef struct pidmap
{unsigned int nr_free;char page[4096];
} pidmap_t;
我们继续来分析有关的函数。
这里涉及到的主要有5个函数。
static int test_and_set_bit(int offset, void *addr);
这个函数的作用主要是将offset在pidmap变量当中相应的位置为1,也就是申请到一个pid号之后,修改位标志。其中addr是pidmap.page变量的地址。
static void clear_bit(int offset, void *addr);
这个函数的作用主要是将offset在pidmap变量当中相应的位置为0,也就是释放一个pid号之后,修改位标志。其中addr是pidmap.page变量的地址。
static int find_next_zero_bit(void *addr, int size, int offset);
从offset开始,找下一个是0(也就是可以分配)的pid号。其中addr是pidmap.page变量的地址,size是一个页的大小。
static int alloc_pidmap();
分配一个pid号。
static void free_pidmap(int pid);
回收一个pid号。
这里还有一个变量:last_pid,这个变量表示的是上一次飞配出去的pid编号。
注意了,关键的地方开始了。
这个算法的思想主要是如何根据一个pid号找到它在pidmap.page变量当中找到相应位的地址。这个可能也是这个算法当中的主要的一个小技巧吧。
好了,如果让我们自己来设计这样的算法——根据pid号找到它在pidmap.page变量当中找到相应位的地址,我们会怎么来写呢?
我首先会给pid除以8,然后取整,即int a=(pid/8),将得到的a的值作为pidmap.page的下标,也就是pidmap.page[a],然后用int b=(pid%8)得到对应位的0—7之间的编号,这样的话,就唯一确定了一个pid编号在pidmap.page中的具体位置。
这个可能是大多数人的想法,但是内核当中是通过移位操作来实现的。我们可以抽象出一张表,这个表有32列,1024行,这个刚好是一个页的大小。这个时候,我们可以通过pid的后5位值(变化范围在0—31之间)来确定在某一行的具体的列。通过pid的高27位(pid本身是32位的)来表示在具体的某一行。所以我们可以通过移位操作来实现。
这里为什么每一行要设置成32列,而不是8列,或者是64列呢?我是这么考虑的,这个算法是在32位机子上运行的,如果是32位的话,刚好是一个unsigned int所占的空间的大小,这样对它的地址的加一操作会跳跃4个字节,和每行8列进行对比的话,这个操作更能够减少加法操作的次数。
这些内容在程序当中对应的是这些语句:
unsigned long mask = 1UL << (offset & (sizeof(unsigned long) * BITS_PER_BYTE - 1));
unsigned long *p = ((unsigned long*)addr) + (offset >> (sizeof(unsigned long) + 1));

其实上面的话,如果在32位机子上运行的话,可以进行如下简化:

unsigned long mask = 1UL << (offset & 31);
unsigned long *p = ((unsigned long*)addr) + (offset >> 5);
说明:
offset对应于一个要处理的pid编号。
offset & 31只保留后5位的值,然后1左移这个值的大小位,这样的话,我们就在某一行中找到了具体的对应的那一位了。
offset右移5位,表示将offset的后5位屏蔽掉了。这样的话,我们就可以找到对应的具体的某一行地址了。
下面是具体的代码:
#include <stdio.h>/* max pid, equal to 2^15=32768 */
#define PID_MAX_DEFAULT 0x8000/* page size = 2^12 = 4K */
#define PAGE_SHIFT 12
#define PAGE_SIZE (1UL << PAGE_SHIFT)#define BITS_PER_BYTE 8
//4k*8 32768
#define BITS_PER_PAGE (PAGE_SIZE * BITS_PER_BYTE)
//7fff
//0111 1111 1111 1111
#define BITS_PER_PAGE_MASK (BITS_PER_PAGE - 1)typedef struct pidmap
{unsigned int nr_free;char page[4096];
} pidmap_t;static pidmap_t pidmap = { PID_MAX_DEFAULT, {0}};static int last_pid = -1;static int test_and_set_bit(int offset, void *addr)
{unsigned long mask = 1UL << (offset & (sizeof(unsigned long) * BITS_PER_BYTE - 1));unsigned long *p = ((unsigned long*)addr) + (offset >> (sizeof(unsigned long) + 1));unsigned long old = *p;*p = old | mask;return (old & mask) != 0;
}static void clear_bit(int offset, void *addr)
{unsigned long mask = 1UL << (offset & (sizeof(unsigned long) * BITS_PER_BYTE - 1));//取offset的后31位数据,并左移unsigned long *p = ((unsigned long*)addr) + (offset >> (sizeof(unsigned long) + 1));//+优先级高于>>unsigned long old = *p;*p = old & ~mask;
}static int find_next_zero_bit(void *addr, int size, int offset)
{unsigned long *p;unsigned long mask;while (offset < size){p = ((unsigned long*)addr) + (offset >> (sizeof(unsigned long) + 1));mask = 1UL << (offset & (sizeof(unsigned long) * BITS_PER_BYTE - 1));if ((~(*p) & mask)){break;}++offset;}return offset;
}static int alloc_pidmap()
{int pid = last_pid + 1;int offset = pid & BITS_PER_PAGE_MASK;//把offset的最高为变为0,其他的不变if (!pidmap.nr_free){return -1;}offset = find_next_zero_bit(&pidmap.page, BITS_PER_PAGE, offset);if (BITS_PER_PAGE != offset && !test_and_set_bit(offset, &pidmap.page)){--pidmap.nr_free;last_pid = offset;return offset;}return -1;
}static void free_pidmap(int pid)
{int offset = pid & BITS_PER_PAGE_MASK;pidmap.nr_free++;clear_bit(offset, &pidmap.page);
}int main()
{int i;for (i = 0; i < PID_MAX_DEFAULT; ++i) {printf("pid = %d\n", alloc_pidmap());}return 0;
}
小结:其实许多海量数据都用到了这个位操作对应的算法,所以知道这个pid位图算法,不失为一件好事。

linux 进程的pid分配策略——pid位图算法相关推荐

  1. Linux 进程管理剖析--转

    地址:http://www.ibm.com/developerworks/cn/linux/l-linux-process-management/index.html Linux 是一种动态系统,能够 ...

  2. linux进程pid分配规则,Linux进程pid分配法

    一. 概述 Android系统创建进程,最终的实现还是调用linux fork方法,对于linux系统每个进程都有唯一的 进程ID(值大于0),也有pid上限,默认为32768. pid可重复利用,当 ...

  3. linux查看pid 对应的程序_资深程序员总结:分析 Linux 进程的 6 个方法,我全都告诉你...

    (给Linux爱好者加星标,提升Linux技能) 作者:LemonCoder(本文来自作者投稿) 操作系统「进程」是学计算机都要接触的基本概念,抛开那些纯理论的操作系统底层实现,在Linux下做软件开 ...

  4. linux命令获取进程pid_如何使用命令获取Linux进程的PID?

    PID或进程ID是标识进程的唯一编号.它们是由Linux内核在运行时创建的,进程调度程序控制着它们的CPU活动.因此,无论何时调用应用程序,内核都会首先产生必要的进程并为它们分配这些唯一的PID值.一 ...

  5. linux进程简介,及PID

    在 Linux 底下执行一个指令时,系统会给予这个动作一个 ID, 我们称为 PID,而根据启用这个指令的使用者与相关的指令功能,而给予这个特定 PID 一组权限, 该指令可以进行的行为则与这个 PI ...

  6. linux进程号是几位数,Linux 进程 PID 编号最大为多少

    问题中提到,通过提问者的测试(Ubuntu18.04操作系统下),Python脚本实现的分配进行pid脚本,渠道系统自动分配给进程的最大pid值是131070,这是一个并不特殊的值.(通常可以想到的上 ...

  7. [ Linux ] 进程概念,pcb,查看进程,pid,ppid,fork

    文章目录 一.进程 1.1 基本概念 1.2 描述进程 - PCB 1.3 查看进程 1.3.1 第一种方式 1.3.2 第二种方式 1.4 通过系统调用获取进程标识符 1.4.1 获取进程的pid ...

  8. linux进程隐藏 hook readdir函数 挂载覆盖/proc/pid 目录

    前言 上篇介绍了如何在有源码的情况下,通过 argv[] 及 prctl 对进程名及参数进行修改,整篇围绕/proc/pid/目录和 ps.top 命令进行分析,做到了初步隐藏,即修改了 /proc/ ...

  9. linux根据pid查看进程,linux根据pid获取进程名和获取进程pid(c语言获取pid)

    2013 Liunx中通过进程名查找进程PID可以通过 pidof [进程名] 来查找.反过来 ,相同通过PID查找进程名则没有相关命令.在linux根目录中,有一个/proc的VFS(虚拟文件系统) ...

  10. linux+pid的管理,Linux 进程管理

    以下是Process Management使用的常用命令 - bg,fg,nohup,ps,pstree,top,kill,killall,免费,正常运行时间,很好. 与过程一起工作 快速提示:Lin ...

最新文章

  1. iOS的相对路径和绝对路径
  2. C++ 之 string
  3. PHP 拷贝图像 imagecopy 与 imagecopyresized 函数
  4. 初中学历怎么学计算机管理,初中学历能否学习计算机
  5. 业余学python数据挖掘怎么赚钱_0基础转行学Python,学到什么地步能拿到月薪15k+?...
  6. 用python实现自己的小说阅读器
  7. 信号灯文件锁linux线程,linux——线程同步(互斥量、条件变量、信号灯、文件锁)...
  8. php 网络图片 转本地,PHP将Base64图片转换为本地图片并保存
  9. python初始环境安装
  10. 华为笔试题——分礼物
  11. 初学者对于SVM的一点点学习心得
  12. 电感耦合等离子体发射光谱法测定水样中的金属含量
  13. Linux电镜分析,环境扫描电子显微镜特点
  14. 【手把手教你】搭建神经网络(3D点云分类)
  15. 开放式社区?太小儿科了,智慧城市才是重点
  16. Android 微软雅黑、仿宋、黑体的使用
  17. 蓝牙耳机南卡和vivo哪个好用?南卡与vivo实际评测!
  18. 如何使用 PyTorch 训练自定义关键点检测模型
  19. 实现悬浮球的桌面显示
  20. 社保能找公司代缴吗?小心问题找上门

热门文章

  1. asp.net编程基础
  2. HDU - 1520 Anniversary party (有向入门树形DP)
  3. dfs序七个经典问题[转]
  4. 利用iisnode模块,让你的Node.js应用跑在Windows系统IIS中
  5. 【OOM】GC overhead limit exceeded
  6. petshop4.0 详解之五(PetShop之业务逻辑层设计)(转帖)
  7. web安全day5:DNS部署与安全
  8. H3C Private VLAN(私有vlan) 实验
  9. CocosCreator2.3.1按钮节点防止多次连续点击
  10. 十七、K8s+gitlab+Jenkins建立CI/CD解决方案