Linux-进程状态&僵尸进程&孤儿进程
####一、Linux进程状态
#####在我们的操作系统中,可以同时运行多个程序,而程序在内存中则是一个个的进程,在windows下我们打开任务管理器就可查看对应进程的状态。Linux下可以通过ps命令查看,Linux上进程主要有以下几种状态:
#####<1>运行状态 R(TASK_RUNNING)
#####当进程正在被CPU执行,或已经准备就绪随时可被调度执行,则称该进程为处于运行状态(running)。所谓就绪状态就是该进程已经具有执行所需的条件,随时可被调度执行,但是还在就绪队列中尚未被调度。
#####<2>可中断睡眠状态 S(TASK_INTERRUPTIBLE)
#####由于cpu同时可执行的进程有限,所以我们查看进程状态时会有大多数的进程都处于此状态。当进程处于可中断睡眠状态时,系统不会调度该进程执行。只有当系统产生一个中断或者释放了该进程正在等待的资源,或者该进程收到一个信号,就可以唤醒该进程并将状态转换到运行状态(R)。
#####<3>不可中断睡眠状态 D(TASK_UNINTERRUPTIBLE)
#####与可中断睡眠状态类似,但处于该状态的进程不响应异步信号,此状态存在的意义就在于,内核的某些处理流程是不能被打断的。如果响应异步信号,程序的执行流程中就会被插入一段用于处理异步信号的流程(这个插入的流程可能只存在于内核态,也可能延伸到用户态),于是原有的流程就被中断了。
#####<4>暂停状态 T(TASK_STOPPED)
#####当进程收到信号SIGSTOP、SIGTSTP、SIGTTIN或SIGTTOU时就会进入暂停状态。可向其发送SIGCONT信号让进程转换到可运行状态。
#####<5>僵死状态 Z(TASK_ZOMBIE)
#####当子进程已停止运行,但其父进程还在运行并且没有询问其状态时,则称该子进程处于僵死状态。僵死进程会以终止状态保持在进程表中,直到等待父进程读取退出状态码。
#####<6>退出状态 X ( TASK_DEAD - EXIT_DEAD)
#####进程将被置于EXIT_DEAD退出状态,这意味着接下来的代码立即就会将该进程彻底释放。该状态是非常短暂的,几乎不可能通过ps命令捕捉到。
#####补充:STAT状态位常见的状态字符
#####D 无法中断的休眠状态(通常 IO 的进程);
#####R 正在运行可中在队列中可过行的;
#####S 处于休眠状态;
#####T 停止或被追踪;
#####W 进入内存交换 (从内核2.6开始无效);
#####X 死掉的进程 (基本很少見);
#####Z 僵尸进程;
#####< 优先级高的进程
#####N 优先级较低的进程
#####L 有些页被锁进内存;
#####s 进程的领导者(在它之下有子进程);
#####l 多进程的(使用 CLONE_THREAD, 类似 NPTL pthreads);
#####+ 位于后台的进程组;
#####使用ps a可查看当前所有进程:
####二、僵尸进程
#####**<1>先来构造一个僵尸进程的例子:**我们在前面已经了解到一个子进程在其父进程没有调用wait()或waitpid()的情况下退出,如果其父进程还存在而一直不调用wait(),则该僵尸进程将无法回收,等到其父进程退出后该进程将被init回收。在这个过程中子进程已经放弃了几乎所有内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退出状态等信息供其他进程收集,这个时候该子进程称为一个僵尸进程。这个子进程就是僵尸进程。例:
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <stdlib.h>
4
5
6 int main()
7 {
8 pid_t id = fork();
9 if(id < 0){
10 perror("fork");
11 return 1;
12 }else if(id > 0){
13 printf("parent[%d] is sleeping\n",getpid());
14 sleep(30);
15 }else{
16 printf("child[%d] is begin\n",getpid());
17 sleep(5);
18 exit(1);
19 }
20
21 return 0;
22 }
#####我们在shell下同时开启两个终端,一个下运行以上程序,另外一个在执行过程中使用grep抓取stat状态为zZ进程,通过结果的显示我们可以看出来在该进程执行时父进程状态为Z,成为了僵尸进程
ps -A -o stat,ppid,pid,cmd | grep -e '^[Zz]'
#####<2>结果分别如下:
#####我们通过fork()创建一个子进程,父进程执行30s,子进程执行5s后就退出,并且父进程没有使用wait或waitpid的方式来获取子进程的退出信息,此时子进程就会出现僵尸进程的现象。
#####<3>避免僵尸进程的方法:
#####⒈父进程通过wait和waitpid等函数等待子进程结束,这会导致父进程挂起,相关用法可以通过man命令查看。
#####⒉ 如果父进程很忙,那么可以用signal函数为SIGCHLD安装handler,因为子进程结束后, 父进程会收到该信号,可以在handler中调用wait回收。
#####⒊ 如果父进程不关心子进程什么时候结束,那么可以用signal(SIGCHLD,SIG_IGN) 通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收, 并不再给父进程发送信号。
#####⒋ 还有一些技巧,就是fork两次,父进程fork一个子进程,然后继续工作,子进程fork一 个孙进程后退出,那么孙进程被init接管,孙进程结束后,init会回收。不过子进程的回收 还要自己做。
#####<4>僵尸进程的危害:
#####假设这样一个情景,我们有一个父进程在不断的创建子进程,每个子进程的存活时间都很短,父进程对子进程的终止状态都不管不顾,任由发展下去,子子孙孙,系统中就会存在许多的僵尸进程,更重要的是每一个僵尸进程都还没占着对应的进程列表,进程列表可是临界资源是有限的,时间一长内存中就没有多余的地方再让我们创建进程了。
####三、孤儿进程
#####<1>同样通过构造一个例子来理解什么叫孤儿进程,此场景就和名字一样通俗易懂,所谓的“孤儿”,就是失去了“父亲”,也就是说在子进程还在执行时,父进程就已经退出了。看例子:
#include <unistd.h>
#include <stdio.h>
int main()
{
pid_t id = fork();
if(id < 0){
perror("for");
return 1;
}else if(id == 0){
printf("start:im child[%d],parent is [%d]\n",getpid(),getppid());
sleep(10);
printf("finish:im child[%d],parent is [%d]\n",getpid(),getppid());
}else{
printf("im father[%d]\n",getpid());
sleep(2);
}
return 0;
}
#####<2>运行结果及分析:
#####我们可以看出本来子进程的父进程Pid为2420,但是我们让父进程sleep两秒后就结束了,此时子进程依然还在运行,等子进程sleep完成后,此时父进程已经不在了,新的父亲的pid为1,也就是系统中的init进程,也可以说该子进程被init进程所接管。
还没有评论,来说两句吧...