Top中是如何取到Linux内核中的Hertz的?以及CPU使用率到底是怎么算出来的?

首先来看CPU使用率到底是怎么算出来的。依据的是这个公式:

(process jiffies) * 100.0f / ((float)Hertz * (float)et * (Rc.mode_irixps ? 1 : Cpu_tot));

process jiffies是内核提供的该进程在DeltaT时间内消耗的jiffies。具体是/proc/<pid>/stat文件的第14-17 token。14-17token分别是utime, stime, cutime, cstime。cutime/cstime分别是该进程spawn的子进程在用户态和内核态消耗的jiffies。/proc/<pid> /task目录下有该进程所有线程的数据,也是在stat文件中读取,格式和process的是一样的,因为Linux内核中线程和进程区别不大的。所以 也完全可以将该进程所有线程消耗的jiffies累加起来,从而得到该进程的jiffies。man proc可以看到/proc下所有内容的解释。

注意stat中的jiffies是一个绝对累计值,所以要取两个时间点,算DeltaT中消耗的jiffies。

那 什么是jiffies呢?其实就是Linux内核定义的一个时间单位,值就是1/Hertz。Linux内核中,进程/线程消耗的时间,单位都是这个 jiffies。Linux内核没有开放什么系统调用,让程序可以直接取得这个Hertz,从而算出jiffies,为此,Top自己写了一个函数来算 Hertz这个值(不同的硬件平台,这个Hertz是不一样的。而且这个jiffies就是Linux内核调度进程的时间片大小)。最后会给出这个 Hertz在top中是如何计算出来的函数。

OK,回到上面的公式。et表示流逝的时间,单位是秒。最后一个部分是CPU/core数。

所以,总结来看,CPU使用率其实就是在DeltaT时间内,进程一共消耗了多少jiffies,消耗的越多,自然该进程的CPU使用率就越高了。更抽象一点来说,一个进程在单位时间内,被分配到的时间片越多,那么这个进程的CPU占用率就越高。

最后给出Top中是如何计算这个Hertz的,一般现在这个Hertz都是100,也就是jiffies就代表10ms,这也是linux内核中调度时间片的大小。
     /* **********************************************************************
    * Some values in /proc are expressed in units of 1/HZ seconds, where HZ
    * is the kernel clock tick rate. One of these units is called a jiffy.
    * The HZ value used in the kernel may vary according to hacker desire.
    * According to Linus Torvalds, this is not true. He considers the values
    * in /proc as being in architecture-dependant units that have no relation
    * to the kernel clock tick rate. Examination of the kernel source code
    * reveals that opinion as wishful thinking.
    *
    * In any case, we need the HZ constant as used in /proc. (the real HZ value
    * may differ, but we don't care) There are several ways we could get HZ:
    *
    * 1. Include the kernel header file. If it changes, recompile this library.
    * 2. Use the sysconf() function. When HZ changes, recompile the C library!
    * 3. Ask the kernel. This is obviously correct...
    *
    * Linus Torvalds won't let us ask the kernel, because he thinks we should
    * not know the HZ value. Oh well, we don't have to listen to him.
    * Someone smuggled out the HZ value. :-)
    *
    * This code should work fine, even if Linus fixes the kernel to match his
    * stated behavior. The code only fails in case of a partial conversion.
    *
    * Recent update: on some architectures, the 2.4 kernel provides an
    * ELF note to indicate HZ. This may be for ARM or user-mode Linux
    * support. This ought to be investigated. Note that sysconf() is still
    * unreliable, because it doesn't return an error code when it is
    * used with a kernel that doesn't support the ELF note. On some other
    * architectures there may be a system call or sysctl() that will work.
    
*/

    unsigned 
long   long  Hertz;

    
static   void  old_Hertz_hack( void ){
      unsigned 
long   long  user_j, nice_j, sys_j, other_j;   /*  jiffies (clock ticks)  */
      
double  up_1, up_2, seconds;
      unsigned 
long   long  jiffies;
      unsigned h;
      
char   * restrict savelocale;

      savelocale 
=  setlocale(LC_NUMERIC, NULL);
      setlocale(LC_NUMERIC, 
" C " );
      
do {
        FILE_TO_BUF(UPTIME_FILE,uptime_fd);  sscanf(buf, 
" %lf " & up_1);
        
/*  uptime(&up_1, NULL);  */
        FILE_TO_BUF(STAT_FILE,stat_fd);
        sscanf(buf, 
" cpu %Lu %Lu %Lu %Lu " & user_j,  & nice_j,  & sys_j,  & other_j);
        FILE_TO_BUF(UPTIME_FILE,uptime_fd);  sscanf(buf, 
" %lf " & up_2);
        
/*  uptime(&up_2, NULL);  */
      } 
while (( long   long )( (up_2 - up_1) * 1000.0 / up_1 ));  /*  want under 0.1% error  */
      setlocale(LC_NUMERIC, savelocale);
      jiffies 
=  user_j  +  nice_j  +  sys_j  +  other_j;
      seconds 
=  (up_1  +  up_2)  /   2 ;
      h 
=  (unsigned)( ( double )jiffies / seconds / smp_num_cpus );
      
/*  actual values used by 2.4 kernels: 32 64 100 128 1000 1024 1200  */
      
switch (h){
      
case      9  ...    11  :  Hertz  =     10 break /*  S/390 (sometimes)  */
      
case     18  ...    22  :  Hertz  =     20 break /*  user-mode Linux  */
      
case     30  ...    34  :  Hertz  =     32 break /*  ia64 emulator  */
      
case     48  ...    52  :  Hertz  =     50 break ;
      
case     58  ...    61  :  Hertz  =     60 break ;
      
case     62  ...    65  :  Hertz  =     64 break /*  StrongARM /Shark  */
      
case     95  ...   105  :  Hertz  =    100 break /*  normal Linux  */
      
case    124  ...   132  :  Hertz  =    128 break /*  MIPS, ARM  */
      
case    195  ...   204  :  Hertz  =    200 break /*  normal << 1  */
      
case    253  ...   260  :  Hertz  =    256 break ;
      
case    393  ...   408  :  Hertz  =    400 break /*  normal << 2  */
      
case    790  ...   808  :  Hertz  =    800 break /*  normal << 3  */
      
case    990  ...  1010  :  Hertz  =   1000 break /*  ARM  */
      
case   1015  ...  1035  :  Hertz  =   1024 break /*  Alpha, ia64  */
      
case   1180  ...  1220  :  Hertz  =   1200 break /*  Alpha  */
      
default :
    #ifdef HZ
        Hertz 
=  (unsigned  long   long )HZ;     /*  <asm/param.h>  */
    
#else
        
/*  If 32-bit or big-endian (not Alpha or ia64), assume HZ is 100.  */
        Hertz 
=  ( sizeof ( long ) == sizeof ( int ||  htons( 999 ) == 999 ?   100UL  :  1024UL ;
    
#endif
        fprintf(stderr, 
" Unknown HZ value! (%d) Assume %Ld.\n " , h, Hertz);
      }
    }

 

 

代码很简单,其实就是计算单位1秒内,每CPU/core产生了多少了jiffies。这个方法就和具体的硬件平台没有关系了。所以这是一个取得Linux内核Hertz的好方法。

 

 

你可能感兴趣的:(linux)