一.进程的退出
进程=内核PCB+数据和代码,它们都要占据内存空间,进程退出的核心工作之一就是释放掉自己的PCB+数据和代码。
为什么要创建出进程呢?一定是我要进程完成某些任务!
当进程退出了,不光光只是退出了这么简单,你还要知道该进程把任务完成的怎么样吧,这里的你指的是谁啊?
一般指的是父进程或者OS,进程退出的时候,要有一些退出信息,表明自己把任务完成得怎么样了!
比如说平时我们写代码,给main函数的返回值都是return 0的,为什么这里我们要返回0呢?返回1,2,3等等行不行,
其实这里return 0就是一种退出信息,表示将任务完成的很好。(后续细说这个退出码)
进程退出了,退出信息会由OS写到该退出进程的PCB中,可以允许进程的代码和数据空间释放掉,但是它的退出信息还没有被父进程或OS读取到,OS就还必须维护该进程的PCB!
此时,该进程算是退出了吗?OS已经不会再调用该进程了,但是它的PCB确实还存在着,这时的进程就叫做僵尸进程。
二.Z(zombie)-僵尸进程
1.什么是僵尸进程?
僵尸状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程(使用wait()系统调用,后面讲)
没有读取到子进程退出的返回代码时就会产生僵尸(死)进程。
僵尸进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。
所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态。
用代码来验证僵尸进程:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
pid_t id=fork();
if(id<0)
return 1;
else if(id==0)//子进程
{
int ret=5;
while(ret)
{
printf("I am child,run time:%d\n",ret--);
sleep(1);
}
printf("I am child,dead!\n");
exit(2);//进程退出(后续说)
}
else
{
while(1)//父进程
{
printf("I am father,running any time\n");
sleep(1);
}
}
return 0;
}
可以很清晰的看见,当进程运行起来的时候,父子进程一起运行起来,当打印了五次之后,子进程退出了,但是父进程还在继续运行,父进程没有读取到子进程的退出信息,此时的子进程的状态就变成了Z+状态,也就是僵尸状态,此时的子进程也就变成了僵尸进程。
当进程变成了僵尸进程,就连kill -9也没办法杀死该进程,因为谁也杀死不了一个已经死了的进程。
2.僵尸进程的危害
进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了。可父进程如果一直不读取,那子进程就一直处于Z状态?是的!
维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话
说,Z状态一直不退出,PCB一直都要维护?是的!
那一个父进程创建了很多子进程,就是不回收,是不是就会造成内存资源的浪费?是的!因为数据结构
对象本身就要占用内存,想想C中定义一个结构体变量(对象),是要在内存的某个位置进行开辟空间!
内存泄漏?是的!
如何避免?后面讲
三.孤儿进程
当子进程退出了,父进程还没退出,父进程没有获取到子进程的退出信息,这时子进程就变成了僵尸进程。
然而孤儿进程恰恰是相反的,当父进程退出的时候,子进程还没有退出,这个时候的子进程就变成了孤儿进程了。
同样的我们还是使用代码来说明:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
pid_t id=fork();
if(id<0)
return 1;
else if(id==0)//父进程
{
int ret=5;
while(ret)
{
printf("I am father,run time:%d\n",ret--);
sleep(1);
}
printf("I am father,dead!\n");
exit(2);//进程退出(后续说)
}
else
{
while(1)//父进程
{
printf("I am child,running any time\n");
sleep(1);
}
}
return 0;
}
父进程的父进程是bash,这时毋庸置疑的,故而父进程退出的时候,它的退出信息被bash获取到了。
父进程使用fork创建出的子进程,父进程需要管理子进程的,但是子进程还在运行,父进程却已经退出了,子进程这个时候需要被领养,也就是被1号进程(init进程)领养,其实也就是OS。
四.进程的优先级
1.优先级的概念
cpu资源分配的先后顺序,就是指进程的优先级(priority)。
优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用,可以改善系统性能。
还可以把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能。
OS中有很多进程需要调度,但是cpu就只有一个,那么这些进程就需要排队,排队的本质就是在确定进程的优先级。
2.为什么要有优先级?
举一个很简单的例子,像我们排队在食堂打饭的时候,为什么我们要排队呢?本质上就是窗口太少了,人太多了,资源分配不足。在OS中也是一样的,CPU只有一个,键盘只有一个,显示器只有一个,当很多进程都需要访问这些软硬件资源的时候,分配不过来,就需要指定哪些先执行,哪些后执行,这就是优先级的作用!!
3.查看系统进程
使用命令:**ps -l**查看系统中的进程。
注意到几种重要的信息:
UID : 代表执行者的身份
PID : 代表这个进程的代号
PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号
PRI :代表这个进程可被执行的优先级,其值越小越早被执行
NI :代表这个进程的nice值
4.操作优先级-修改NICE值
在linux中进程的优先级的范围为:60-99(一共四十个)。
默认进程的优先级都是80。
其中优先级的数字越小,优先级越高。
其中通过nice值可以修改进程的优先级。
pri(新)=80+nice值。
修改步骤如下:
使用命令top,进入任务管理器。
然后在按“r”->输入进程的PID->输入nice值。
注意优先级的返回是有限制的。所以你输入再大或者再小的nice值,进程的优先级都会在60-99之间。
当你输入负nice值的时候,进程的优先级会变大,普通用户是做不到的,root账户才能做到。
为什么要将进程的优先级设置在一定范围呢?
OS调度进程的时候,要较为均衡的让每一个进程都要得到调度!
如果将进程可以设置为超级大,那么优先级小的进程,长时间得不到cpu的调度——进程饥饿。
其他概念:
竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级。
独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰。
并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行。
并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发。