psproc源码阅读 - 3
接着上一篇的来,首先这个ARG_SYSV的花括号是真的很风骚,我也是第一次看到把case放在if的花括号里面的。anyway,先看这两个分支共同会到达的部分parse_bsd_option。
case ARG_SYSV:
if(!force_bsd) { /* else go past case ARG_BSD */
err = parse_sysv_option();
break;
case ARG_BSD:
if(force_bsd && !(personality & PER_FORCE_BSD)) return _("way bad");
}
prefer_bsd_defaults = 1;
err = parse_bsd_option();
break;
BSD options的处理由一个大函数完成,这也是我们用ps时常用的语法。开头几句它检查命令的格式是否和设置冲突,然后对flag中每一个字符进行switch case来处理。
/************************* parse BSD options **********************/
static const char *parse_bsd_option(void) {
const char *arg;
const char *err;
flagptr = ps_argv[thisarg]; /* assume we _have_ a '-' */
if(flagptr[0]=='-') {
if(!force_bsd) return _("cannot happen - problem #1");
} else {
flagptr--; /* off beginning, will increment before use */
if(personality & PER_FORCE_BSD) {
if(!force_bsd) return _("cannot happen - problem #2");
} else {
if(force_bsd) return _("second chance parse failed, not BSD or SysV");
}
}
while(*++flagptr) {
switch(*flagptr) {
case '0' ... '9': /* end */
这里的处理没什么新意,挑几个之前没出现过的函数来读一下。
case 'O': /* end */
trace("O like o + defaults, add new columns after PID, also sort\n");
arg=get_opt_arg();
if(!arg) return _("format or sort specification must follow O");
defer_sf_option(arg, SF_B_O);
return NULL; /* can't have any more options */
break;
首先是defer_sf_option。其实出现过了,但是当时我懒得看,现在看一下它在做什么。defer_sf_option是一个中等长度的函数,开头依然是熟悉的初始化,初始化要用到的sf_node结构体。支持的sort & format一共7种,在common.h中定义。
/* sorting & formatting */
/* U,B,G is Unix,BSD,Gnu and then there is the option itself */
#define SF_U_O 1
#define SF_U_o 2
#define SF_B_O 3
#define SF_B_o 4
#define SF_B_m 5 /* overloaded: threads, sort, format */
#define SF_G_sort 6
#define SF_G_format 7
/************ Main parser calls this to save lists for later **********/
/* store data for later and return 1 if arg looks non-standard */
int defer_sf_option(const char *arg, int source) {
sf_node *sfn;
char buf[16];
int dist;
const format_struct *fs;
int need_item = 1;
sfn = xmalloc(sizeof(sf_node));
sfn->sf = strdup(arg);
sfn->sf_code = source;
sfn->s_cooked = NULL;
sfn->f_cooked = NULL;
sfn->next = sf_list;
sf_list = sfn;
if(source == SF_G_sort) have_gnu_sort = 1;
/* Now try to find an excuse to ignore broken Unix98 parsing. */
if(source != SF_U_o) return 1; /* Wonderful! Already non-Unix98. */
do {
switch(*arg) {
case ' ':
case ',':
case '\0': /* no \t\n\r support in Unix98 */
if(need_item) return 1; /* something wrong */
need_item=1;
break;
case '=':
if(need_item) return 1; /* something wrong */
return 0; /* broken Unix98 parsing is required */
default:
if(!need_item) break;
need_item=0;
dist = strcspn(arg,", =");
if(dist>15) return 1; /* something wrong, sort maybe? */
strncpy(buf,arg,dist); /* no '\0' on end */
buf[dist] = '\0'; /* fix that problem */
fs = search_format_array(buf);
if(!fs) return 1; /* invalid spec, macro or sort maybe? */
if(fs->vendor) return 1; /* Wonderful! Legal non-Unix98 spec. */
}
} while (*++arg);
return 0; /* boring, Unix98 is no change */
}
说是有这么多SF格式,实际上它只当场处理SF_G_sort和SF_U_o。其余的直接初始化完sf_node,加到sf_list(全局变量)链表中便结束了。
对SF_U_o,仍然是经典的列表处理,找到项目后,使用search_format_array来搜索对应处理项,如果没找到,或者有fs->vendor(只有U98是0,其余的都是>0的值)返回1。
const format_struct *search_format_array(const char *findme) {
format_struct key;
key.spec = findme;
return bsearch(&key, format_array, format_array_count,
sizeof(format_struct), compare_format_structs
);
}
/* Note: upon conversion to the <pids> API the numerous former sort provisions
for otherwise non-printable fields (pr_nop) have been retained. And,
since the new library can sort on any item, many previously printable
but unsortable fields have now been made sortable. */
/* there are about 211 listed */
/* Many of these are placeholders for unsupported options. */
static const format_struct format_array[] = { /*
.spec .head .pr .sr .width .vendor .flags */
{"%cpu", "%CPU", pr_pcpu, PIDS_extra, 4, BSD, ET|RIGHT}, /*pcpu*/
{"%mem", "%MEM", pr_pmem, PIDS_VM_RSS, 4, BSD, PO|RIGHT}, /*pmem*/
{"_left", "LLLLLLLL", pr_t_left, PIDS_noop, 8, TST, ET|LEFT},
{"_left2", "L2L2L2L2", pr_t_left2, PIDS_noop, 8, TST, ET|LEFT},
{"_right", "RRRRRRRRRRR", pr_t_right, PIDS_noop, 11, TST, ET|RIGHT},
回到parse_bsd_option中。其余的选项大多数是在设置标记位,不再重复了。
case 'X':
trace("X old Linux i386 register format\n");
format_flags |= FF_LX;
break;
case 'Z': /* FreeBSD does MAC like SGI's Irix does it */
trace("Z print security label for Mandatory Access Control.\n");
format_modifiers |= FM_M;
break;
case 'a':
trace("a select all w/tty, including other users\n");
simple_select |= SS_B_a;
break;
case 'c':
trace("c true command name\n");
bsd_c_option = 1;
break;
目前只剩下来最后一个,parse_sysv_option(),想必也不会有什么惊喜。
/***************** parse SysV options, including Unix98 *****************/
static const char *parse_sysv_option(void) {
const char *arg;
const char *err;
flagptr = ps_argv[thisarg];
while(*++flagptr) {
switch(*flagptr) {
case 'A':
trace("-A selects all processes\n");
all_processes = 1;
break;
case 'C': /* end */
trace("-C select by process name\n"); /* Why only HP/UX and us? */
arg=get_opt_arg();
if(!arg) return _("list of command names must follow -C");
err=parse_list(arg, parse_cmd);
if(err) return err;
selection_list->typecode = SEL_COMM;
return NULL; /* can't have any more options */
case 'F': /* DYNIX/ptx -f plus sz,rss,psr=ENG between c and stime */
trace("-F does fuller listing\n");
format_modifiers |= FM_F;
format_flags |= FF_Uf;
unix_f_option = 1; /* does this matter? */
break;
事实也确实如此,这里都是我们看过的函数,不再过多介绍了。
终于看完了parse_all_options的所有内容,回到上一层arg_parse。
int arg_parse(int argc, char *argv[]) {
const char *err = NULL;
const char *err2 = NULL;
ps_argc = argc;
ps_argv = argv;
thisarg = 0;
if(personality & PER_FORCE_BSD) goto try_bsd;
err = parse_all_options(); //<----------
if(err) goto try_bsd;
err = thread_option_check();
if(err) goto try_bsd;
err = process_sf_options();
if(err) goto try_bsd;
err = select_bits_setup();
if(err) goto try_bsd;
看来我们只走了一小步,thread_option_check全是在处理thread_flags这个全局变量,根据之前parse_all_options传入的内容对其进行设置。process_sf_options对参数“o”进行处理,代码比较复杂,我们单独拖出来看一看。
首先其注释表示这个功能是遗留下来的坑,前人挖坑埋后人系列。
/**************************************************************************
* Used to parse option O lists. Option O is shared between
* sorting and formatting. Users may expect one or the other.
* The "broken" flag enables a really bad Unix98 misfeature.
*/
const char *process_sf_options(void) {
sf_node *sf_walk;
if(sf_list) {
const char *err;
err = parse_O_option(sf_list);
if(err) return err;
}
if(format_list) catastrophic_failure(__FILE__, __LINE__, _("bug: must reset the list first"));
第一阶段的代码调用parse_O_option,这玩意儿也是个中型函数。没办法,看一看它是什么。
/*
* Used to parse option O lists. Option O is shared between
* sorting and formatting. Users may expect one or the other.
* Recursion is to preserve original order.
*/
static const char *parse_O_option(sf_node *sfn) {
const char *err; /* error code that could or did happen */
if(sfn->next) {
err = parse_O_option(sfn->next);
if(err) return err;
}
switch(sfn->sf_code) {
case SF_B_o:
case SF_G_format:
case SF_U_o: /*** format ***/
err = format_parse(sfn);
if(!err) already_parsed_format = 1;
break;
case SF_U_O: /*** format ***/
/* Can have -l -f f u... set already_parsed_format like DEC does */
if(already_parsed_format) return _("option -O can not follow other format options");
err = format_parse(sfn);
if(err) return err;
already_parsed_format = 1;
O_wrap(sfn,'u'); /* must wrap user format in default */
break;
case SF_B_O: /*** both ***/
if(have_gnu_sort || already_parsed_sort) err = _("multiple sort options");
else err = verify_short_sort(sfn->sf);
if(!err) { /* success as sorting code */
short_sort_parse(sfn);
already_parsed_sort = 1;
return NULL;
}
if(already_parsed_format) {
err = _("option O is neither first format nor sort order");
break;
}
if(!format_parse(sfn)) { /* if success as format code */
already_parsed_format = 1;
O_wrap(sfn,'b'); /* must wrap user format in default */
return NULL;
}
break;
case SF_G_sort:
case SF_B_m: /*** sort ***/
if(already_parsed_sort) err = _("multiple sort options");
else err = long_sort_parse(sfn);
already_parsed_sort = 1;
break;
default: /*** junk ***/
catastrophic_failure(__FILE__, __LINE__, _("please report this bug"));
}
return err; /* could be NULL */
}
很不妙,一上来就是另一个parser。对G_o、G_format、U_o三个情况而言,进入format_parse。
case SF_B_o:
case SF_G_format:
case SF_U_o: /*** format ***/
err = format_parse(sfn);
if(!err) already_parsed_format = 1;
format_parse定义如下,由一个大型状态机构成。前面的状态机只是检查是否符合语法,并不做其他事情。通过后,开始处理。
/******************************************************************
* Used to parse option AIX field descriptors.
* Put each completed format_node onto the list starting at ->f_cooked
*/
static const char *aix_format_parse(sf_node *sfn) {
char *buf; /* temp copy of arg to hack on */
char *walk;
int items;
/*** sanity check and count items ***/
items = 0;
walk = sfn->sf;
/* state machine */ {
int c;
initial:
c = *walk++;
if(c=='%') goto get_desc;
if(!c) goto looks_ok;
/* get_text: */
items++;
get_more_text:
c = *walk++;
if(c=='%') goto get_desc;
if(c) goto get_more_text;
goto looks_ok;
get_desc:
items++;
c = *walk++;
if(c) goto initial;
return _("improper AIX field descriptor");
looks_ok:
;
}
处理阶段,复制一份sfn->sf,这是带%的命令行。只要不是%%,就交给search_aix_array去搜索对应的列。
/*** sanity check passed ***/
buf = strdup(sfn->sf);
walk = sfn->sf;
while(items--) {
format_node *fnode; /* newly allocated */
format_node *endp; /* for list manipulation */
if(*walk == '%') {
const aix_struct *aix;
walk++;
if(*walk == '%') goto double_percent;
aix = search_aix_array(*walk);
search_aix_array的定义如下:
const aix_struct *search_aix_array(const int findme) {
const aix_struct *walk = aix_array;
while(walk->desc != '~') {
if(walk->desc == findme) return walk;
walk++;
}
return NULL;
}
它搜索的是这样一个数组。
/*************************** AIX formats ********************/
/* Convert AIX format codes to normal format specifiers. */
static const aix_struct aix_array[] = {
{'C', "pcpu", "%CPU"},
{'G', "group", "GROUP"},
{'P', "ppid", "PPID"},
{'U', "user", "USER"},
{'a', "args", "COMMAND"},
{'c', "comm", "COMMAND"},
{'g', "rgroup", "RGROUP"},
{'n', "nice", "NI"},
{'p', "pid", "PID"},
{'r', "pgid", "PGID"},
{'t', "etime", "ELAPSED"},
{'u', "ruser", "RUSER"},
{'x', "time", "TIME"},
{'y', "tty", "TTY"},
{'z', "vsz", "VSZ"},
{'~', "~", "~"} /* NULL would ruin alphabetical order */
};
回到之前的函数。如果找到了,则调用do_one_spec来处理对应的规范和表头。
walk++;
if(!aix) {
free(buf);
return _("unknown AIX field descriptor");
}
fnode = do_one_spec(aix->spec, aix->head);
do_one_spec也是个大函数,定义如下:
/**************** Parse single format specifier *******************/
static format_node *do_one_spec(const char *spec, const char *override) {
const format_struct *fs;
const macro_struct *ms;
fs = search_format_array(spec);
函数一上来就在format_array中找对应的spec(参数1)。之前已经见识过这个format_array了:
static const format_struct format_array[] = { /*
.spec .head .pr .sr .width .vendor .flags */
{"%cpu", "%CPU", pr_pcpu, PIDS_extra, 4, BSD, ET|RIGHT}, /*pcpu*/
{"%mem", "%MEM", pr_pmem, PIDS_VM_RSS, 4, BSD, PO|RIGHT}, /*pmem*/
如果找到,则对其进行处理,生成format_node并返回。
if(fs) {
int w1, w2;
format_node *thisnode;
thisnode = xmalloc(sizeof(format_node));
if(fs->flags & CF_PIDMAX) {
w1 = (int)procps_pid_length();
w2 = strlen(fs->head);
if(w2>w1) w1=w2; // FIXME w/ separate header/body column sizing
} else {
w1 = fs->width;
}
if(override) {
w2 = strlen(override);
thisnode->width = (w1>w2)?w1:w2;
thisnode->name = strdup(override);
} else {
thisnode->width = w1;
thisnode->name = strdup(fs->head);
}
thisnode->pr = fs->pr;
thisnode->vendor = fs->vendor;
thisnode->flags = fs->flags;
thisnode->next = NULL;
return thisnode;
}
format_node的各项解释如下:
1) .pr ,处理函数,处理函数由format_array定义,各项形如:
/* normal %CPU in ##.# format. */
static int pr_pcpu(char *restrict const outbuf, const proc_t *restrict const pp) {
unsigned long long total_time; /* jiffies used by this process */
unsigned pcpu; /* scaled %cpu, 999 means 99.9% */
unsigned long long seconds; /* seconds of process life */
setREL3(TICS_ALL,TICS_ALL_C,TIME_ELAPSED)
pcpu = 0;
if(include_dead_children) total_time = rSv(TICS_ALL_C, ull_int, pp);
else total_time = rSv(TICS_ALL, ull_int, pp);
seconds = rSv(TIME_ELAPSED, ull_int, pp);
if(seconds) pcpu = (total_time * 1000ULL / Hertz) / seconds;
if (pcpu > 999U)
return snprintf(outbuf, COLWID, "%u", pcpu/10U);
return snprintf(outbuf, COLWID, "%u.%u", pcpu/10U, pcpu%10U);
}
2) .vendor ,哪个系统引入的功能。
3) .flags,预设的flag。
4) .next,与其关联的下一个节点(链表)。
如果没有找到format_array,则尝试按macro再次查找。macro array是一组对应的字符映射关系,很像C的宏:
static const macro_struct macro_array[] = {
{"DFMT", "pid,tname,state,cputime,cmd"}, /* Digital's default */
{"DefBSD", "pid,tname,stat,bsdtime,args"}, /* Our BSD default */
{"DefSysV", "pid,tname,time,cmd"}, /* Our SysV default */
将macro展开后,对macro中每个section,调用自己再解析一次。
/* That failed, so try it as a macro. */
ms = search_macro_array(spec);
if(ms) {
format_node *list = NULL;
format_node *newnode;
const char *walk;
int dist;
char buf[16]; /* trust strings will be short (from above, not user) */
walk = ms->head;
while(*walk) {
dist = strcspn(walk, ", ");
strncpy(buf,walk,dist);
buf[dist] = '\0';
newnode = do_one_spec(buf,override); /* call self, assume success */
newnode->next = list;
list = newnode;
walk += dist;
if(*walk) walk++;
}
return list;
}
return NULL; /* bad, spec not found */
}
继续回到上层,把%之前的内容dump出来,保存在fnode中。检查最后一个节点,保存到sfn->f_cooked中,然后退出。
if(!fnode) {
free(buf);
return _("AIX field descriptor processing bug");
}
} else {
size_t len;
len = strcspn(walk, "%");
memcpy(buf,walk,len);
if(0) {
double_percent:
len = 1;
buf[0] = '%';
}
buf[len] = '\0';
walk += len;
fnode = xmalloc(sizeof(format_node));
fnode->width = len < INT_MAX ? len : INT_MAX;
fnode->name = strdup(buf);
fnode->pr = NULL; /* checked for */
fnode->vendor = AIX;
fnode->flags = CF_PRINT_EVERY_TIME;
fnode->next = NULL;
}
endp = fnode;
while(endp->next) endp = endp->next; /* find end */
endp->next = sfn->f_cooked;
sfn->f_cooked = fnode;
}
free(buf);
already_parsed_format = 1;
return NULL;
}
再回到最外面的那层。后面就比较简单了,分别维护两个链表,一个是format_list,一个是sort_list,将二者分类放到不同的链表中。
/* merge formatting info of sf_list into format_list here */
sf_walk = sf_list;
while(sf_walk) {
format_node *fmt_walk;
fmt_walk = sf_walk->f_cooked;
sf_walk->f_cooked = NULL;
while(fmt_walk) { /* put any nodes onto format_list in opposite way */
format_node *travler;
travler = fmt_walk;
fmt_walk = fmt_walk->next;
travler->next = format_list;
format_list = travler;
}
sf_walk = sf_walk->next;
}
/* merge sorting info of sf_list into sort_list here */
sf_walk = sf_list;
while(sf_walk) {
sort_node *srt_walk;
srt_walk = sf_walk->s_cooked;
sf_walk->s_cooked = NULL;
if (srt_walk) {
sort_node *travler = srt_walk;
while (travler->next) travler = travler->next;
travler->next = sort_list;
sort_list = srt_walk;
}
sf_walk = sf_walk->next;
}
并在接下来处理PS_FORMAT环境变量(format_parse),然后重复放到format_list的步骤。
// Get somebody to explain how -L/-T is supposed to interact
// with sorting. Do the threads remain grouped, with sorting
// by process, or do the threads get sorted by themselves?
if(sort_list && (thread_flags&TF_no_sort)) {
return _("tell <procps@freelists.org> what you expected");
}
// If nothing else, try to use $PS_FORMAT before the default.
if(!format_flags && !format_modifiers && !format_list) {
char *tmp;
tmp = getenv("PS_FORMAT"); /* user override kills default */
if(tmp && *tmp) {
const char *err;
sf_node sfn;
if(thread_flags&TF_must_use) return _("tell <procps@freelists.org> what you want (-L/-T, -m/m/H, and $PS_FORMAT)");
sfn.sf = tmp;
sfn.f_cooked = NULL;
err = format_parse(&sfn);
if(!err) {
format_node *fmt_walk;
fmt_walk = sfn.f_cooked;
while(fmt_walk) { /* put any nodes onto format_list in opposite way */
format_node *travler;
travler = fmt_walk;
fmt_walk = fmt_walk->next;
travler->next = format_list;
format_list = travler;
}
return NULL;
}
// FIXME: prove that this won't be hit on valid bogus-BSD options
fprintf(stderr, _("warning: $PS_FORMAT ignored. (%s)\n"), err);
}
}
如果有指定format_flags,则同样处理它。
if(format_list) {
if(format_flags) return _("conflicting format options");
if(format_modifiers) return _("can not use output modifiers with user-defined output");
if(thread_flags&TF_must_use) return _("-L/-T with H/m/-m and -o/-O/o/O is nonsense");
return NULL;
}
do {
const char *spec;
switch(format_flags) {
default:
return _("conflicting format options");
/* These can be NULL, which enables SysV list generation code. */
case 0:
spec=NULL;
break;
……
case FF_Lm:
spec="OL_m";
break;
/* This is the sole FLASK security option. */
case FF_Fc:
spec="FLASK_context";
break;
} /* end switch(format_flags) */
// not just for case 0, since sysv_l_format and such may be NULL
if(!spec) return generate_sysv_list();
do {
format_node *fmt_walk;
fmt_walk = do_one_spec(spec, NULL); /* use override "" for no headers */
while(fmt_walk) { /* put any nodes onto format_list in opposite way */
format_node *travler;
travler = fmt_walk;
fmt_walk = fmt_walk->next;
travler->next = format_list;
format_list = travler;
}
} while(0);
} while(0);
接下来,对format_modifiers进行处理。fmt_add_after、fmt_delete将字符串与format_list的项目name属性做对比,并添加项目/删除项目。
do {
format_node *fn;
if(format_modifiers & FM_j) {
fn = do_one_spec("pgid", NULL);
if(!fmt_add_after("PPID", fn)) if(!fmt_add_after("PID", fn))
catastrophic_failure(__FILE__, __LINE__, _("internal error: no PID or PPID for -j option"));
fn = do_one_spec("sid", NULL);
if(!fmt_add_after("PGID", fn)) return _("lost my PGID");
}
if(format_modifiers & FM_y) {
/* TODO: check for failure to do something, and complain if so */
fmt_delete("F");
fn = do_one_spec("rss", NULL);
if(fmt_add_after("ADDR", fn)) fmt_delete("ADDR");
}
if(format_modifiers & FM_c) {
fmt_delete("%CPU");
fmt_delete("CPU");
fmt_delete("CP");
fmt_delete("C");
fmt_delete("NI");
fn = do_one_spec("class", NULL);
if(!fmt_add_after("PRI", fn))
catastrophic_failure(__FILE__, __LINE__, _("internal error: no PRI for -c option"));
fmt_delete("PRI"); /* we want a different one */
fn = do_one_spec("pri", NULL);
if(!fmt_add_after("CLS", fn)) return _("lost my CLS");
}
if(thread_flags & TF_U_T) {
fn = do_one_spec("spid", NULL);
if(!fmt_add_after("PID", fn) && (thread_flags&TF_must_use))
return _("-T with H/-m/m but no PID for SPID to follow");
}
if(thread_flags & TF_U_L) {
fn = do_one_spec("lwp", NULL);
if(fmt_add_after("SID", fn)) goto did_lwp;
if(fmt_add_after("SESS", fn)) goto did_lwp;
if(fmt_add_after("PGID", fn)) goto did_lwp;
if(fmt_add_after("PGRP", fn)) goto did_lwp;
if(fmt_add_after("PPID", fn)) goto did_lwp;
if(fmt_add_after("PID", fn)) goto did_lwp;
if(thread_flags&TF_must_use)
return _("-L with H/-m/m but no PID/PGID/SID/SESS for NLWP to follow");
did_lwp:
fn = do_one_spec("nlwp", NULL);
fmt_add_after("%CPU", fn);
}
if(format_modifiers & FM_M) { // Mandatory Access Control, IRIX style
fn = do_one_spec("label", NULL);
fn->next=format_list;
format_list=fn;
}
/* Do personality-specific translations not covered by format_flags.
* Generally, these only get hit when personality overrides unix output.
* That (mostly?) means the Digital and Debian personalities.
*/
if((personality & PER_ZAP_ADDR) && (format_flags & FF_Ul)) {
fn = do_one_spec("sgi_p", NULL);
if(fmt_add_after("ADDR", fn)) fmt_delete("ADDR");
}
if((personality & PER_SANE_USER) && (format_flags & FF_Uf)) {
fn = do_one_spec("user", NULL);
if(fmt_add_after("UID", fn)) fmt_delete("UID");
}
} while(0);
return NULL;
}
终于回到最开始的arg_parse。
err = parse_all_options();
if(err) goto try_bsd;
err = thread_option_check();
if(err) goto try_bsd;
err = process_sf_options(); //<------
if(err) goto try_bsd;
err = select_bits_setup();
if(err) goto try_bsd;
我们接下来是select_bits_setup。这是一个设置select_bits全局变量的函数,不介绍了,全是魔法数字,后面碰着再看。这个函数调用完成后,arg_parse也结束了。回到最初的起点main()。
reset_global(); /* must be before parser */
arg_parse(argc,argv); //<--------在这里
/* check for invalid combination of arguments */
arg_check_conflicts();
/* arg_show(); */
trace("screen is %ux%u\n",screen_cols,screen_rows);
/* printf("sizeof(proc_t) is %d.\n", sizeof(proc_t)); */
trace("======= ps output follows =======\n");
init_output(); /* must be between parser and output */
接下来的arg_check_conflicts没有什么惊喜,只是检查有没有冲突的参数。进入init_output。
void init_output(void)
{
int outbuf_pages;
char *outbuf;
// add page_size-1 to round up
outbuf_pages = (OUTBUF_SIZE+SPACE_AMOUNT+page_size-1)/page_size;
outbuf = mmap(
0,
page_size * (outbuf_pages+1), // 1 more, for guard page at high addresses
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS,
-1,
0);
if(outbuf == MAP_FAILED)
catastrophic_failure(__FILE__, __LINE__, _("please report this bug"));
memset(outbuf, ' ', SPACE_AMOUNT);
if(SPACE_AMOUNT==page_size)
mprotect(outbuf, page_size, PROT_READ);
mprotect(outbuf + page_size*outbuf_pages, page_size, PROT_NONE); // guard page
saved_outbuf = outbuf + SPACE_AMOUNT;
// available space: page_size*outbuf_pages-SPACE_AMOUNT
seconds_since_1970 = time(NULL);
check_header_width();
}
OUTBUF_SIZE是2 * 64 * 1024, SPACE_AMOUNT是144,page_size是分页大小,一般认为是4096。所以outbuf实际上是由33页构成(如果page_size=4096)。mmap申请34页(139,264字节)。前SPACE_AMOUNT字节设置为空格,最后一页无权限。
回到main中,还剩最后一点点代码:
lists_and_needs(); //<===
finalize_stacks();
if(forest_type || sort_list) fancy_spew(); /* sort or forest */
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;
}
继续看lists_and_needs。check_headers用于检查有多少个header(遍历format_list中有name的、有pr项的并计数)。然后后面的代码用于对列表中的项目需求进行处理,并修改一部分类型节点的pr值。
/***** munge lists and determine final needs */
static void lists_and_needs(void) {
check_headers();
// only care about the difference when showing both
if(thread_flags & TF_show_both) {
format_node pfn, tfn; // junk, to handle special case at begin of list
format_node *walk = format_list;
format_node *p_end = &pfn;
format_node *t_end = &tfn;
while(walk) {
format_node *new = xmalloc(sizeof(format_node));
memcpy(new,walk,sizeof(format_node));
p_end->next = walk;
t_end->next = new;
p_end = walk;
t_end = new;
switch(walk->flags & CF_PRINT_MASK) {
case CF_PRINT_THREAD_ONLY:
p_end->pr = pr_nop;
break;
case CF_PRINT_PROCESS_ONLY:
t_end->pr = pr_nop;
break;
default:
catastrophic_failure(__FILE__, __LINE__, _("please report this bug"));
// FALL THROUGH
case CF_PRINT_AS_NEEDED:
case CF_PRINT_EVERY_TIME:
break;
}
walk = walk->next;
}
t_end->next = NULL;
p_end->next = NULL;
proc_format_list = pfn.next;
task_format_list = tfn.next;
} else {
proc_format_list = format_list;
task_format_list = format_list;
}
}
回到main,阅读finalize_stacks()。