/* * The task state array is a strange "bitmap" of * reasons to sleep. Thus "running" is zero, and * you can test for combinations of others with * simple bit tests. */ static const char * const task_state_array[] = { "R (running)", /* 0 */ "S (sleeping)", /* 1 */ "D (disk sleep)", /* 2 */ "T (stopped)", /* 4 */ "t (tracing stop)", /* 8 */ "X (dead)", /* 16 */ "Z (zombie)", /* 32 */ };
- R运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。
- S睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠(interruptible sleep))。
- D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。
- T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。
- X死亡状态(dead):这个状态是一个返回状态,在任务列表里是无法查看到这个状态,瞬时性非常强,代表着进程已结束,告诉操作系统随时可以回收。
- Z僵尸状态:一个进程已经退出,但是还不允许被操作系统释放,处于一个被检测的状态
示例:
注意,状态后面存在
+ 这个符号,代表这是一个前台任务,可以随时被 Ctrl+c 终止掉
状态后面不存在
+ 这个符号,代表这是一个后台任务(运行程序时,后面加上 & 符号),无法被 Ctrl+c 终止掉,可以通过 kill 发送信号终止
R状态
1 #include<stdio.h> 2 3 int main() 4 { 5 while(1) 6 {} 7 8 return 0; 9 }
S状态
1 #include<stdio.h> 2 3 int main() 4 { 5 while(1) 6 { 7 printf("I am process!\n"); 8 } 9 10 return 0; 11 }
T状态:
t:通过gdb调试
1 #include<stdio.h> 2 3 int main() 4 { 5 while(1) 6 { 7 printf("I am process!\n"); 8 } 9 10 return 0; 11 }
T:通过发送信号
睡眠状态和停止状态有什么区别呢?
- S:等待某种资源,是阻塞状态
- T:可以直接暂停,不会等待资源(比如gdb调试)
关于D状态:深度睡眠,不可被中断,不可以被被动唤醒
当服务器压力过大的时候,系统会通过一定的手段,杀掉一些进程,来起节省空间的作用!!
如图:当进程是S状态的时候,是有可能被系统干掉的,从而会导致传输失败。所以为了防止这样情况的发生,才有了D状态,他的意思是告诉系统,我是不可以被干掉的,只能等我传输成功,从睡眠中醒来才可以干掉我这个进程。
关于僵尸进程
- 僵死状态(Zombies)是一个比较特殊的状态。
- 没有读取到子进程退出的返回代码时就会产生僵死(尸)进程
- 僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。
- 所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态
僵尸进程危害
进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了。可父进程如果一直不读取,那子进程就会一直处于Z状态!
维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话说,Z状态一直不退出,PCB一直都要维护!
如果一个父进程创建了很多子进程,就是不回收,是会造成内存资源的浪费!
因为数据结构对象本身就要占用内存,想想C中定义一个结构体变量(对象),是要在内存的某个位置进行开辟空间!所以一定会导致内存泄漏!
那么如何避免?后续会在进程等待中有所展开。
关于孤儿进程
- 父进程如果提前退出,子进程后退出,子进程进入Z之后,那该如何处理呢?
- 父进程先退出,子进程就称之为“孤儿进程”
- 孤儿进程被1号init进程领养,当然要有init进程回收喽。
示例:创建一个子进程,父进程运行5秒后结束,此时,子进程被1号进程收养。