用top或者ps命令会输出PRI/PR、NI、%ni/%nice这三种指标值

  • PRI :进程优先权,代表这个进程可被执行的优先级,其值越小,优先级就越高,越早被执行
  • NI :进程Nice值,代表这个进程的优先值
  • %nice :改变过优先级的进程的占用CPU的百分比 (呵呵,这句好难理解是吧,不急慢慢来^_^)

PRI是比较好理解的,即进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,此值越小进程的优先级别越高。那NI呢?就是我们所要说的nice值了,其表示进程可被执行的优先级的修正数值。如前面所说,PRI值越小越快被执行,那么加入nice值后,将会使得PRI变为:PRI(new)=PRI(old)+nice。由此看出,PR是根据NICE排序的,规则是NICE越小PR越前(小,优先权更大),即其优先级会变高,则其越快被执行。如果NICE相同则进程uid是root的优先权更大。

在LINUX系统中,Nice值的范围从-20到+19(不同系统的值范围是不一样的),正值表示低优先级,负值表示高优先级,值为零则表示不会调整该进程的优先级。具有最高优先级的程序,其nice值最低,所以在LINUX系统中,值-20使得一项任务变得非常重要;与之相反,如果任务的nice为+19,则表示它是一个高尚的、无私的任务,允许所有其他任务比自己享有宝贵的CPU时间的更大使用份额,这也就是nice的名称的来意。

进程在创建时被赋予不同的优先级值,而如前面所说,nice的值是表示进程优先级值可被修正数据值,因此,每个进程都在其计划执行时被赋予一个nice值,这样系统就可以根据系统的资源以及具体进程的各类资源消耗情况,主动干预进程的优先级值。在通常情况下,子进程会继承父进程的nice值,比如在系统启动的过程中,init进程会被赋予0,其他所有进程继承了这个nice值(因为其他进程都是init的子进程)。

对nice值一个形象比喻,假设在一个CPU轮转中,有2个runnable的进程A和B,如果他们的nice值都为0,假设内核会给他们每人分配1k个cpu时间片。但是假设进程A的为0,但是B的值为-10,那么此时CPU可能分别给A和B分配1k和1.5k的时间片。故可以形象的理解为,nice的值影响了内核分配给进程的cpu时间片的多少,时间片越多的进程,其优先级越高,其优先级值(PRI)越低。%nice,就是改变过优先级的进程的占用CPU的百分比,如上例中就是0.5k/2.5k=1/5=20%。

由此可见,进程nice值和进程优先级不是一个概念,但是进程nice值会影响到进程的优先级变化。

进程优先级可以通过nice,renice命令修改。

android查看进程优先级

1
busybox ps -eo "pid,pri,nice,comm"

Android中的nice

由于Android基于Linux Kernel,在Android中也存在nice值。但是一般情况下我们无法控制,原因如下:

  • Android系统并不像其他Linux发行版那样便捷地使用nice命令操作。
  • renice需要root权限,一般应用无法实现。

线程调度

虽然对于进程的优先级,我们无法控制,但是我们可以控制进程中的线程的优先级。在Android中有两种线程的优先级,一种为Android API版本,另一种是 Java 原生版本。

  • Android API

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    Android中的线程优先级别目前规定了如下,了解了进程优先级与nice值的关系,那么线程优先级与值之间的关系也就更加容易理解。
      
    THREAD_PRIORITY_DEFAULT,默认的线程优先级,值为0。
    THREAD_PRIORITY_LOWEST,最低的线程级别,值为19。
    THREAD_PRIORITY_BACKGROUND 后台线程建议设置这个优先级,值为10。
    THREAD_PRIORITY_FOREGROUND 用户正在交互的UI线程,代码中无法设置该优先级,系统会按照情况调整到该优先级,值为-2。
    THREAD_PRIORITY_DISPLAY 也是与UI交互相关的优先级界别,但是要比THREAD_PRIORITY_FOREGROUND优先,代码中无法设置,由系统按照情况调整,值为-4。
    THREAD_PRIORITY_URGENT_DISPLAY 显示线程的最高级别,用来处理绘制画面和检索输入事件,代码中无法设置成该优先级。值为-8。
    THREAD_PRIORITY_AUDIO 声音线程的标准级别,代码中无法设置为该优先级,值为 -16。
    THREAD_PRIORITY_URGENT_AUDIO 声音线程的最高级别,优先程度较THREAD_PRIORITY_AUDIO要高。代码中无法设置为该优先级。值为-19。
    THREAD_PRIORITY_MORE_FAVORABLE 相对THREAD_PRIORITY_DEFAULT稍微优先,值为-1。
    THREAD_PRIORITY_LESS_FAVORABLE 相对THREAD_PRIORITY_DEFAULT稍微落后一些,值为1。
    使用Android API为线程设置优先级也很简单,只需要在线程执行时调用android.os.Process.setThreadPriority方法即可。这种在线程运行时进行修改优先级,效果类似renice。
    new Thread () {
        @Override
        public void run() {
          super.run();
            android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        }
    }.start();
    
  • Java原生API

    1
    2
    3
    4
    5
    6
    7
    8
    
    Java为Thread提供了三个级别的设置,
      
    MAX_PRIORITY,相当于android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY,值为10。
    MIN_PRIORITY,相当于android.os.Process.THREAD_PRIORITY_LOWEST,值为0。
    NORM_PRIORITY,相当于android.os.Process.THREAD_PRIORITY_DEFAULT,值为5。
    使用setPriority我们可以为某个线程设置优先级,使用getPriority可以获得某个线程的优先级。
      
    在Android系统中,不建议使用Java原生的API,因为Android提供的API划分的级别更多,更适合在Android系统中进行设定细致的优先级。
    

注意

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Android API的线程优先级和Java原生API的优先级是相对独立的,比如使用android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)后,使用Java原生API,Thread.getPriority()得到的值不会改变。如下面代码:
new Thread() {
    @Override
    public void run() {
        super.run();
        Log.i(LOGTAG, "Java Thread Priority Before=" + Thread.currentThread().getPriority());
        Process.setThreadPriority(Process.THREAD_PRIORITY_LOWEST);
        Log.i(LOGTAG, "Java Thread Priority=" + Thread.currentThread().getPriority());
    }
}.start();

上述代码的运行日志为

I/MainActivity( 3679): Java Thread Priority Before=5
I/MainActivity( 3679): Java Thread Priority=5

由于上面的这一点缺陷,导致我们在分析ANR trace时需要注意,在下面的ANR日志信息中,prio=5中proi的值对应的Java原生API的线程优先级。而nice=-6中的nice表示的Android API版本的线程优先级。

"main" prio=5 tid=1 NATIVE
  | group="main" sCount=1 dsCount=0 obj=0x41690f18 self=0x4167e650
  | sysTid=1765 nice=-6 sched=0/0 cgrp=apps handle=1074196888
  | state=S schedstat=( 0 0 0 ) utm=5764 stm=3654 core=2
  #00  pc 00022624  /system/lib/libc.so (__futex_syscall3+8)
  #01  pc 0000f054  /system/lib/libc.so (__pthread_cond_timedwait_relative+48)
  #02  pc 0000f0b4  /system/lib/libc.so (__pthread_cond_timedwait+64)
避免ANR

我在之前的文章说说Android中的ANR中提到使用WorkerThread处理耗时IO操作,同时将WorkerThread的优先级降低,对于耗时IO操作,比如读取数据库,文件等,我们可以设置该workerThread优先级为THREAD_PRIORITY_BACKGROUND,以此降低与主线程竞争的能力。