1.进程创建
会用到的函数:
getpid() 获取进程的PID,谁调用则获取谁的
getppid() 获取父进的PID
fork() 用来创建一个子进程(父子进程为相互独立的进程),返回值pid_t (其实就是int被typedef为pid_t),无参,属于系统调用函数。这个函数的返回值会比较特殊,返回值有两个,分两类进行说明:
(1)创建成功则返回一个小于0的数(-1)
(2)创建失败会分别给父进程和子进程一个返回值,父进程得到的返回值为大于0的数(创建出来的子进程的PID) ,返回给子进程的是0。
fork()函数创建子进程的原理:fork会将父进程的PCB拷贝一份给子进程(但是父子进程为相互独立的进程);父进程中会保存子进程的PID,子进程会保存父进程的PID,这样父子进程就能联系在一起了;
比如创建了下面这样一个父子进程,来看看程序是怎么运行的:
#include <stdio.h>
#include <stdlib.h>
int main() {
pid_t id = fork();
if(id < 0){ perror("fork");
return 1;
}
else if(id > 0){
//parent
printf("parent[%d] is sleeping...\n", getpid());
sleep(30);
}
else{
printf("child[%d] is begin Z...\n", getpid());
sleep(5);
exit(EXIT_SUCCESS);
}
return 0;
}
子进程会拷贝父进程的PCB,父子进程拥有相同的代码段,但是由于fork()的返回值不同,父子进程执行的代码块不同。
问题1:子进程是从fork()开始运行的,还是从foork()之后开始运行的?
fork()之后,因为父进程在执行完毕fork之后,程序计数器当中保存的是fork之后的指令,子进程拷贝父进程的PCB,也将程序计数器信息拷贝了,所以是从fork之后开始执行
问题2:父进程先执行还是子进程先执行?
父进程先运行
问题3:创建出子进程之后父子进程谁先执行?
不确定,父子进程是抢占式执行的(CUP少,进程多,并发执行)。
有了上面这些基础,来开始学习一下如何创建和杀死僵尸进程。
2.僵尸进程
僵尸状态:Z 僵死状态(Zombies)是一个比较特殊的状态,进程退出,但是资源没有被完全释放的一种等待处理状态。
产生原因:当子进程退出并且父进程没有读取到子进程退出的返回代码(子进程退出原因)时就会产生僵死(尸)进程。僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态
僵尸进程的危害:子进程占用了操作系统的资源(PCB需要维护它一直保存着它的信息),造成内存泄漏。每个用户有最大进程数量限制。
模拟僵尸状态:子进程退出+父进程进入死循环。
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t ret = fork();
if(ret<0){
printf("ret:%d\n",ret);
return -1;
}
else if(ret ==0){
//child
printf("父进程:I :%d\n",getpid());
printf("father :%d\n",getppid());
sleep(30);
}
else{
//father
while(1){
sleep(1);
printf("子进程:I :%d\n",getpid());
printf("father :%d\n",getppid());
}
执行程序可以看到以下信息:
如何杀死僵尸进程:
1.重启操作系统 ,虽然可以,但显然不是首选
2.杀掉父进程,这样子进程就会被1号进程领养,1号进程会回收该子进程的资源:
如何杀死一个进程:找到进程的PID,然后使用kill命令
kill [PID] 杀死一个进程
kill -9 [PID] 强杀一个进程
3.进程等待,使用wait(),waitpid()函数等待子进程的退出。
3.孤儿进程
父进程先于子进程退出,子进程就成了孤儿进程,会被1号进程所领养,
注意:没有孤儿状态
为了看到现象,将子进程设为死循环,父进程在30S后结束,上面代码稍微改一下就成了
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t ret = fork();
if(ret<0){
printf("ret:%d\n",ret);
return -1;
}
else if(ret ==0){
//child
while(1){
sleep(1);
printf("子进程:I :%d\n",getpid());
printf("father :%d\n",getppid());
}
}
else{
//father
printf("父进程:I :%d\n",getpid());
printf("father :%d\n",getppid());
sleep(30);
}
杀死孤儿进程就比较简单了: kill [PID] 杀死一个进程
kill -9 [PID] 强杀一个进程
孤儿进程特点:脱离终端,运行在后台;父进程成为1号进程。