alloc_pidmap()函数注解

  在创建进程的时候会调用到alloc_pidmap来分配一个空闲的pid。

  函数入参是pid_namespace结构体,介绍下该结构体的两个成员:

    int last_pid 上一个进程所使用的pid;

    struct pidmap pidmap[PIDMAP_ENTRIES] pid位图数组,pidmap有两个成员:nr_free,用来表征当前位图中的空闲pid个数;page指针,存储一页大小的内存的虚拟地址,也就是pid位图,一页有32768位,每一位代表一个pid,置1表示该位表征的pid已被使用。

  来看具体的实现流程:

 1     pid = last + 1;
 2     if (pid >= pid_max)
 3         pid = RESERVED_PIDS;
 4 
 5     /* 32位体系结构上,pid位图是一个物理页,但是pid的值可以超过一页含有的位数32768,
 6        获取新进程pid在页内偏移
 7     */
 8     offset = pid & BITS_PER_PAGE_MASK;
 9     /* pid/BITS_PER_PAGE 表征处于哪一页,每个页都有一个与之对应的pidmap结构体 */
10     map = &pid_ns->pidmap[pid/BITS_PER_PAGE];

  首先是last pid加1作为新进程的pid,如果pid大于等于pid_max,就把pid置为RESERVED_PIDS(300),从RESERVED_PIDS开始查找空闲pid,然后获取页内偏移和该pid所对应的pidmap结构体,假设pid_max为65536,那么pidmap[0]表征pid 0~32767,pidmap[1]表征pid 32768~65535。

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

  接下来会通过一个for循环来遍历位图,max_scan表明遍历次数,如果offset为0,即从0开始查找空闲pid,max_scan为0,那么只需要遍历一次;如果offset不为0,max_scan为1,需要遍历二次。

 1 /* max_scan表征的查找次数只适用于从offset开始查找位图没有找到空闲pid的情况,
 2        因为一旦查找到了就直接返回pid了,不会再开始下一次查找;
 3 
 4        主要是担心offset如果不为0,即使从offset开始直到位图末没有查找到空闲pid,但
 5        是offset之前的位图中仍然可能有空闲的pid,于是将offset置为RESERVED_PIDS,重
 6        新从最初的位图开始查找,或者置offset为0,从下一张开始查找
 7 
 8        如果offset为0,max_scan就为1,从头开始查找,如果没有找到合适的pid,就说明的确
 9        是没有空闲的pid,就不必再继续遍历,max_scan为0即可
10     */
11     for (i = 0; i <= max_scan; ++i) 
12     {
13         /* 分配新页作为pid位图 
14            SOLVE_ME:为什么不用alloc_pages
15         */
16         if (unlikely(!map->page)) 
17         {
18             void *page = kzalloc(PAGE_SIZE, GFP_KERNEL);
19             /*
20              * Free the page if someone raced with us
21              * installing it:
22              */
23             spin_lock_irq(&pidmap_lock);
24             if (!map->page) 
25             {
26                 map->page = page;
27                 page = NULL;
28             }
29             spin_unlock_irq(&pidmap_lock);
30             /* 如果map->page是NULL,page赋值为NULL,接下来free(NULL);
31                如果map->page不是NULL,就不要再分配新页,把新分配的页free掉
32             */
33             kfree(page);
34             if (unlikely(!map->page))
35                 break;
36         }
37         /* 如果该位图内还有未分配的pid */
38         if (likely(atomic_read(&map->nr_free)))
39         {
40             do 
41             {    
42                 /*  查看位图中由偏移offset指定的位是否置位,没有就把它置位,返回原
43                     先的值,返回0说明找到了未被使用的pid
44                 */
45                 if (!test_and_set_bit(offset, map->page)) 
46                 {
47                     /* 空闲pid个数减1 */
48                     atomic_dec(&map->nr_free);
49                     
50                     /* 将pid置为pid_ns->last_pid */
51                     set_last_pid(pid_ns, last, pid);
52                     return pid;
53                 }
54                 /* 如果offset指定的位已被占用,就从offset开始在map->page中找到
55                    第一个为0的位,就是下一个pid,返回值offset是距离地址map起第一个
56                    为0的位,offset如果大于BITS_PER_PAGE说明已经不属于该pidmap了
57                 */
58                 offset = find_next_offset(map, offset);
59                 /* 根据offset得到实际pid值 */
60                 pid = mk_pid(pid_ns, map, offset);
61                 
62             } while (offset < BITS_PER_PAGE && pid < pid_max);
63         }
64 
65         /*
66           在以上的代码中,比较理想的情况是找到了合适的pid,然后return,但是还有一
67           些其他的情况,比如从某个offset开始直到该页结束的位都被用光了,那么根据
68           find_next_offset查找到的offset就会超过BITS_PER_PAGE,此时假如还有多余的
69           位图,offset置0,map指向下一个pidmap,从下一页继续查找即可;假如pid已使用到
70           最后一张位图,就将map设为pidmap[0],从第一张位图开始继续查找,只不过要从
71           offset 300开始查找,前提是max_scan为1,那么max_scan是否有可能不为1呢?
72           
73           如果最初查找空闲pid时获得的offset为0,只有一种情况,就是在内核启动过程中
74           刚刚开始创建进程,即从offset为0开始查找空闲pid是肯定能查找到的,因而如果
75           查找不到pid,最初的offset肯定不为0,即max_scan肯定为1,肯定还可以再执行一
76           个for循环查找一次
77         */
78         if (map < &pid_ns->pidmap[(pid_max-1)/BITS_PER_PAGE]) 
79         {
80             ++map;
81             offset = 0;
82         }
83         else
84         {
85             map = &pid_ns->pidmap[0];
86             offset = RESERVED_PIDS;
87             if (unlikely(last == offset))
88                 break;
89         }
90         pid = mk_pid(pid_ns, map, offset);
91     }

   为pid位图分配一页大小的内存;通过nr_free判断将要查找的pidmap中是否还有空闲的pid,开始一个do...while循环来遍历该位图查找pid,调用test_and_set_bit判断offset所指定的位图中的位是否为0(test_and_set_bit返回0),如果是表明该位表征的pid空闲,即已经找到了空闲pid,nr_free减1,将该pid设置了last_pid之后返回。

  如果offset指定的pid已被占用,就调用find_next_offset从offset开始找到第一个为0的位,返回值是该位距离位图首地址的偏移,如果找到的空闲位的位置没有超过BITS_PER_PAGE,表示仍属于该pidmap并且由空闲位得到的pid没有超过pid_max,那么该位就是所要找的pid,返回。

  存在三种情况会跳出该do...while循环:

  1. offset > BITS_PER_PAGE ,pid < pid_max

    查找到的空闲位不属于该pidmap,但是没有超过pid_max,说明当前位图的pid已全部被占用,那么就从下一个位图的偏移0处开始继续查找;

  2. offset < BITS_PER_PAGE ,pid > pid_max
    查找到的空闲位属于该pidmap,但是超过pid_max,就从第一个pidmap[0]的RESERVED_PIDS开始查找,发生在pid_max不是BITS_PER_PAGE的整数倍的情况;

  3. offset > BITS_PER_PAGE ,pid > pid_max

    查找到的空闲位属于该pidmap,但是超过pid_max,就从第一个pidmap[0]的RESERVED_PIDS开始查找,发生在pid_max是BITS_PER_PAGE的整数倍的情况;

   (其他的可以看函数注释)   

  

          

 

 

 

 

转载于:https://www.cnblogs.com/IrisZhou/p/3237854.html

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值