psproc 源码阅读 - 4

回到main中,还剩最后一点点代码:

    lists_and_needs(); 
    finalize_stacks(); //<===

    if(forest_type || sort_list) fancy_spew(); 
    else simple_spew(); /* no sort, no forest */
    show_one_proc((proc_t *)-1,format_list); /* no output yet? */

    procps_pids_unref(&Pids_info);
    return 0;
}

finalize_stacks是一个……基本由宏组成的函数。中间重复的宏太多了我就删掉了。

static void finalize_stacks (void)
{
    format_node *f_node;
    sort_node *s_node;

#if (PIDSITEMS < 60)
# error PIDSITEMS (common.h) should be at least 60!
#endif

    /* first, ensure minimum result structures for items
       which may or may not actually be displayable ... */
    Pids_index = 0;

    // needed by for selections
    chkREL(CMD)
    chkREL(ID_EGID)
    ………………
    chkREL(extra)
    chkREL(noop)

    // now accommodate any results not yet satisfied
    f_node = format_list;
    while (f_node) {
        (*f_node->pr)(NULL, NULL);
        f_node = f_node->next;
    }
    s_node = sort_list;
    while (s_node) {
        if (s_node->xe) (*s_node->xe)(NULL, NULL);
        s_node = s_node->next;
    }

    procps_pids_reset(Pids_info, Pids_items, Pids_index);
}

其中,chkREL的定义如下:

#define namREL(e) rel_ ## e
#define makEXT(e) extern int namREL(e);
#define makREL(e) int namREL(e) = -1;
#define chkREL(e) if (namREL(e) < 0) { \
      Pids_items[Pids_index] = PIDS_ ## e; \
      namREL(e) = (Pids_index < PIDSITEMS) ? Pids_index++ : rel_noop; }

展开一下就是:

   if(rel_XX < 0) {
     Pids_items[Pids_index] = PIDS_XX;
     rel_XX = (Pids_index < PIDSITEMS) ? Pids_index++ : rel_noop;
   }

chkREL实际做的事情就是初始化Pids_items中各不同的rel_XXX项。然后,对format_list中的每一项,都调用其pr()来处理。pr其实就是print函数,调用snprintf向其outbuf来输出内容。然后,对sort_list中的每一项,调用其xe()来处理。最后,调用procps_pids_reset。之前看过一次就不再重复了。

回到main中,下一个函数是fancy_spew,当然仅当开启forest_type / sort_list后才调用。

    if(forest_type || sort_list) fancy_spew();  //<---
    else simple_spew(); /* no sort, no forest */
    show_one_proc((proc_t *)-1,format_list); /* no output yet? */

    procps_pids_unref(&Pids_info);
    return 0;
}

fancy_spew定义如下:

/***** sorted or forest */
static void fancy_spew(void) {
    struct pids_fetch *pidread;
    enum pids_fetch_type which;
    proc_t *buf;
    int i, n = 0;

    which = (thread_flags & TF_loose_tasks)
            ? PIDS_FETCH_THREADS_TOO : PIDS_FETCH_TASKS_ONLY;

    pidread = procps_pids_reap(Pids_info, which);
    if (!pidread || !pidread->counts->total) {
        fprintf(stderr, _("fatal library error, reap\n"));
        exit(EXIT_FAILURE);
    }
    processes = xcalloc(pidread->counts->total, sizeof(void*));
    for (i = 0; i < pidread->counts->total; i++) {
        buf = pidread->stacks[i];
        value_this_proc_pcpu(buf);
        if (want_this_proc(buf))
            processes[n++] = buf;
    }
    if (n) {
        if(forest_type) prep_forest_sort();
        while(sort_list) {
            procps_pids_sort(Pids_info, processes, n, sort_list->sr, sort_list->reverse);
            sort_list = sort_list->next;
        }
        if(forest_type) show_forest(n);
        else show_proc_array(n);
    }
    free(processes);
}

调用的第一个函数是procps_pids_reap。这个函数是一个重要的信息处理函数。它调用pids_oldproc_open。

/* procps_pids_reap():
 *
 * Harvest all the available tasks/threads and provide the result
 * stacks along with a summary of the information gathered.
 *
 * Returns: pointer to a pids_fetch struct on success, NULL on error.
 */
PROCPS_EXPORT struct pids_fetch *procps_pids_reap (
    struct pids_info *info,
    enum pids_fetch_type which)
{
    int rc;

    errno = EINVAL;
    if (info == NULL)
        return NULL;
    if (which != PIDS_FETCH_TASKS_ONLY && which != PIDS_FETCH_THREADS_TOO)
        return NULL;
    /* with items & numitems technically optional at 'new' time, it's
       expected 'reset' will have been called -- but just in case ... */
    if (!info->curitems)
        return NULL;
    errno = 0;

    if (!pids_oldproc_open(&info->fetch_PT, info->oldflags))
        return NULL;
    info->read_something = which ? readeither : readproc;

    rc = pids_stacks_fetch(info);

    pids_oldproc_close(&info->fetch_PT);
    // we better have found at least 1 pid
    return (rc > 0) ? &info->fetch.results : NULL;
} // end: procps_pids_reap

pids_oldproc_open定义如下,重要的一眼就可以看出来,openproc函数。

static inline int pids_oldproc_open (
    PROCTAB **this,
    unsigned flags,
    ...)
{
    va_list vl;
    int *ids;
    int num = 0;

    if (*this == NULL) {
        va_start(vl, flags);
        ids = va_arg(vl, int*);
        if (flags & PROC_UID) num = va_arg(vl, int);
        va_end(vl);
        if (NULL == (*this = openproc(flags, ids, num)))
            return 0;
    }
    return 1;
} // end: pids_oldproc_open

openproc的定义如下,函数比较长,重要的内容我们分段切入阅读。

// initiate a process table scan
PROCTAB *openproc(unsigned flags, ...) {
    va_list ap;
    struct stat sbuf;
    static __thread int did_stat;
    PROCTAB *PT = calloc(1, sizeof(PROCTAB));

    if (!PT)
        return NULL;
    if (!did_stat) {
        task_dir_missing = stat("/proc/self/task", &sbuf);
        did_stat = 1;
    }

这里注册一些处理函数。

    PT->taskdir = NULL;
    PT->taskdir_user = -1;
    PT->taskfinder = simple_nexttid;
    PT->taskreader = simple_readtask;

    PT->reader = simple_readproc;
    if (flags & PROC_PID) {
        PT->procfs = NULL;
        PT->finder = listed_nextpid;
    } else {
        PT->procfs = opendir("/proc");
        if (!PT->procfs) {
            free(PT);
            return NULL;
        }
        PT->finder = simple_nextpid;
    }
    PT->flags = flags;

如果传入的内容包含一组pid/uid则这里读取它们。我们现在还没遇到这个情况,暂且不理。

    va_start(ap, flags);
    if (flags & PROC_PID)
        PT->pids = va_arg(ap, pid_t*);
    else if (flags & PROC_UID) {
        PT->uids = va_arg(ap, uid_t*);
        PT->nuid = va_arg(ap, int);
    }
    va_end(ap);

MAX_BUFSZ为1024 * 64 * 2字节。这里初始化src_buffer和dst_buffer(都是全局变量)。

    if (!src_buffer
            && !(src_buffer = malloc(MAX_BUFSZ))) {
        closedir(PT->procfs);
        free(PT);
        return NULL;
    }
    if (!dst_buffer
            && !(dst_buffer = malloc(MAX_BUFSZ))) {
        closedir(PT->procfs);
        free(src_buffer);
        free(PT);
        return NULL;
    }

    return PT;
}

两个buffer已经申请完成,回到上上一层的procps_pids_reap中。下一个调用的函数是pids_stacks_fetch。其代码如下:

static int pids_stacks_fetch (
    struct pids_info *info)
{
#define n_alloc  info->fetch.n_alloc
#define n_inuse  info->fetch.n_inuse
#define n_saved  info->fetch.n_alloc_save
    struct stacks_extent *ext;

最前方是一堆初始化的,这里先懒得看了,有需要后面再返回来阅读。STACKS_INIT的值是1024。

    // initialize stuff -----------------------------------
    if (!info->fetch.anchor) {
        if (!(info->fetch.anchor = calloc(STACKS_INIT, sizeof(void *))))
            return -1;
        if (!(ext = pids_stacks_alloc(info, STACKS_INIT)))
            return -1;       // here, errno was set to ENOMEM
        memcpy(info->fetch.anchor, ext->stacks, sizeof(void *) * STACKS_INIT);
        n_alloc = STACKS_INIT;
    }
    pids_toggle_history(info);
    memset(&info->fetch.counts, 0, sizeof(struct pids_counts));

之后就到具体的处理函数了。info->read_something由procps_pids_reap设置,如果fetch的是PIDS_FETCH_TASKS_ONLY则read_something是readeither,否则是readproc。这个标志由thread_flags决定(fancy_spew中设置)。

    // iterate stuff --------------------------------------
    n_inuse = 0;
    while (info->read_something(info->fetch_PT, &info->fetch_proc)) {

readeither,其代码如下。调用的函数也如注释中描述的那样,是一堆simple_函数。

//////////////////////////////////////////////////////////////////////////////////
// readeither: return a pointer to a proc_t filled with requested info about
// the next unique process or task available.  If no more are available,
// return a null pointer (boolean false).
proc_t *readeither (PROCTAB *restrict const PT, proc_t *restrict x) {
    static __thread proc_t skel_p;    // skeleton proc_t, only uses tid + tgid
    static __thread proc_t *new_p;    // for process/task transitions
    static __thread int canary, leader;
    char path[PROCPATHLEN];
    proc_t *ret;

    free_acquired(x);

    if (new_p) {
        if (new_p->tid != canary) new_p = NULL;
        goto next_task;
    }

next_proc:
    new_p = NULL;
    for (;;) {
        if (errno == ENOMEM) goto end_procs;
        // fills in the PT->path, plus skel_p.tid and skel_p.tgid
        if (!PT->finder(PT,&skel_p)) goto end_procs;       // simple_nextpid
        leader = skel_p.tid;
        if (!task_dir_missing) break;
        if ((ret = PT->reader(PT,x))) return ret;          // simple_readproc
    }

next_task:
    // fills in our path, plus x->tid and x->tgid
    if (!(PT->taskfinder(PT,&skel_p,x,path)))              // simple_nexttid
        goto next_proc;
    /* to avoid loss of some thread group leader data,
       we must check its base dir, not its 'task' dir! */
    if (x->tid == leader) ret = PT->reader(PT,x);          // simple_readproc
    else ret = PT->taskreader(PT,x,path);                  // simple_readtask
    if (!ret) goto next_proc;
    if (!new_p) {
        new_p = ret;
        canary = new_p->tid;
    }
    return ret;

end_procs:
    return NULL;
}

我们随便抽几个例子看一下。首先是simple_nextpid。它在/proc下读取下一个文件夹。

//////////////////////////////////////////////////////////////////////////////////
// This finds processes in /proc in the traditional way.
// Return non-zero on success.
static int simple_nextpid(PROCTAB *restrict const PT, proc_t *restrict const p) {
static __thread struct dirent *ent; /* dirent handle */
char *restrict const path = PT->path;
for (;;) {
ent = readdir(PT->procfs);
if(!ent || !ent->d_name[0]) return 0;
if(*ent->d_name > '0' && *ent->d_name <= '9') break;
}
p->tgid = strtoul(ent->d_name, NULL, 10);
p->tid = p->tgid;
snprintf(path, PROCPATHLEN, "/proc/%s", ent->d_name);
return 1;
}

而具体处理/proc信息的是simple_readproc,我们依旧分解着来读。

//////////////////////////////////////////////////////////////////////////////////
// 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) {
    static __thread struct utlbuf_s ub = { NULL, 0 };    // buf for stat,statm,status
    static __thread struct stat sb;     // stat() buffer
    char *restrict const path = PT->path;
    unsigned flags = PT->flags;
    int rc = 0;

    if (stat(path, &sb) == -1)                  /* no such dirent (anymore) */
        goto next_proc;

    if ((flags & PROC_UID) && !XinLN(uid_t, sb.st_uid, PT->uids, PT->nuid))
        goto next_proc;                      /* not one of the requested uids */

这里遇到一个XinLN宏,用于测试列表的前N个项目中有没有某个类型的值。这里列表是PT->uids。

/* Test if item X of type T is present in the 0 terminated list L */
#   define XinL(T, X, L) ( {                    \
            T  x = (X), *l = (L);               \
            while (*l && *l != x) l++;          \
            *l == x;                            \
        } )

/* Test if item X of type T is present in the list L of length N */
#   define XinLN(T, X, L, N) ( {                \
            T x = (X), *l = (L);                \
            int i = 0, n = (N);                 \
            while (i < n && l[i] != x) i++;     \
            i < n && l[i] == x;                 \
        } )

回到原来的代码里。很快可以看到一个名为file2str的函数,用于从文件中读取数据,放到ub->buf里。

static int file2str(const char *directory, const char *what, struct utlbuf_s *ub) {
#define buffGRW 1024
    char path[PROCPATHLEN];
    int fd, num, tot_read = 0, len;

    /* on first use we preallocate a buffer of minimum size to emulate
       former 'local static' behavior -- even if this read fails, that
       buffer will likely soon be used for another subdirectory anyway
       ( besides, with the calloc call we will never need use memcpy ) */
    if (ub->buf) ub->buf[0] = '\0';
    else {
        ub->buf = calloc(1, (ub->siz = buffGRW));
        if (!ub->buf) return -1;
    }
    len = snprintf(path, sizeof path, "%s/%s", directory, what);
    if (len <= 0 || (size_t)len >= sizeof path) return -1;
    if (-1 == (fd = open(path, O_RDONLY, 0))) return -1;
    while (0 < (num = read(fd, ub->buf + tot_read, ub->siz - tot_read))) {
        tot_read += num;
        if (tot_read < ub->siz) break;
        if (ub->siz >= INT_MAX - buffGRW) {
            tot_read--;
            break;
        }
        if (!(ub->buf = realloc(ub->buf, (ub->siz += buffGRW)))) {
            close(fd);
            return -1;
        }
    };
    ub->buf[tot_read] = '\0';
    close(fd);
    if (tot_read < 1) return -1;
    return tot_read;
#undef buffGRW
}

回到外层函数,可以看到它读取stat,然后调用stat2proc(其他同)对读取到的数据进行处理。这里的处理都是psproc的核心功能,因此我们会挨个跟踪进去。

    p->euid = sb.st_uid;                        /* need a way to get real uid */
    p->egid = sb.st_gid;                        /* need a way to get real gid */

首先是stat2proc。stat文件形式类似:

$ cat /proc/13/stat
13 (bash) S 12 13 12 1025 0 0 0 0 0 0 21 59 120 489 20 0 1 0 34 213183188992 1011 18446744073709551615 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

stat2proc(是的我没贴代码……不然太多了)会去查找( ) 括号中的内容,这个是来自task_struct结构的进程名,最长只有15字节。为了处理进程名中的特殊字符,它还会对其进行简单替换,然后对剩余内容进行扫描(sscanf)并保存到结构体中。

    if (flags & PROC_FILLSTAT) {                // read /proc/#/stat
        if (file2str(path, "stat", &ub) == -1)
            goto next_proc;
        rc += stat2proc(ub.buf, p);
    }

接下来是io的内容,一个sscanf解决。

    if (flags & PROC_FILLIO) {                  // read /proc/#/io
        if (file2str(path, "io", &ub) != -1)
            io2proc(ub.buf, p);
    }

然后是读取smaps,不过是从smaps_rollup读取。这个文件在哪个系统里有我目前还不清楚。它解析该文件,然后把每一项都创建一个object存起来。

    if (flags & PROC_FILLSMAPS) {               // read /proc/#/smaps_rollup
        if (file2str(path, "smaps_rollup", &ub) != -1)
            smaps2proc(ub.buf, p);
    }

statm,一样是sscanf读取。

    if (flags & PROC_FILLMEM) {                 // read /proc/#/statm
        if (file2str(path, "statm", &ub) != -1)
            statm2proc(ub.buf, p);
    }

status,一样。pwcache_get_user调用getpwuid来获取信息。如果用户名过长,则只复制uid部分。

    if (flags & PROC_FILLSTATUS) {              // read /proc/#/status
        if (file2str(path, "status", &ub) != -1) {
            rc += status2proc(ub.buf, p, 1);
            if (flags & (PROC_FILL_SUPGRP & ~PROC_FILLSTATUS))
                rc += supgrps_from_supgids(p);
            if (flags & (PROC_FILL_OUSERS & ~PROC_FILLSTATUS)) {
                p->ruser = pwcache_get_user(p->ruid);
                p->suser = pwcache_get_user(p->suid);
                p->fuser = pwcache_get_user(p->fuid);
            }
            if (flags & (PROC_FILL_OGROUPS & ~PROC_FILLSTATUS)) {
                p->rgroup = pwcache_get_group(p->rgid);
                p->sgroup = pwcache_get_group(p->sgid);
                p->fgroup = pwcache_get_group(p->fgid);
            }
        }
    }

    // if multithreaded, some values are crap
    if(p->nlwp > 1)
        p->wchan = ~0ul;

    /* some number->text resolving which is time consuming */
    /* ( names are cached, so memcpy to arrays was silly ) */
    if (flags & PROC_FILLUSR)
        p->euser = pwcache_get_user(p->euid);
    if (flags & PROC_FILLGRP)
        p->egroup = pwcache_get_group(p->egid);

继续,environ部分。首先仍然是读取environ文件,但是如果失败了,则调用vectorize_dash_rc解析p->environ_v。

    if (flags & PROC_FILLENV)                   // read /proc/#/environ
        if (!(p->environ_v = file2strvec(path, "environ")))
            rc += vectorize_dash_rc(&p->environ_v);
    if (flags & PROC_EDITENVRCVT)
        rc += fill_environ_cvt(path, p);

这里出现了一个新函数,vectorize_dash_rc,把"-"转为一个“vector元素”保存在*vec中。

// This littl' guy just serves those true vectorized fields
// ( when a /proc source field didn't exist )
static int vectorize_dash_rc (char ***vec) {
    if (!(*vec = vectorize_this_str("-")))
        return 1;
    return 0;
}
char **vectorize_this_str (const char *src) {
#define pSZ  (sizeof(char*))
    char *cpy, **vec;
    size_t adj, tot;

    tot = strlen(src) + 1;                       // prep for our vectors
    if (tot < 1 || tot >= INT_MAX) tot = INT_MAX-1; // integer overflow?
    adj = (pSZ-1) - ((tot + pSZ-1) & (pSZ-1));   // calc alignment bytes
    cpy = calloc(1, tot + adj + (2 * pSZ));      // get new larger buffer
    if (!cpy) return NULL;                       // oops, looks like ENOMEM
    snprintf(cpy, tot, "%s", src);               // duplicate their string
    vec = (char**)(cpy + tot + adj);             // prep pointer to pointers
    *vec = cpy;                                  // point 1st vector to string
    *(vec+1) = NULL;                             // null ptr 'list' delimit
    return vec;                                  // ==> free(*vec) to dealloc
#undef pSZ
}

最后回来调用fill_environ_cvt。

// This routine reads an 'environ' for the designated proc_t and
// guarantees the caller a valid proc_t.environ pointer.
static int fill_environ_cvt (const char *directory, proc_t *restrict p) {
    dst_buffer[0] = '\0';
    if (read_unvectored(src_buffer, MAX_BUFSZ, directory, "environ", ' '))
        escape_str(dst_buffer, src_buffer, MAX_BUFSZ);
    p->environ = strdup(dst_buffer[0] ? dst_buffer : "-");
    if (!p->environ)
        return 1;
    return 0;
}

它调用read_unvectored来解析数据。它的前半部分从文件中读取数据(这个单独封装成一个函数不好吗……重复看到好多次了)。如果出现“\n”或者“\0”,则改为“sep”(最后一个参数)。

// this is the former under utilized 'read_cmdline', which has been
// generalized in support of these new libproc flags:
//     PROC_EDITCGRPCVT, PROC_EDITCMDLCVT and PROC_EDITENVRCVT
static int read_unvectored(char *restrict const dst, unsigned sz, const char *whom, const char *what, char sep) {
    char path[PROCPATHLEN];
    int fd, len;
    unsigned n = 0;

    if(sz <= 0) return 0;
    if(sz >= INT_MAX) sz = INT_MAX-1;
    dst[0] = '\0';

    len = snprintf(path, sizeof(path), "%s/%s", whom, what);
    if(len <= 0 || (size_t)len >= sizeof(path)) return 0;
    fd = open(path, O_RDONLY);
    if(fd==-1) return 0;

    for(;;) {
        ssize_t r = read(fd,dst+n,sz-n);
        if(r==-1) {
            if(errno==EINTR) continue;
            break;
        }
        if(r<=0) break;  // EOF
        n += r;
        if(n==sz) {      // filled the buffer
            --n;         // make room for '\0'
            break;
        }
    }
    close(fd);
    if(n) {
        unsigned i = n;
        while(i && dst[i-1]=='\0') --i; // skip trailing zeroes
        while(i--)
            if(dst[i]=='\n' || dst[i]=='\0') dst[i]=sep;
        if(dst[n-1]==' ') dst[n-1]='\0';
    }
    dst[n] = '\0';
    return n;
}

处理结束后调用escape_str。MAX_BUFSZ是1024642。escape_str定义如下,用于标准化字符串。

static inline void esc_all (unsigned char *str) {
    unsigned char c;

    // if bad locale/corrupt str, replace non-printing stuff
    while (*str) {
        if ((c = ESC_tab[*str]) != '|')
            *str = c;
        ++str;
    }
}

static inline void esc_ctl (unsigned char *str, int len) {
    int i, n;

    for (i = 0; i < len; ) {
        // even with a proper locale, strings might be corrupt
        if ((n = UTF_tab[*str]) < 0 || i + n > len) {
            esc_all(str);
            return;
        }
        // and eliminate those non-printing control characters
        if (*str < 0x20 || *str == 0x7f)
            *str = '?';
        str += n;
        i += n;
    }
}

int escape_str (unsigned char *dst, const unsigned char *src, int bufsize) {
    static __thread int utf_sw = 0;
    int n;

    if (utf_sw == 0) {
        char *enc = nl_langinfo(CODESET);
        utf_sw = enc && strcasecmp(enc, "UTF-8") == 0 ? 1 : -1;
    }
    SECURE_ESCAPE_ARGS(dst, bufsize);
    n = snprintf(dst, bufsize, "%s", src);
    if (n < 0) {
        *dst = '\0';
        return 0;
    }
    if (n >= bufsize) n = bufsize-1;
    if (utf_sw < 0)
        esc_all(dst);
    else
        esc_ctl(dst, n);
    return n;
}

下一行,类似的做法,只不过处理cmdline。

    if (flags & PROC_FILLARG)                   // read /proc/#/cmdline
        if (!(p->cmdline_v = file2strvec(path, "cmdline")))
            rc += vectorize_dash_rc(&p->cmdline_v);
    if (flags & PROC_EDITCMDLCVT)
        rc += fill_cmdline_cvt(path, p);

fill_cmdline_cvt稍有不同。

// This routine reads a 'cmdline' for the designated proc_t, "escapes"
// the result into a single string while guaranteeing the caller a
// valid proc_t.cmdline pointer.
static int fill_cmdline_cvt (const char *directory, proc_t *restrict p) {
#define uFLG ( ESC_BRACKETS | ESC_DEFUNCT )
    if (read_unvectored(src_buffer, MAX_BUFSZ, directory, "cmdline", ' '))
        escape_str(dst_buffer, src_buffer, MAX_BUFSZ);
    else
        escape_command(dst_buffer, p, MAX_BUFSZ, uFLG);
    p->cmdline = strdup(dst_buffer[0] ? dst_buffer : "?");
    if (!p->cmdline)
        return 1;
    return 0;
#undef uFLG
}

其中escape_command定义如下。这里将数据解析到pp->cmd中。pp是上一层的“p”,最早在stat中被设置。

// Reads /proc/*/stat files, being careful not to trip over processes with
// names like ":-) 1 2 3 4 5 6".
static int stat2proc (const char *S, proc_t *restrict P) {
    char buf[64], raw[64];
    //...............
        if (!P->cmd) {
            num = tmp - S;
            memcpy(raw, S, num);
            raw[num] = '\0';
            escape_str(buf, raw, sizeof(buf));
            if (!(P->cmd = strdup(buf))) return 1;    //<------------
        }



int escape_command (unsigned char *outbuf, const proc_t *pp, int bytes, unsigned flags) {
    int overhead = 0;
    int end = 0;

    if (flags & ESC_BRACKETS)
        overhead += 2;
    if (flags & ESC_DEFUNCT) {
        if (pp->state == 'Z') overhead += 10;    // chars in " <defunct>"
        else flags &= ~ESC_DEFUNCT;
    }
    if (overhead + 1 >= bytes) {
        // if no room for even one byte of the command name
        outbuf[0] = '\0';
        return 0;
    }
    if (flags & ESC_BRACKETS)
        outbuf[end++] = '['; 
    end += escape_str(outbuf+end, pp->cmd, bytes-overhead);  //<----从cmd拷贝到outbuf+end。
    // we want "[foo] <defunct>", not "[foo <defunct>]"
    if (flags & ESC_BRACKETS)
        outbuf[end++] = ']'; 
    if (flags & ESC_DEFUNCT) {
        memcpy(outbuf+end, " <defunct>", 10);  
        end += 10;
    }
    outbuf[end] = '\0';
    return end;  // bytes, not including the NUL
}

接下来是读取cgroup、oom_score/oom_score_adj/ns/sdlogin

    if ((flags & PROC_FILLCGROUP))              // read /proc/#/cgroup
        if (!(p->cgroup_v = file2strvec(path, "cgroup")))
            rc += vectorize_dash_rc(&p->cgroup_v);
    if (flags & PROC_EDITCGRPCVT)
        rc += fill_cgroup_cvt(path, p);

    if (flags & PROC_FILLOOM) {
        if (file2str(path, "oom_score", &ub) != -1)
            oomscore2proc(ub.buf, p);
        if (file2str(path, "oom_score_adj", &ub) != -1)
            oomadj2proc(ub.buf, p);
    }

    if (flags & PROC_FILLNS)                    // read /proc/#/ns/*
        procps_ns_read_pid(p->tid, &(p->ns));


    if (flags & PROC_FILLSYSTEMD)               // get sd-login.h stuff
        rc += sd2proc(p);

这里读取lxc container相关的内容。

    if (flags & PROC_FILL_LXC)                  // value the lxc name
        p->lxcname = lxc_containers(path);

    if (flags & PROC_FILL_LUID)                 // value the login user id
        p->luid = login_uid(path);

解析exe指向的路径。

    if (flags & PROC_FILL_EXE) {
        if (!(p->exe = readlink_exe(path)))
            rc += 1;
    }

以及最后一小部分内容。

    if (flags & PROC_FILLAUTOGRP)               // value the 2 autogroup fields
        autogroup_fill(path, p);

    if (rc == 0) return p;
    errno = ENOMEM;
next_proc:
    return NULL;
}

读取完成后回到readeither里,另一个重要的是simple_readtask。但它读取的基本一样,不同的是它从/proc/#/task/#下面读。最后重复这一步骤直到所有的被读完。

另一个函数是readproc,是一个简化版readeither它的定义如下

proc_t *readproc(PROCTAB *restrict const PT, proc_t *restrict p) {
    proc_t *ret;

    free_acquired(p);

    for(;;) {
        if (errno == ENOMEM) goto out;
        // fills in the path, plus p->tid and p->tgid
        if (!PT->finder(PT,p)) goto out;

        // go read the process data
        ret = PT->reader(PT,p);
        if(ret) return ret;
    }

out:
    return NULL;
}

回到外侧pids_stacks_fetch中,剩余的基本就是在整理数据,并做展示前的准备工作。

        if (!(n_inuse < n_alloc)) {
            n_alloc += STACKS_GROW;
            if (!(info->fetch.anchor = realloc(info->fetch.anchor, sizeof(void *) * n_alloc))
                    || (!(ext = pids_stacks_alloc(info, STACKS_GROW))))
                return -1;   // here, errno was set to ENOMEM
            memcpy(info->fetch.anchor + n_inuse, ext->stacks, sizeof(void *) * STACKS_GROW);
        }
        if (!pids_proc_tally(info, &info->fetch.counts, &info->fetch_proc))
            return -1;       // here, errno was set to ENOMEM
        if (!pids_assign_results(info, info->fetch.anchor[n_inuse++], &info->fetch_proc))
            return -1;       // here, errno was set to ENOMEM
    }
    /* while the possibility is extremely remote, the readproc.c (read_something) |
       simple_readproc and simple_readtask guys could have encountered this error |
       in which case they would have returned a NULL, thus ending our while loop. | */
    if (errno == ENOMEM)
        return -1;

    // finalize stuff -------------------------------------
    /* note: we go to this trouble of maintaining a duplicate of the consolidated |
             extent stacks addresses represented as our 'anchor' since these ptrs |
             are exposed to a user (um, not that we don't trust 'em or anything). |
             plus, we can NULL delimit these ptrs which we couldn't do otherwise. | */
    if (n_saved < n_inuse + 1) {
        n_saved = n_inuse + 1;
        if (!(info->fetch.results.stacks = realloc(info->fetch.results.stacks, sizeof(void *) * n_saved)))
            return -1;
    }
    memcpy(info->fetch.results.stacks, info->fetch.anchor, sizeof(void *) * n_inuse);
    info->fetch.results.stacks[n_inuse] = NULL;

    return n_inuse;     // callers beware, this might be zero !
#undef n_alloc
#undef n_inuse
#undef n_saved
} // end: pids_stacks_fetch

标签:none

添加新评论

captcha
请输入验证码