/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) 编辑 收藏 举报