从Cts测试testCheckForDuplicateOutput到linux kernel中Thread消耗PID

最近折腾CTS

android.security.cts

testCheckForDuplicateOutput项,单项测试很容易过,但是联测就挂了。


源码:http://xdecay.com/docs/android-sdk/cts/tests/tests/security/d0/db5/_cloned_secure_random_test_8java_source.php

测试的原理是:不停的创建和关闭进程,测试Pass的前提是出现两个Pid相同的进程。
循环过程如下:
a). 创建进程A, 记录下A的Pid为pid-1; 然后立即Kill pid-1;
b). 创建进程B, 记录下B的Pid为pid-2; 然后立即Kill pid-2;
……
直到再次出现记录过的进程号。

因为Linux的PId数是有限的,
根据鸽巢原理(n个盒子装n+1个球,必然出现一个盒子有两个球的现象),假设PID的上线时pid_max, 那么如果创建pid_max个进程(并不同时存在),那么
必然会再次出现以前出现过的进程号。


 CTS 测试为了加快测试速率,调用了如下的函数(wastePids):
比如 第一次是pid-1, 多次循环之后还未出现同PID的进程号,比如这时出现了pid-5000,那么就调用wastePids(1,5000)

    /**
     * This is an attempt to get the PIDs to roll over faster. Threads use up
     * PIDs on Android and spawning a new thread is much faster than having
     * another service spawned as we are doing in this test.
     */
    private static void wastePids(int firstPid, int previousPid) {
        int distance = (firstPid - previousPid + MAX_PID) % MAX_PID;
 
        // Don't waste PIDs if we're close to wrap-around to improve odds of
        // collision.
        if ((distance < PID_WASTING_SKIP_LOWER) || (MAX_PID - distance < PID_WASTING_SKIP_UPPER)) {
            return;
        }
 
        for (int i = 0; i < distance; i++) {
            Thread t = new Thread();
            t.start();
        }
    }

按注释翻译而言,应该是通过创建Thread ,将可分配的剩下的Pid占用掉,让Pid尽快循环复用。

问题来了:从传统的thread学习而言,thread怎么会占用Pid,Linux书籍都会告诉我们,所有同一个进程下的thread共享一个Pid;更何况,JVM的线程跟Pthread的线程还不是一个东西,因此迷惑了。


查了linux内核的书籍,基本不怎么讲述thread, 这主要是pthread是一个库,而不是Linux内核本身的一部分。


然后开始翻墙google。

找到3篇文章:

第一篇:http://www.cnblogs.com/princessd8251/articles/3914434.html
这篇文章,测试JVM能开辟的最大线程数:  发现线程数量在达到32279以后,不再增长。查了一下,32位Linux系统可创建的最大pid数是32678,这个数值可以通过/proc/sys/kernel/pid_max来做修改(修改方法同threads-max),但是在32系统下这个值只能改小,无法更大。
虽然实际线程数,可以通过修改内核来创建更多线程,但这表明,线程数跟Pid_max是有关的;

第二篇:http://www.ibm.com/developerworks/cn/linux/kernel/l-thread/

这篇文章分析Linux 线程实现机制
虽然,在线程库里面,pthread的创建,使用的是多个线程share 同一个PID,然而,内核确有自己的机制:
Linux内核并不支持真正意义上的线程,LinuxThreads是用与普通进程具有同样内核调度视图的轻量级进程来实现线程支持的。这些轻量级进程拥有独立的进程id,在进程调度、信号处理、IO等方面享有与普通进程一样的能力。

这句话就表明了,内核中,采用的是轻量级的进程来支撑线程的调度的,那么必然会占用Pid:

第三篇:http://blog.chinaunix.net/uid-27767798-id-3470592.html

讲述了Pid的分配过程:alloc_pidmap:


  static int alloc_pidmap(struct pid_namespace *pid_ns)
 {
         int i, offset, max_scan, pid, last = pid_ns->last_pid;  //取出last_pid
         struct pidmap *map;
  
         pid = last + 1;                                      //这里last+1,取得备选pid
//如果pid到了pidmax,那么重头开始寻找可用的pid,从RESERVED_PIDS开始,保留RESERVED_PIDS之前的pid号,默认300
         if (pid >= pid_max)
                 pid = RESERVED_PIDS;
         ……
}

指明pid是从低往高分配,当超过pid_max,那么重头开始寻找可用的pid,从RESERVED_PIDS开始,保留RESERVED_PIDS之前的pid号,默认300


到这里基本就明朗了。


至于Thread如何通过VM进入到内核 参看:http://www.eoeandroid.com/blog-21517-3026.html



郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。