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()。

标签:none

添加新评论

captcha
请输入验证码