Linux中初始化空循环次数和pid位图初始化

校准处理器速度calibrate_delay

c 复制代码
void __devinit calibrate_delay(void)
{
        unsigned long ticks, loopbit;
        int lps_precision = LPS_PREC;

        if (preset_lpj) {
                loops_per_jiffy = preset_lpj;
                printk("Calibrating delay loop (skipped)... "
                        "%lu.%02lu BogoMIPS preset\n",
                        loops_per_jiffy/(500000/HZ),
                        (loops_per_jiffy/(5000/HZ)) % 100);
        } else {
                loops_per_jiffy = (1<<12);

                printk(KERN_DEBUG "Calibrating delay loop... ");
                while ((loops_per_jiffy <<= 1) != 0) {
                        /* wait for "start of" clock tick */
                        ticks = jiffies;
                        while (ticks == jiffies)
                                /* nothing */;
                        /* Go .. */
                        ticks = jiffies;
                        __delay(loops_per_jiffy);
                        ticks = jiffies - ticks;
                        if (ticks)
                                break;
                }

                /*
                 * Do a binary approximation to get loops_per_jiffy set to
                 * equal one clock (up to lps_precision bits)
                 */
                loops_per_jiffy >>= 1;
                loopbit = loops_per_jiffy;
                while (lps_precision-- && (loopbit >>= 1)) {
                        loops_per_jiffy |= loopbit;
                        ticks = jiffies;
                        while (ticks == jiffies)
                                /* nothing */;
                        ticks = jiffies;
                        __delay(loops_per_jiffy);
                        if (jiffies != ticks)   /* longer than 1 tick */
                                loops_per_jiffy &= ~loopbit;
                }

                /* Round the value and print it */
                printk("%lu.%02lu BogoMIPS (lpj=%lu)\n",
                        loops_per_jiffy/(500000/HZ),
                        (loops_per_jiffy/(5000/HZ)) % 100,
                        loops_per_jiffy);
        }

}

函数功能概述

这个函数用于校准处理器速度,计算 loops_per_jiffy(每个时钟节拍可以执行的循环次数),从而确定系统的 BogoMIPS 值。BogoMIPS 是一个粗略的处理器速度指标,BogoMIPS = Bogus (虚假的/伪造的) + MIPS (每秒百万条指令),表示处理器在一秒内可以执行空循环的次数(以百万为单位)

代码详细解释

第一部分:变量声明和预设值检查

c 复制代码
void __devinit calibrate_delay(void)
{
    unsigned long ticks, loopbit;
    int lps_precision = LPS_PREC;

    if (preset_lpj) {
        loops_per_jiffy = preset_lpj;
        printk("Calibrating delay loop (skipped)... "
                "%lu.%02lu BogoMIPS preset\n",
                loops_per_jiffy/(500000/HZ),
                (loops_per_jiffy/(5000/HZ)) % 100);
    }
  • __devinit 表示这个函数在设备初始化期间使用,初始化完成后可能被释放
  • 声明变量:ticks 用于时间测量,loopbit 用于二分查找,lps_precision 设置校准精度
  • 如果 preset_lpj(预设的 loops_per_jiffy)不为0,直接使用预设值
  • 计算并打印 BogoMIPS 值:loops_per_jiffy/(500000/HZ) 计算整数部分,(loops_per_jiffy/(5000/HZ)) % 100 计算小数部分

第二部分:粗略校准 - 寻找数量级

c 复制代码
    } else {
        loops_per_jiffy = (1<<12);

        printk(KERN_DEBUG "Calibrating delay loop... ");
        while ((loops_per_jiffy <<= 1) != 0) {
            /* wait for "start of" clock tick */
            ticks = jiffies;
            while (ticks == jiffies)
                /* nothing */;
            /* Go .. */
            ticks = jiffies;
            __delay(loops_per_jiffy);
            ticks = jiffies - ticks;
            if (ticks)
                break;
        }
  • 如果没有预设值,从 4096(1<<12)开始
  • 进入循环,每次将 loops_per_jiffy 左移一位(加倍)
  • while (ticks == jiffies) 等待下一个时钟节拍开始
  • 执行 __delay(loops_per_jiffy) 运行指定次数的空循环
  • 计算实际消耗的时间节拍数
  • 如果消耗了时间(ticks != 0),说明循环次数足够多,跳出循环

第三部分:精确校准 - 二分查找

c 复制代码
        loops_per_jiffy >>= 1;
        loopbit = loops_per_jiffy;
        while (lps_precision-- && (loopbit >>= 1)) {
            loops_per_jiffy |= loopbit;
            ticks = jiffies;
            while (ticks == jiffies)
                /* nothing */;
            ticks = jiffies;
            __delay(loops_per_jiffy);
            if (jiffies != ticks)   /* longer than 1 tick */
                loops_per_jiffy &= ~loopbit;
        }
  • loops_per_jiffy >>= 1 回退到上一次成功的值
  • loopbit = loops_per_jiffy 初始化二分查找的步长
  • 循环 lps_precision 次进行精确校准
  • loops_per_jiffy |= loopbit 尝试增加当前精度位
  • 同样等待时钟节拍开始,然后执行延迟
  • 如果执行时间超过1个节拍(jiffies != ticks),说明循环次数过多,清除该精度位

第四部分:结果输出

c 复制代码
        /* Round the value and print it */
        printk("%lu.%02lu BogoMIPS (lpj=%lu)\n",
                loops_per_jiffy/(500000/HZ),
                (loops_per_jiffy/(5000/HZ)) % 100,
                loops_per_jiffy);
    }
}
  • 打印最终的校准结果,格式为:"整数.小数 BogoMIPS (lpj=实际循环次数)"

关键概念说明

  • jiffy: 系统时钟节拍,通常是 1ms、4ms 或 10ms,取决于 HZ 值
  • loops_per_jiffy: 在一个 jiffy 内可以执行的空循环次数
  • BogoMIPS: "Bogus MIPS" - 一个近似的处理器速度指标
  • 二分查找: 用于精确确定最大的不超时的循环次数

这个函数通过动态测试确定了处理器执行空循环的速度,为系统中的时间延迟函数提供了准确的基准

初始化PID位图pidmap_init

c 复制代码
void __init pidmap_init(void)
{
        int i;

        pidmap_array->page = (void *)get_zeroed_page(GFP_KERNEL);
        set_bit(0, pidmap_array->page);
        atomic_dec(&pidmap_array->nr_free);

        /*
         * Allocate PID 0, and hash it via all PID types:
         */

        for (i = 0; i < PIDTYPE_MAX; i++)
                attach_pid(current, i, 0);
}

函数功能概述

这个函数用于初始化进程ID(PID)位图,并设置PID 0为已使用状态。PID 0通常分配给内核的空闲进程(swapper进程)

代码详细解释

第一部分:变量声明和基础初始化

c 复制代码
void __init pidmap_init(void)
{
        int i;

        pidmap_array->page = (void *)get_zeroed_page(GFP_KERNEL);
  • __init 表示这个函数在系统初始化期间调用,初始化完成后内存会被释放
  • 声明循环变量 i,用于遍历所有PID类型
  • get_zeroed_page(GFP_KERNEL) 申请一个物理内存页并将其内容清零
    • GFP_KERNEL 是内存分配标志,表示内核正常优先级分配
    • 返回的是页的虚拟地址
  • 将获取的页赋值给 pidmap_array->page,这个位图用于跟踪PID的分配状态

第二部分:保留PID 0

c 复制代码
        set_bit(0, pidmap_array->page);
        atomic_dec(&pidmap_array->nr_free);
  • set_bit(0, pidmap_array->page) 将位图的第0位设置为1
    • 在位图中,1表示已使用,0表示空闲
    • PID 0被保留,不能被普通进程使用
  • atomic_dec(&pidmap_array->nr_free) 原子操作减少空闲PID计数器
    • atomic_dec 保证在多处理器环境下的原子性
    • nr_free 记录当前可用的PID数量
    • 由于PID 0被占用,空闲PID数量减1

第三部分:为所有PID类型注册PID 0

c 复制代码
        /*
         * Allocate PID 0, and hash it via all PID types:
         */

        for (i = 0; i < PIDTYPE_MAX; i++)
                attach_pid(current, i, 0);
}
  • 分配PID 0,并通过所有PID类型进行哈希处理
  • for (i = 0; i < PIDTYPE_MAX; i++) 遍历所有PID类型
    • PIDTYPE_MAX 是PID类型的最大值,通常包括:
      • PIDTYPE_PID:进程ID
      • PIDTYPE_TGID:线程组ID
      • PIDTYPE_PGID:进程组ID
      • PIDTYPE_SID:会话ID
  • attach_pid(current, i, 0) 为当前进程附加PID 0到每种PID类型的哈希表中
    • current 指向当前正在执行的进程(内核的swapper进程)
    • i 是PID类型
    • 0 是PID值

关键数据结构

PID位图结构

c 复制代码
struct pidmap {
    atomic_t nr_free;        // 空闲PID数量
    void *page;              // 位图页面
};

PID类型枚举

c 复制代码
enum pid_type {
    PIDTYPE_PID,      // 进程ID
    PIDTYPE_TGID,     // 线程组ID
    PIDTYPE_PGID,     // 进程组ID
    PIDTYPE_SID,      // 会话ID
    PIDTYPE_MAX       // PID类型数量
};

内存布局示意图

复制代码
pidmap_array->page 位图内存页:
+---+---+---+---+---+---+
| 1 | 0 | 0 | 0 | 0 | ... |  (4096字节,32768个PID)
+---+---+---+---+---+---+
  ^   ^   ^   ^   ^
  |   |   |   |   |
PID0 PID1 PID2 PID3 PID4...
  • 第0位:设置为1(PID 0已使用)
  • 其他位:初始为0(空闲状态)

将进程关联到指定的PID attach_pid

c 复制代码
int fastcall attach_pid(task_t *task, enum pid_type type, int nr)
{
        struct pid *pid, *task_pid;

        task_pid = &task->pids[type];
        pid = find_pid(type, nr);
        if (pid == NULL) {
                hlist_add_head(&task_pid->pid_chain,
                                &pid_hash[type][pid_hashfn(nr)]);
                INIT_LIST_HEAD(&task_pid->pid_list);
        } else {
                INIT_HLIST_NODE(&task_pid->pid_chain);
                list_add_tail(&task_pid->pid_list, &pid->pid_list);
        }
        task_pid->nr = nr;

        return 0;
}

函数功能概述

这个函数负责将进程任务关联到指定的PID编号和类型,管理Linux内核中复杂的PID组织结构。它处理PID哈希表和进程链表的维护,支持多线程共享同一PID的场景

代码逐行详细解释

第一部分:函数声明和变量定义

c 复制代码
int fastcall attach_pid(task_t *task, enum pid_type type, int nr)
{
        struct pid *pid, *task_pid;
  • fastcall:这是一个函数调用约定修饰符,指示编译器使用寄存器来传递参数,而不是通过堆栈,这样可以提高函数调用速度。在x86架构中,前两个参数通常通过ECX和EDX寄存器传递

  • 参数分析

    • task_t *task:指向进程任务描述符的指针,即需要关联PID的进程
    • enum pid_type type:PID类型枚举,包括:
      • PIDTYPE_PID - 进程ID
      • PIDTYPE_TGID - 线程组ID
      • PIDTYPE_PGID - 进程组ID
      • PIDTYPE_SID - 会话ID
    • int nr:具体的PID数值
  • 局部变量

    • struct pid *pid:用于指向在全局哈希表中查找到的现有PID结构
    • struct pid *task_pid:指向当前任务内部的PID结构

第二部分:获取任务PID结构和查找全局PID

c 复制代码
        task_pid = &task->pids[type];
        pid = find_pid(type, nr);
  • task_pid = &task->pids[type]

    • 从任务结构体中获取对应类型的PID结构指针
    • task->pids 是一个数组,每个元素对应一种PID类型
    • 例如:task->pids[PIDTYPE_PID] 是进程ID结构
    • 这个操作获取了任务内部存储PID信息的位置
  • pid = find_pid(type, nr)

    • 调用 find_pid 函数在全局PID哈希表中查找指定的PID
    • 查找过程:pid_hash[type][pid_hashfn(nr)] → 遍历哈希链表
    • 如果找到匹配的PID,返回该PID结构指针
    • 如果没找到,返回NULL

第三部分:处理新PID的情况(首次分配)

c 复制代码
        if (pid == NULL) {
                hlist_add_head(&task_pid->pid_chain,
                                &pid_hash[type][pid_hashfn(nr)]);
                INIT_LIST_HEAD(&task_pid->pid_list);
  • if (pid == NULL):条件判断,如果该PID在全局哈希表中不存在

  • hlist_add_head(&task_pid->pid_chain, &pid_hash[type][pid_hashfn(nr)])

    • pid_hashfn(nr):计算PID的哈希值,将PID数值映射到哈希桶索引
    • pid_hash[type][...]:二维哈希表,第一维是PID类型,第二维是哈希桶
    • hlist_add_head:将任务的pid_chain节点添加到哈希链表的头部
    • 这使该任务成为该PID在哈希表中的第一个代表
  • INIT_LIST_HEAD(&task_pid->pid_list)

    • 初始化一个空的双向链表头
    • 这个链表用于存储所有共享同一PID的任务
    • 对于新PID,当前只有这一个任务,所以初始化空链表

第四部分:处理现有PID的情况(共享PID)

c 复制代码
        } else {
                INIT_HLIST_NODE(&task_pid->pid_chain);
                list_add_tail(&task_pid->pid_list, &pid->pid_list);
        }
  • else:如果该PID已经在全局哈希表中存在

  • INIT_HLIST_NODE(&task_pid->pid_chain)

    • 初始化哈希链表节点,但不将其加入任何哈希表
    • 因为该PID已经在全局哈希表中存在,不需要重复添加
    • 只是确保节点的状态正确,防止未定义行为
  • list_add_tail(&task_pid->pid_list, &pid->pid_list)

    • 将当前任务的pid_list节点添加到现有PID的进程链表尾部
    • 这实现了多个任务共享同一PID的功能
    • 例如:线程组中的所有线程共享同一个TGID

第五部分:设置PID数值并返回

c 复制代码
        task_pid->nr = nr;

        return 0;
}
  • task_pid->nr = nr

    • 设置PID结构中的数值字段
    • 无论新旧PID,都需要记录具体的PID数值
    • 这个数值用于后续的PID查找和比较
  • return 0

    • 函数总是返回成功(0)
    • 因为所有错误情况都应该在调用前被处理

数据结构关系详解

PID结构定义

c 复制代码
struct pid {
    int nr;                      // PID数值
    struct hlist_node pid_chain; // 哈希表链表节点
    struct list_head pid_list;   // 共享同一PID的任务链表
};

场景1:新PID创建

复制代码
执行前:
全局哈希表: 空
任务PID结构: 未初始化

执行过程:
1. 将任务PID结构加入哈希表: pid_hash[type][hash] → task_pid
2. 初始化空进程链表: task_pid→pid_list → [空]
3. 设置PID数值: task_pid→nr = nr

执行后:
全局哈希表: pid_hash[type][hash] → task_pid (通过pid_chain连接)
任务链表: task_pid→pid_list 指向空链表头

场景2:共享现有PID

复制代码
执行前:
全局哈希表: pid_hash[type][hash] → existing_pid
现有PID链表: existing_pid→pid_list → task1 → task2

执行过程:
1. 初始化task_pid的pid_chain(不加入哈希表)
2. 将task_pid加入现有PID的进程链表: existing_pid→pid_list → task1 → task2 → task_pid
3. 设置PID数值: task_pid→nr = nr

执行后:
全局哈希表: 不变,仍然指向existing_pid
现有PID链表: 新增task_pid在尾部
相关推荐
东城绝神4 小时前
《Linux运维总结:基于X86_64+ARM64架构CPU使用docker-compose一键离线部署consul 1.21.5容器版集群》
linux·运维·docker·架构·consul
ajassi20004 小时前
开源 Linux 服务器与中间件(三)服务器--Nginx
linux·服务器·开源
wheeldown4 小时前
【Linux】Linux进程间通信:命名管道(FIFO)的模拟实现重要知识点梳理
linux·运维·服务器
Crazy________4 小时前
34部署LNMP架构详细解析
linux·运维·服务器·nginx
tan180°4 小时前
Linux网络HTTP(上)(7)
linux·网络·http
小醉你真好4 小时前
17、Centos9 安装 1Panel
linux·docker·运维开发
九皇叔叔4 小时前
Linux Shell 正则表达式:从入门到实战,玩转文本匹配与处理
linux·mysql·正则表达式
lhxcc_fly5 小时前
Linux网络--6、网络层
linux·网络·ip
刺客xs5 小时前
linux GDB调试器
linux·运维·windows