procpidstatus简要分析
关键词:status、virtual memory、signal、capability、context switch等等。
每个进程/线程都有⾃⼰私有状态,在/proc/<pid>status中体现。
了解status中每项的含义,有助于问题定位时发现问题。
下⾯结合status在内中函数()对每⼀项进⾏简单的了解,然后结合⼀个⽰例进⾏分析。
1. status在内核中的解释
status当前进程/线程的名称、运⾏状态、pid、信号、权限、cpu使⽤范围、进程切换等等信息。
int proc_pid_status(struct q_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);
(m, ns, pid, task);
if (mm) {
(m, mm);
mmput(mm);
}
task_sig(m, task);
task_cap(m, task);
task_ccomp(m, task);
task_cpus_allowed(m, task);
cput_task_status_allowed(m, task);
task_context_switch_counts(m, task);
哈布洛先生
return0;
}
static inline void task_state(struct q_file *m, struct pid_namespace *ns,
struct pid *pid, struct task_struct *p)
{
struct ur_namespace *ur_ns = q_ur_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)
q_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();
q_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.
q_put_decimal_ull(m, "\nTgid:\t", tgid);----------------------------------线程组ID,也就是线程组leader的进程id,等于pid。
q_put_decimal_ull(m, "\nNgid:\t", ngid);----------------------------------进程所属的NUMA id。
q_put_decimal_ull(m, "\nPid:\t", pid_nr_ns(pid, ns));---------------------进程pid。
q_put_decimal_ull(m, "\nPPid:\t", ppid);----------------------------------进程的⽗进程pid。
q_put_decimal_ull(m, "\nTracerPid:\t", tpid);-----------------------------ptrace对应的进程id。
q_put_decimal_ull(m, "\nUid:\t", from_kuid_munged(ur_ns, cred->uid));---实际⽤户id,指的是进程执⾏者是谁。
q_put_decimal_ull(m, "\t", from_kuid_munged(ur_ns, cred->euid));--------有效⽤户id,指进程执⾏时对系统资源的访问权限。
q_put_decimal_ull(m, "\t", from_kuid_munged(ur_ns, cred->suid));--------保存设置⽤户id,是进程刚开始执⾏时euid的副本。在执⾏exec调⽤之后能重新恢复原来的euid。
q_put_decimal_ull(m, "\t", from_kuid_munged(ur_ns, cred->fsuid));-------通常等于euid,⽤于访问⽂件时检查访问权限。
q_put_decimal_ull(m, "\nGid:\t", from_kgid_munged(ur_ns, cred->gid));
q_put_decimal_ull(m, "\t", from_kgid_munged(ur_ns, cred->egid));
q_put_decimal_ull(m, "\t", from_kgid_munged(ur_ns, cred->sgid));
q_put_decimal_ull(m, "\t", from_kgid_munged(ur_ns, cred->fsgid));
q_put_decimal_ull(m, "\nFDSize:\t", max_fds);-----------------------------进程已打开最⼤⽂件描述符数。这个值不是⽂件描述符的上限,也不是实际使⽤中的⽂件描述符数量。以32递增。
q_puts(m, "\nGroups:\t");-------------------------------------------------Supplementary group list.
group_info = cred->group_info;
for (g = 0; g < group_info->ngroups; g++)
q_put_decimal_ull(m, g ? "" : "",
from_kgid_munged(ur_ns, group_info->gid[g]));
put_cred(cred);
/* Trailing space shouldn't have been added in the first place. */
q_putc(m, '');
...
brentq_putc(m, '\n');
}
void task_mem(struct q_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);
q_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 q_file *m, struct task_struct *p)
{
unsigned long flags;
sigt_t pending, shpending, blocked, ignored, caught;美国枪支管理
int num_threads = 0;
unsigned long qsize = 0;
unsigned long qlim = 0;
sigemptyt(&pending);
sigemptyt(&shpending);
sigemptyt(&blocked);
sigemptyt(&ignored);
sigemptyt(&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)->ur->sigpending);
rcu_read_unlock();
qlim = task_rlimit(p, RLIMIT_SIGPENDING);
unlock_task_sighand(p, &flags);
}
q_put_decimal_ull(m, "Threads:\t", num_threads);----------当前进程下总的线程数。
q_put_decimal_ull(m, "\nSigQ:\t", qsize);
q_put_decimal_ull(m, "/", qlim);--------------------------<qsize>/<qlim>分别表⽰当前进程的信号队列⼤⼩和系统对信号队列的阈值。
/* render them all */
render_sigt_t(m, "\nSigPnd:\t", &pending);----------------信号队列中处于pending状态的位图。
render_sigt_t(m, "ShdPnd:\t", &shpending);----------------线程组中处于pending状态的位图。
render_sigt_t(m, "SigBlk:\t", &blocked);------------------处于阻塞blocked状态的信号位图。
render_sigt_t(m, "SigIgn:\t", &ignored);------------------被忽略的信号位图,产⽣这些信号不进⾏处理。
render_sigt_t(m, "SigCgt:\t", &caught);-------------------已经捕获到的信号位图。
}
static inline void task_cap(struct q_file *m, struct task_struct *p)
{
const struct cred *cred;
kernel_cap_t cap_inheritable, cap_permitted, cap_effective,
cap_bt, 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_bt = cred->cap_bt;
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_bt);-----------------------表⽰进程能获得的最⼤能⼒。
render_cap_t(m, "CapAmb:\t", &cap_ambient);
}
static void task_cpus_allowed(struct q_file *m, struct task_struct *task)
{
q_printf(m, "Cpus_allowed:\t%*pb\n",
offer letter
cpumask_pr_args(&task->cpus_allowed));--------------进程可运⾏CPU列表。
q_printf(m, "Cpus_allowed_list:\t%*pbl\n",
cpumask_pr_args(&task->cpus_allowed));
}
/* Display task mems_allowed in /proc/<pid>/status file. */
void cput_task_status_allowed(struct q_file *m, struct task_struct *task)
{
q_printf(m, "Mems_allowed:\t%*pb\n",
nodemask_pr_args(&task->mems_allowed));------------进程可使⽤的内存节点。
q_printf(m, "Mems_allowed_list:\t%*pbl\n",
nodemask_pr_args(&task->mems_allowed));
}
static inline void task_context_switch_counts(struct q_file *m,
struct task_struct *p)
{
q_put_decimal_ull(m, "voluntary_ctxt_switches:\t", p->nvcsw);-----------线程主动切换次数。表⽰CPU主动放弃CPU。
q_put_decimal_ull(m, "\nnonvoluntary_ctxt_switches:\t", p->nivcsw);-----线程被动切换次数。表⽰被动放弃CPU,被⾼优先级任务或本⾝时间⽚耗完⽽被动放弃CPU。 q_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
babyfaceState: 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
2013年重庆高考数学Cpus_allowed: 1---------------------------仅在第1个cpu上执⾏。
Cpus_allowed_list: 0
voluntary_ctxt_switches: 2377-------------线程主动切换2377次,被动切换5次。
nonvoluntary_ctxt_switches: 5