文章基本转载自 http://www.ibm.com/developerworks/cn/linux/l-task-killable/
关于进程状态
在进程的生命周期内,可能会经历一系列互斥的状态。内核将进程的状态信息保存在 struct task_struct
的 state 字段中。图 1 展示了进程状态之间的转换。
图 1. 进程状态转换
![进程状态转换](https://i-blog.csdnimg.cn/blog_migrate/7cec82c621497fe8e257f47fc32461f2.png)
我们先来了解一下各种进程状态:
-
TASK_RUNNING
:进程当前正在运行,或者正在运行队列中等待调度。 -
TASK_INTERRUPTIBLE
:进程处于睡眠状态,正在等待某些事件发生。进程可以被信号中断。接收到信号或被显式的唤醒呼叫唤醒之后,进程将转变为TASK_RUNNING
状态。 -
-
TASK_UNINTERRUPTIBLE
:此进程状态类似于TASK_INTERRUPTIBLE
,只是它不会处理信号。中断处于这种状态的进程是不合适的,因为它可能正在完成某些重要的任务。 当它所等待的事件发生时,进程将被显式的唤醒呼叫唤醒。 -
-
TASK_STOPPED
:进程已中止执行,它没有运行,并且不能运行。接收到SIGSTOP
和SIGTSTP
等信号时,进程将进入这种状态。接收到SIGCONT
信号之后,进程将再次变得可运行。 -
-
TASK_TRACED
:正被调试程序等其他进程监控时,进程将进入这种状态。 -
-
EXIT_ZOMBIE
:进程已终止,它正等待其父进程收集关于它的一些统计信息。 -
-
EXIT_DEAD
:最终状态(正如其名)。将进程从系统中删除时,它将进入此状态,因为其父进程已经通过wait4()
或waitpid()
调用收集了所有统计信息。
如前所述,进程状态 TASK_UNINTERRUPTIBLE
和 TASK_INTERRUPTIBLE
都是睡眠状态。现在,我们来看看内核如何将进程置为睡眠状态。
内核映射
Linux 内核提供了两种方法将进程置为睡眠状态。
将进程置为睡眠状态的普通方法是将进程状态设置为 TASK_INTERRUPTIBLE
或 TASK_UNINTERRUPTIBLE
并调用调度程序的 schedule()
函数。这样会将进程从 CPU 运行队列中移除。如果进程处于可中断模式的睡眠状态(通过将其状态设置为 TASK_INTERRUPTIBLE
),那么可以通过显式的唤醒呼叫(wakeup_process()
)或需要处理的信号来唤醒它。
但是,如果进程处于非可中断模式的睡眠状态(通过将其状态设置为 TASK_UNINTERRUPTIBLE
),那么只能通过显式的唤醒呼叫将其唤醒。除非万不得已,否则我们建议您将进程置为可中断睡眠模式,而不是不可中断睡眠模式(比如说在设备 I/O 期间,处理信号非常困难时)。
当处于可中断睡眠模式的任务接收到信号时,它需要处理该信号(除非它已被屏弊),离开之前正在处理的任务(此处需要清除代码),并将-EINTR
返回给用户空间。再一次,检查这些返回代码和采取适当操作的工作将由程序员完成。因此,懒惰的程序员可能比较喜欢将进程置为不可中断模式的睡眠状态,因为信号不会唤醒这类任务。但需要注意的一种情况是,对不可中断睡眠模式的进程的唤醒呼叫可能会由于某些原因不会发生,这会使进程无法被终止,从而最终引发问题,因为惟一的解决方法就是重启系统。一方面,您需要考虑一些细节,因为不这样做会在内核端和用户端引入 bug。另一方面,您可能会生成永远不会停止的进程(被阻塞且无法终止的进程)。
现在,我们在内核中实现了一种新的睡眠方法!
新睡眠状态:TASK_KILLABLE
Linux Kernel 2.6.25 引入了一种新的进程睡眠状态,TASK_KILLABLE
:当进程处于这种可以终止的新睡眠状态中,它的运行原理类似于TASK_UNINTERRUPTIBLE
,只不过可以响应致命信号。清单 1 给出了内核 2.6.18 与内核 2.6.26 进程状态(定义在 include/linux/sched.h 中)之间的比较:
清单 1. 2.6.18 和 2.6.26 进程状态之间的比较
Linux Kernel 2.6.18 Linux Kernel 2.6.26
================================= ===================================
#define TASK_RUNNING 0 #define TASK_RUNNING 0
#define TASK_INTERRUPTIBLE 1 #define TASK_INTERRUPTIBLE 1
#define TASK_UNINTERRUPTIBLE 2 #define TASK_UNINTERRUPTIBLE 2
#define TASK_STOPPED 4 #define __TASK_STOPPED 4
#define TASK_TRACED 8 #define __TASK_TRACED 8
/* in tsk->exit_state */ /* in tsk->exit_state */
#define EXIT_ZOMBIE 16 #define EXIT_ZOMBIE 16
#define EXIT_DEAD 32 #define EXIT_DEAD 32
/* in tsk->state again */ /* in tsk->state again */
#define TASK_NONINTERACTIVE 64 #define TASK_DEAD 64
#define TASK_WAKEKILL 128
注意,状态 TASK_INTERRUPTIBLE
和 TASK_UNINTERRUPTIBLE
并未修改。 TASK_WAKEKILL
用于在接收到致命信号时唤醒进程。
清单 2 展示了状态 TASK_STOPPED
和 TASK_TRACED
的修改之处(以及 TASK_KILLABLE
的定义):
清单 2. 内核 2.6.26 中的新状态定义
#define TASK_KILLABLE (TASK_WAKEKILL | TASK_UNINTERRUPTIBLE) #define TASK_STOPPED (TASK_WAKEKILL | __TASK_STOPPED) #define TASK_TRACED (TASK_WAKEKILL | __TASK_TRACED)
换句话说,TASK_UNINTERRUPTIBLE
+ TASK_WAKEKILL
= TASK_KILLABLE
。
尽管此特性是对现有选项的改进 — 毕竟,它是解决死进程的另一种方法 — 但它要得到普遍应用还有待时日。记住,除非真的非常有必要 禁止显式唤醒呼叫(通过传统的 TASK_UNINTERRUPTIBLE
)之外的任何中断,否则请使用新的 TASK_KILLABLE
。
补充,新版3.10的内核进程状态似又改变:
#define TASK_RUNNING 0
#define TASK_INTERRUPTIBLE 1
#define TASK_UNINTERRUPTIBLE 2
#define __TASK_STOPPED 4
#define __TASK_TRACED 8
/* in tsk->exit_state */
#define EXIT_ZOMBIE 16
#define EXIT_DEAD 32
/* in tsk->state again */
#define TASK_DEAD 64
#define TASK_WAKEKILL 128
#define TASK_WAKING 256
#define TASK_PARKED 512
#define TASK_STATE_MAX 1024
/* Convenience macros for the sake of set_task_state */
#define TASK_KILLABLE (TASK_WAKEKILL | TASK_UNINTERRUPTIBLE)
#define TASK_STOPPED (TASK_WAKEKILL | __TASK_STOPPED)
#define TASK_TRACED (TASK_WAKEKILL | __TASK_TRACED)
另外 摘自 http://blog.chinaunix.net/uid-23629988-id-3242488.html
- top - 10:50:12 up 1:20, 11 users, load average: 0.08, 0.03, 0.01
$busybox uptime
- top - 10:54:21 up 1:24, 11 users, load average: 0.97, 0.51, 0.20
关于查询进程状态:
转自 http://blog.csdn.net/weiruoao/article/details/36875899
1.查看进程的pid,以Java为例:ps -ef | grep java
2.查看进程状态:cat /proc/[pid]/status
[root@localhost ~]# cat /proc/self/status
Name: cat
State: R (running)
SleepAVG: 88%
Tgid: 5783
Pid: 5783
PPid: 5742
TracerPid: 0
Uid: 0 0 0 0
Gid: 0 0 0 0
FDSize: 256
Groups: 0 1 2 3 4 6 10
VmSize: 6588 kB
VmLck: 0 kB
VmRSS: 400 kB
VmData: 144 kB
VmStk: 2040 kB
VmExe: 14 kB
VmLib: 1250 kB
StaBrk: 0804e000 kB
Brk: 088df000 kB
StaStk: bfe03270 kB
ExecLim: 0804c000
Threads: 1
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000000000000
SigCgt: 0000000000000000
CapInh: 0000000000000000
CapPrm: 00000000fffffeff
CapEff: 00000000fffffeff
输出解释
参数 解释
Name 应用程序或命令的名字
State 任务的状态,运行/睡眠/僵死/
SleepAVG 任务的平均等待时间(以nanosecond为单位),交互式任务因为休眠次数多、时间长,它们的 sleep_avg 也会相应地更大一些,所以计算出来的优先级也会相应高一些。
Tgid 线程组号
Pid 任务ID
Ppid 父进程ID
TracerPid 接收跟踪该进程信息的进程的ID号
Uid Uid euid suid fsuid
Gid Gid egid sgid fsgid
FDSize 文件描述符的最大个数,file->fds
Groups
VmSize(KB) 任务虚拟地址空间的大小 (total_vm-reserved_vm),其中total_vm为进程的地址空间的大小,reserved_vm:进程在预留或特殊的内存间的物理页
VmLck(KB) 任务已经锁住的物理内存的大小。锁住的物理内存不能交换到硬盘 (locked_vm)
VmRSS(KB) 应用程序正在使用的物理内存的大小,就是用ps命令的参数rss的值 (rss)
VmData(KB) 程序数据段的大小(所占虚拟内存的大小),存放初始化了的数据; (total_vm-shared_vm-stack_vm)
VmStk(KB) 任务在用户态的栈的大小 (stack_vm)
VmExe(KB) 程序所拥有的可执行虚拟内存的大小,代码段,不包括任务使用的库 (end_code-start_code)
VmLib(KB) 被映像到任务的虚拟内存空间的库的大小 (exec_lib)
VmPTE 该进程的所有页表的大小,单位:kb
Threads 共享使用该信号描述符的任务的个数,在POSIX多线程序应用程序中,线程组中的所有线程使用同一个信号描述符。
SigQ 待处理信号的个数
SigPnd 屏蔽位,存储了该线程的待处理信号
ShdPnd 屏蔽位,存储了该线程组的待处理信号
SigBlk 存放被阻塞的信号
SigIgn 存放被忽略的信号
SigCgt 存放被俘获到的信号
CapInh Inheritable,能被当前进程执行的程序的继承的能力
CapPrm Permitted,进程能够使用的能力,可以包含CapEff中没有的能力,这些能力是被进程自己临时放弃的,CapEff是CapPrm的一个子集,进程放弃没有必要的能力有利于提高安全性
CapEff Effective,进程的有效能力