linux的top命令源码解析:RES指标

CLion:2019.3.6
源码管理平台:Macbook Pro 10.12.6
C语言源码程序:Makefile格式的linux系统的top命令源码。
top所在的系统:ubuntu 14.04

一、源码导入

linux的top命令采用的是procps-ng项目,可以通过Clion 导入Makefile格式的C语言源程序:procps项目 这篇博客将源码进行下载和导入。

二、RES 指标的源码分析

1、输入top命令后显示如下:
在这里插入图片描述
对于RES指标, 代表PID进程占用的物理内存,其中包括共享库内存,RES的数据是怎么计算得来的呢,接下来我们将通过查看源码来了解这个数据的源头。

2、top的源码分析
top的源码位于top.c文件中:
在这里插入图片描述
top.c通过task_show如下进行获取res的数值,

/*
 * Build the information for a single task row and
 * display the results or return them to the caller. */
static const char *task_show (const WIN_t *q, const int idx) {
  ... 省略....
 #define pages2K(n)  (unsigned long)( (n) << Pg2K_shft )
   ... 省略....
   // we must begin a row with a possible window number in mind...
   *(rp = rbuf) = '\0';
   if (Rc.mode_altscr) rp = scat(rp, " ");
   ... 省略....
   for (x = 0; x < q->maxpflgs; x++) {
      const char *cp = NULL;
      FLG_t       i = q->procflgs[x];
      ... 省略....
      switch (i) {
       ... 省略....
         case EU_MEM:
            cp = scale_pcnt((float)pages2K(p->resident) * 100 / kb_main_total, W, Jn);
         ... 省略....
         case EU_NMA:
            cp = make_num(numa_node_of_cpu(p->processor), W, Jn, AUTOX_NO, 0);
            break;
         case EU_RES:
            cp = scale_mem(S, pages2K(p->resident), W, Jn);
            break;
         case EU_SHR:
            cp = scale_mem(S, pages2K(p->share), W, Jn);
            break;
         case EU_SWP:
            cp = scale_mem(S, p->vm_swap, W, Jn);
            break;
         ... 省略....
         default:                 // keep gcc happy
            continue;
      } // end: switch 'procflag'
 #undef pages2K
} // end: task_show

其中RES获取的片段如下:
在这里插入图片描述
其中p->resident 是res的数据源,p的结构:

   proc_t *p = q->ppt[idx];

其中proc_t的结构体主要内容如下所示:

// Basic data structure which holds all information we can get about a process.
// (unless otherwise specified, fields are read from /proc/#/stat)
//
// Most of it comes from task_struct in linux/sched.h
//
typedef struct proc_t {
// 1st 16 bytes
    int
        tid,		// (special)       task id, the POSIX thread ID (see also: tgid)
    	ppid;		// stat,status     pid of parent process
    unsigned long       // next 2 fields are NOT filled in by readproc
    ..........省略.........
    long
	priority,	// stat            kernel scheduling priority
	nice,		// stat            standard unix nice level of process
	rss,		// stat            identical to 'resident'
	alarm,		// stat            ?
    // the next 7 members come from /proc/#/statm
	size,		// statm           total virtual memory (as # pages)
	resident,	// statm           resident non-swapped memory (as # pages)
	share,		// statm           shared (mmap'd) memory (as # pages)
	trs,		// statm           text (exe) resident set (as # pages)
	lrs,		// statm           library resident set (always 0 w/ 2.6)
	drs,		// statm           data+stack resident set (as # pages)
	dt;		// statm           dirty pages (always 0 w/ 2.6)
    unsigned long
	vm_size,        // status          equals 'size' (as kb)
	vm_lock,        // status          locked pages (as kb)
	vm_rss,         // status          equals 'rss' and/or 'resident' (as kb)
	vm_rss_anon,    // status          the 'anonymous' portion of vm_rss (as kb)
	vm_rss_file,    // status          the 'file-backed' portion of vm_rss (as kb)
	vm_rss_shared,  // status          the 'shared' portion of vm_rss (as kb)
	vm_data,        // status          data only size (as kb)
	vm_stack,       // status          stack only size (as kb)
	vm_swap,        // status          based on linux-2.6.34 "swap ents" (as kb)
    ..........省略.........
    const char
        *lxcname;       // n/a             lxc container name
} proc_t;

所以top的res是从proc_t->resident获取的,其中resident的单位是页面,是通过pages2K将页面数量转换成字节数, 页面的大小是4K:

         case EU_RES:
            cp = scale_mem(S, pages2K(p->resident), W, Jn);

其中pages2K函数:

   /* The run-time acquired page stuff */
   static unsigned Pg2K_shft = 0;
   // get virtual page stuff
   i = page_bytes; // from sysinfo.c, at lib init
   while(i > 1024) { i >>= 1; Pg2K_shft++; }

 #define pages2K(n)  (unsigned long)( (n) << Pg2K_shft )

如上所示:q->ppt[idx]又是从哪儿初始化的呢?
如下代码所示:q作为task_show函数传递进来:

static const char *task_show (const WIN_t *q, const int idx) {

WIN_t的结构定义如下:


        /* This structure stores configurable information for each window.
           By expending a little effort in its creation and user requested
           maintenance, the only real additional per frame cost of having
           windows is an extra sort -- but that's just on pointers! */
typedef struct WIN_t {
   FLG_t  pflgsall [PFLAGSSIZ],        // all 'active/on' fieldscur, as enum
          procflgs [PFLAGSSIZ];        // fieldscur subset, as enum
   RCW_t  rc;                          // stuff that gets saved in the rcfile
   int    winnum,          // a window's number (array pos + 1)
          winlines,        // current task window's rows (volatile)
          maxpflgs,        // number of displayed procflgs ("on" in fieldscur)
          totpflgs,        // total of displayable procflgs in pflgsall array
          begpflg,         // scrolled beginning pos into pflgsall array
          endpflg,         // scrolled ending pos into pflgsall array
          begtask,         // scrolled beginning pos into Frame_maxtask
          begnext,         // new scrolled delta for next frame's begtask
#ifndef SCROLLVAR_NO
          varcolbeg,       // scrolled position within variable width col
#endif
          varcolsz,        // max length of variable width column(s)
          usrseluid,       // validated uid for 'u/U' user selection
          usrseltyp,       // the basis for matching above uid
          usrselflg,       // flag denoting include/exclude matches
          hdrcaplen;       // column header xtra caps len, if any
   char   capclr_sum [CLRBUFSIZ],      // terminfo strings built from
          capclr_msg [CLRBUFSIZ],      //   RCW_t colors (& rebuilt too),
          capclr_pmt [CLRBUFSIZ],      //   but NO recurring costs !
          capclr_hdr [CLRBUFSIZ],      //   note: sum, msg and pmt strs
          capclr_rowhigh [CLRBUFSIZ],  //         are only used when this
          capclr_rownorm [CLRBUFSIZ],  //         window is the 'Curwin'!
          cap_bold [CAPBUFSIZ],        // support for View_NOBOLD toggle
          grpname [GRPNAMSIZ],         // window number:name, printable
#ifdef USE_X_COLHDR
          columnhdr [ROWMINSIZ],       // column headings for procflgs
#else
          columnhdr [SCREENMAX],       // column headings for procflgs
#endif
         *captab [CAPTABMAX];          // captab needed by show_special()
   struct osel_s *osel_1st;            // other selection criteria anchor
   int    osel_tot;                    // total of other selection criteria
   char  *findstr;                     // window's current/active search string
   int    findlen;                     // above's strlen, without call overhead
   proc_t **ppt;                       // this window's proc_t ptr array
   struct WIN_t *next,                 // next window in window stack
                *prev;                 // prior window in window stack
} WIN_t;

关键的代码在readproc.c,如下代码所示:

//
// This reads process info from /proc in the traditional way, for one process.
// The pid (tgid? tid?) is already in p, and a path to it in path, with some
// room to spare.
static proc_t* simple_readproc(PROCTAB *restrict const PT, proc_t *restrict const p) {
     ......省略.......
    if (flags & PROC_FILLMEM) {                 // read /proc/#/statm
        if (likely(file2str(path, "statm", &ub) != -1))
            statm2proc(ub.buf, p);
    }
     ......省略.......

其中statm2proc的函数实现如下:

static void statm2proc(const char* s, proc_t *restrict P) {
    sscanf(s, "%ld %ld %ld %ld %ld %ld %ld",
	   &P->size, &P->resident, &P->share,
	   &P->trs, &P->lrs, &P->drs, &P->dt);
}

可以看出,top的RES是从/proc/pid/statm文件中格式化读出来的。


版权声明:本文为whbing1471原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。