LXR | KVM | PM | Time | Interrupt | Systems Performance | Bootup Optimization

/proc/<pid>/status简要分析

关键词:status、virtual memory、signal、capability、context switch等等。

 

每个进程/线程都有自己私有状态,在/proc/<pid>status中体现。

了解status中每项的含义,有助于问题定位时发现问题。

下面结合status在内中函数proc_pid_status()对每一项进行简单的了解,然后结合一个示例进行分析。

1. status在内核中的解释

status当前进程/线程的名称、运行状态、pid、信号、权限、cpu使用范围、进程切换等等信息。

int proc_pid_status(struct seq_file *m, struct pid_namespace *ns,
            struct pid *pid, struct task_struct *task)
{
    struct mm_struct *mm = get_task_mm(task);

    task_name(m, task);
    task_state(m, ns, pid, task);

    if (mm) {
        task_mem(m, mm);
        mmput(mm);
    }
    task_sig(m, task);
    task_cap(m, task);
    task_seccomp(m, task);
    task_cpus_allowed(m, task);
    cpuset_task_status_allowed(m, task);
    task_context_switch_counts(m, task);
    return 0;
}

static inline void task_state(struct seq_file *m, struct pid_namespace *ns,
                struct pid *pid, struct task_struct *p)
{
    struct user_namespace *user_ns = seq_user_ns(m);
    struct group_info *group_info;
    int g, umask;
    struct task_struct *tracer;
    const struct cred *cred;
    pid_t ppid, tpid = 0, tgid, ngid;
    unsigned int max_fds = 0;

    rcu_read_lock();
    ppid = pid_alive(p) ?
        task_tgid_nr_ns(rcu_dereference(p->real_parent), ns) : 0;

    tracer = ptrace_parent(p);
    if (tracer)
        tpid = task_pid_nr_ns(tracer, ns);

    tgid = task_tgid_nr_ns(p, ns);
    ngid = task_numa_group_id(p);
    cred = get_task_cred(p);

    umask = get_task_umask(p);
    if (umask >= 0)
        seq_printf(m, "Umask:\t%#04o\n", umask);

    task_lock(p);
    if (p->files)
        max_fds = files_fdtable(p->files)->max_fds;
    task_unlock(p);
    rcu_read_unlock();

    seq_printf(m, "State:\t%s", get_task_state(p));-----------------------------"R (running)"/"S (sleeping)"/"D (disk sleep)"/"T (stopped)"/"t (tracing stop)"/"X (dead)"/"Z (zombie)"
D is sleeping in an uninterruptible wait.
    seq_put_decimal_ull(m, "\nTgid:\t", tgid);----------------------------------线程组ID,也就是线程组leader的进程id,等于pid。
    seq_put_decimal_ull(m, "\nNgid:\t", ngid);----------------------------------进程所属的NUMA id。
    seq_put_decimal_ull(m, "\nPid:\t", pid_nr_ns(pid, ns));---------------------进程pid。
    seq_put_decimal_ull(m, "\nPPid:\t", ppid);----------------------------------进程的父进程pid。
    seq_put_decimal_ull(m, "\nTracerPid:\t", tpid);-----------------------------ptrace对应的进程id。
    seq_put_decimal_ull(m, "\nUid:\t", from_kuid_munged(user_ns, cred->uid));---实际用户id,指的是进程执行者是谁。
    seq_put_decimal_ull(m, "\t", from_kuid_munged(user_ns, cred->euid));--------有效用户id,指进程执行时对系统资源的访问权限。
    seq_put_decimal_ull(m, "\t", from_kuid_munged(user_ns, cred->suid));--------保存设置用户id,是进程刚开始执行时euid的副本。在执行exec调用之后能重新恢复原来的euid。
    seq_put_decimal_ull(m, "\t", from_kuid_munged(user_ns, cred->fsuid));-------通常等于euid,用于访问文件时检查访问权限。
    seq_put_decimal_ull(m, "\nGid:\t", from_kgid_munged(user_ns, cred->gid));
    seq_put_decimal_ull(m, "\t", from_kgid_munged(user_ns, cred->egid));
    seq_put_decimal_ull(m, "\t", from_kgid_munged(user_ns, cred->sgid));
    seq_put_decimal_ull(m, "\t", from_kgid_munged(user_ns, cred->fsgid));
    seq_put_decimal_ull(m, "\nFDSize:\t", max_fds);-----------------------------进程已打开最大文件描述符数。这个值不是文件描述符的上限,也不是实际使用中的文件描述符数量。以32递增。

    seq_puts(m, "\nGroups:\t");-------------------------------------------------Supplementary group list.
    group_info = cred->group_info;
    for (g = 0; g < group_info->ngroups; g++)
        seq_put_decimal_ull(m, g ? " " : "",
                from_kgid_munged(user_ns, group_info->gid[g]));
    put_cred(cred);
    /* Trailing space shouldn't have been added in the first place. */
    seq_putc(m, ' ');
...
    seq_putc(m, '\n');
}

void task_mem(struct seq_file *m, struct mm_struct *mm)
{
    unsigned long text, lib, swap, ptes, pmds, anon, file, shmem;
    unsigned long hiwater_vm, total_vm, hiwater_rss, total_rss;

    anon = get_mm_counter(mm, MM_ANONPAGES);
    file = get_mm_counter(mm, MM_FILEPAGES);
    shmem = get_mm_counter(mm, MM_SHMEMPAGES);

    hiwater_vm = total_vm = mm->total_vm;
    if (hiwater_vm < mm->hiwater_vm)
        hiwater_vm = mm->hiwater_vm;
    hiwater_rss = total_rss = anon + file + shmem;
    if (hiwater_rss < mm->hiwater_rss)
        hiwater_rss = mm->hiwater_rss;

    text = (PAGE_ALIGN(mm->end_code) - (mm->start_code & PAGE_MASK)) >> 10;
    lib = (mm->exec_vm << (PAGE_SHIFT-10)) - text;
    swap = get_mm_counter(mm, MM_SWAPENTS);
    ptes = PTRS_PER_PTE * sizeof(pte_t) * atomic_long_read(&mm->nr_ptes);
    pmds = PTRS_PER_PMD * sizeof(pmd_t) * mm_nr_pmds(mm);
    seq_printf(m,
        "VmPeak:\t%8lu kB\n"------------------------------------虚拟内存使用量的峰值,取mm->total_vm和mm->hiwater_vm的大值。
        "VmSize:\t%8lu kB\n"------------------------------------当前虚拟内存的实际使用量。
        "VmLck:\t%8lu kB\n"-------------------------------------PG_mlocked属性的页面总量,常被mlock()置位。
        "VmPin:\t%8lu kB\n"-------------------------------------不可被移动的Pined Memory内存大小。
        "VmHWM:\t%8lu kB\n"-------------------------------------HWM是High Water Mark的意思,表示rss的峰值。
        "VmRSS:\t%8lu kB\n"-------------------------------------应用程序实际占用的物理内存大小,这里和VmSize有区别。VmRss要小于等于VmSize。
        "RssAnon:\t%8lu kB\n"-----------------------------------匿名RSS内存大小。
        "RssFile:\t%8lu kB\n"-----------------------------------文件RSS内存大小。
        "RssShmem:\t%8lu kB\n"----------------------------------共享内存RSS内存大小。
        "VmData:\t%8lu kB\n"------------------------------------程序数据段的所占虚拟内存大小,存放了初始化了的数据。
        "VmStk:\t%8lu kB\n"-------------------------------------进程在用户态的栈大小。
        "VmExe:\t%8lu kB\n"-------------------------------------进程主程序代码段内存使用量,即text段大小。
        "VmLib:\t%8lu kB\n"-------------------------------------进程共享库内存使用量。
        "VmPTE:\t%8lu kB\n"-------------------------------------进程页表项Page Table Entries内存使用量。
        "VmPMD:\t%8lu kB\n"-------------------------------------进程PMD内存使用量。
        "VmSwap:\t%8lu kB\n",-----------------------------------进程swap使用量。
        hiwater_vm << (PAGE_SHIFT-10),
        total_vm << (PAGE_SHIFT-10),
        mm->locked_vm << (PAGE_SHIFT-10),
        mm->pinned_vm << (PAGE_SHIFT-10),
        hiwater_rss << (PAGE_SHIFT-10),
        total_rss << (PAGE_SHIFT-10),
        anon << (PAGE_SHIFT-10),
        file << (PAGE_SHIFT-10),
        shmem << (PAGE_SHIFT-10),
        mm->data_vm << (PAGE_SHIFT-10),
        mm->stack_vm << (PAGE_SHIFT-10), text, lib,
        ptes >> 10,
        pmds >> 10,
        swap << (PAGE_SHIFT-10));
    hugetlb_report_usage(m, mm);
}

static inline void task_sig(struct seq_file *m, struct task_struct *p)
{
    unsigned long flags;
    sigset_t pending, shpending, blocked, ignored, caught;
    int num_threads = 0;
    unsigned long qsize = 0;
    unsigned long qlim = 0;

    sigemptyset(&pending);
    sigemptyset(&shpending);
    sigemptyset(&blocked);
    sigemptyset(&ignored);
    sigemptyset(&caught);

    if (lock_task_sighand(p, &flags)) {
        pending = p->pending.signal;
        shpending = p->signal->shared_pending.signal;
        blocked = p->blocked;
        collect_sigign_sigcatch(p, &ignored, &caught);
        num_threads = get_nr_threads(p);
        rcu_read_lock();  /* FIXME: is this correct? */
        qsize = atomic_read(&__task_cred(p)->user->sigpending);
        rcu_read_unlock();
        qlim = task_rlimit(p, RLIMIT_SIGPENDING);
        unlock_task_sighand(p, &flags);
    }

    seq_put_decimal_ull(m, "Threads:\t", num_threads);----------当前进程下总的线程数。
    seq_put_decimal_ull(m, "\nSigQ:\t", qsize);
    seq_put_decimal_ull(m, "/", qlim);--------------------------<qsize>/<qlim>分别表示当前进程的信号队列大小和系统对信号队列的阈值。

    /* render them all */
    render_sigset_t(m, "\nSigPnd:\t", &pending);----------------信号队列中处于pending状态的位图。
    render_sigset_t(m, "ShdPnd:\t", &shpending);----------------线程组中处于pending状态的位图。
    render_sigset_t(m, "SigBlk:\t", &blocked);------------------处于阻塞blocked状态的信号位图。
    render_sigset_t(m, "SigIgn:\t", &ignored);------------------被忽略的信号位图,产生这些信号不进行处理。
    render_sigset_t(m, "SigCgt:\t", &caught);-------------------已经捕获到的信号位图。
}

static inline void task_cap(struct seq_file *m, struct task_struct *p)
{
    const struct cred *cred;
    kernel_cap_t cap_inheritable, cap_permitted, cap_effective,
            cap_bset, cap_ambient;

    rcu_read_lock();
    cred = __task_cred(p);
    cap_inheritable    = cred->cap_inheritable;
    cap_permitted    = cred->cap_permitted;
    cap_effective    = cred->cap_effective;
    cap_bset    = cred->cap_bset;
    cap_ambient    = cred->cap_ambient;
    rcu_read_unlock();

    render_cap_t(m, "CapInh:\t", &cap_inheritable);----------------表示能被子进程继承的能力。
    render_cap_t(m, "CapPrm:\t", &cap_permitted);------------------进程被允许使用的能力。
    render_cap_t(m, "CapEff:\t", &cap_effective);------------------进程要使用某个特权时,系统会检查cap_effective对应为是否有效。cap_effective是cap_permitted子集。
    render_cap_t(m, "CapBnd:\t", &cap_bset);-----------------------表示进程能获得的最大能力。
    render_cap_t(m, "CapAmb:\t", &cap_ambient);
}

static void task_cpus_allowed(struct seq_file *m, struct task_struct *task)
{
    seq_printf(m, "Cpus_allowed:\t%*pb\n",
           cpumask_pr_args(&task->cpus_allowed));--------------进程可运行CPU列表。
    seq_printf(m, "Cpus_allowed_list:\t%*pbl\n",
           cpumask_pr_args(&task->cpus_allowed));
}

/* Display task mems_allowed in /proc/<pid>/status file. */
void cpuset_task_status_allowed(struct seq_file *m, struct task_struct *task)
{
    seq_printf(m, "Mems_allowed:\t%*pb\n",
           nodemask_pr_args(&task->mems_allowed));------------进程可使用的内存节点。
    seq_printf(m, "Mems_allowed_list:\t%*pbl\n",
           nodemask_pr_args(&task->mems_allowed));
}

static inline void task_context_switch_counts(struct seq_file *m,
                        struct task_struct *p)
{
    seq_put_decimal_ull(m, "voluntary_ctxt_switches:\t", p->nvcsw);-----------线程主动切换次数。表示CPU主动放弃CPU。
    seq_put_decimal_ull(m, "\nnonvoluntary_ctxt_switches:\t", p->nivcsw);-----线程被动切换次数。表示被动放弃CPU,被高优先级任务或本身时间片耗完而被动放弃CPU。
    seq_putc(m, '\n');
}

信号位图和信号值对应关系:位图bit=信号值-1。

#define SIGHUP         1
#define SIGINT         2
#define SIGQUIT         3
#define SIGILL         4
#define SIGTRAP         5
#define SIGABRT         6
#define SIGIOT         6
#define SIGBUS         7
#define SIGFPE         8
#define SIGKILL         9
#define SIGUSR1        10
#define SIGSEGV        11
#define SIGUSR2        12
#define SIGPIPE        13
#define SIGALRM        14
#define SIGTERM        15
#define SIGSTKFLT    16
#define SIGCHLD        17
#define SIGCONT        18
#define SIGSTOP        19
#define SIGTSTP        20
#define SIGTTIN        21
#define SIGTTOU        22
#define SIGURG        23
#define SIGXCPU        24
#define SIGXFSZ        25
#define SIGVTALRM    26
#define SIGPROF        27
#define SIGWINCH    28
#define SIGIO        29
#define SIGPOLL        SIGIO
#define SIGPWR        30
#define SIGSYS        31
#define    SIGUNUSED    31

#define SIGRTMIN    32
#ifndef SIGRTMAX
#define SIGRTMAX    _NSIG
#endif

2. 一个进程status实例解析

 下面结合一个实例,解析一下status。

Name: Log2Hostflush
Umask: 0022
State: D (disk sleep)-----------------------表示此时线程处于sleeping,并且是uninterruptible状态的wait。

Tgid: 157-----------------------------------线程组的主pid为157。
Ngid: 0
Pid: 159------------------------------------线程自身的pid为159。
PPid: 1-------------------------------------线程组是由init进程创建的。
TracerPid: 0
Uid: 0 0 0 0
Gid: 0 0 0 0
FDSize: 256---------------------------------表示到目前为止进程使用过的描述符总数。
Groups: 0 10
VmPeak: 1393220 kB--------------------------虚拟内存峰值大小。
VmSize: 1390372 kB--------------------------当前使用中的虚拟内存,小于VmPeak。
VmLck: 0 kB
VmPin: 0 kB
VmHWM: 47940 kB-----------------------------RSS峰值。
VmRSS: 47940 kB-----------------------------RSS实际使用量=RSSAnon+RssFile+RssShmem。
RssAnon: 38700 kB
RssFile: 9240 kB
RssShmem: 0 kB
VmData: 366648 kB--------------------------进程数据段共366648KB。
VmStk: 132 kB------------------------------进程栈一共132KB。
VmExe: 84 kB-------------------------------进程text段大小84KB。
VmLib: 11488 kB----------------------------进程lib占用11488KB内存。
VmPTE: 1220 kB
VmPMD: 0 kB
VmSwap: 0 kB
Threads: 40-------------------------------进程中一个40个线程。
SigQ: 0/3142------------------------------进程信号队列最大3142,当前没有pending状态的信号。
SigPnd: 0000000000000000------------------没有进程pending,所以位图为0。
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000000000006------------------被忽略的信号,对应信号为SIGINT和SIGQUIT,这两个信号产生也不会进行处理。
SigCgt: 0000000180000800------------------已经产生的信号位图,对应信号为SIGUSR2、以及实时信号32和33。
CapInh: 0000000000000000
CapPrm: 0000003fffffffff
CapEff: 0000003fffffffff
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000
Cpus_allowed: 1---------------------------仅在第1个cpu上执行。
Cpus_allowed_list: 0
voluntary_ctxt_switches: 2377-------------线程主动切换2377次,被动切换5次。
nonvoluntary_ctxt_switches: 5

posted on 2020-01-12 00:00  ArnoldLu  阅读(13211)  评论(0编辑  收藏  举报

导航