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