6.vfork创建进程
下图是fork与vfork的区别:
下面我们来对比一下fork和vfork的区别:首先看fork的代码(demo8.c)
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t pid;
printf("father pid = %d\n",getpid());
pid = fork();
if(pid > 0)
{
while(1){
printf("this is father print,pid = %d\n",getpid());
sleep(1);
}
}
else if(pid == 0){
while(1){
printf("this is child print,child pid = %d\n",getpid());
sleep(1);
}
}
return 0;
}
下图是运行的结果图:可以看出父子进程执行争夺cpu不一定是一人一次,有可能父先也有可能是子先(取决于进程的调度)。
当改成vfork的时候,都是子进程先运行,直到子进程退出父进程才运行。代码是demo11.c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
pid_t pid;
int cnt = 0;
printf("father pid = %d\n",getpid());
pid = vfork();
if(pid > 0)
{
while(1){
printf("this is father print,pid = %d\n",getpid());
sleep(1);
printf("cnt=%d\n",cnt);//这里打印出来的数据是经过子进程修改的,即子进程是用父进程的空间。
}
}
else if(pid == 0){
while(1){
printf("this is child print,child pid = %d\n",getpid());
sleep(1);
cnt++;
if(cnt == 3){
exit(0);//这里之前用break退出,破坏了cnt的值,所以子进程退出要用好的方式退出。
}
}
}
return 0;
}
下图是运行结果图(子进程运行结束退出后父进程才开始运行)
7.进程退出
退出情况有两种:1、正常退出;2、异常退出
正常退出情况方式如图:
异常退出情况的方式如图:
正常退出的函数如下:(建议用exit函数退出,因为它是对另外两个函数的封装,会对缓冲区的内容做一些处理再退出,另外两个直接退出)
8.父进程等待子进程退出(一)
子进程退出的状态码(exit(3)),那么父进程wait(状态码)不会等于3,需要经过下图的宏解析一下才可以等于3。
****补充:WIFEXITED(status)是可以判断子进程是否正常退出,用法比如if(WIFEXITED(status)){ 用WEXITSTATUS(status)//解析退出码 }else{ }
子进程退出状态不被收集就是僵尸进程,代码为(demo12.c)写的比较全面(啰嗦),这里是用vfork创建的子进程,用fork也是一样(demo13.c 这里没有写,运行结果是cnt没有变成3,而是0,因为fork的子进程不与父进程共享空间)的,子进程也会变成僵尸进程。
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
pid_t pid;
int cnt = 0;
printf("father pid = %d\n",getpid());
pid = vfork();//pid = fork();
if(pid > 0)
{
while(1){
printf("this is father print,pid = %d\n",getpid());//这一段子进程的退出码都没有被收集
sleep(1);
printf("cnt=%d\n",cnt);
}
}
else if(pid == 0){
while(1){
printf("this is child print,child pid = %d\n",getpid());
sleep(1);
cnt++;
if(cnt == 3){
exit(0);
}
}
}
return 0;
}
僵尸进程的图如下:
使得父进程等待子进程退出有以下几个api(使用了api就可以使得子进程不变成僵尸进程),本节主要使用wait函数就可以使得子进程变为非僵尸进程,但是,其状态值的存放有两种情况(参数是NULL或这非空),下图是wait函数的原函数及其他api部分讲解
情况1(wait(NULL))
父进程等待子进程退出但是不收集子进程的退出状态代码(也就是多了wait(NULL)demo14.c)
这里只是父进程部分代码,因为其他的部分一样的和上面代码
if(pid > 0)
{
wait(NULL);//就是调用wait函数(子进程先运行,父进程在等待),但是并不关心子进程的退出码,但是就是因为有这个wait,子进程不再是僵尸进程。
while(1){
printf("this is father print,pid = %d\n",getpid());
sleep(1);
printf("cnt=%d\n",cnt);
}
}
代码运行结果为:
情况2(wait(参数))
父进程创建子进程干活,子进程退出,父进程并把子进程的退出码获取,并经过宏解析的代码(demo15.c)
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
pid_t pid;
int cnt = 0;
int status =10;
printf("father pid = %d\n",getpid());
pid = fork();
if(pid > 0)
{
wait(&status);//父进程获取退出码,切记取的是地址
printf("child quit,child status = %d\n",WEXITSTATUS(status));//用宏解析退出码打印子进程退出码
while(1){
printf("this is father print,pid = %d\n",getpid());
sleep(1);
printf("cnt=%d\n",cnt);
}
}
else if(pid == 0){
while(1){
printf("this is child print,child pid = %d\n",getpid());
sleep(1);
cnt++;
if(cnt == 5){
exit(3);//子进程退出
}
}
}
return 0;
}
运行结果如下图:(退出码为3,正确的)
9.父进程等待子进程退出(二)
父进程等待子进程有以下几个API上面使用的是wait等待子进程的退出,这里就是wait与waitpid的区别图如下:
下图是对waitpid参数的解读(这个api实战中用的并不多,目前没有好的场景运用,先看看怎么调用吧,(在demo16.c)):
废话不多说,直接撸代码(demo16.c)这里就是修改了父进程的代码,其他和上面的代码一样。
if(pid > 0)
{
//wait(&status);
waitpid(pid,&status,WNOHANG);//就是把wait替代成这个api,子进程和父进程都在运行,父进程没有被阻塞。
printf("child quit,child status = %d\n",WEXITSTATUS(status));
while(1){
printf("this is father print,pid = %d\n",getpid());
sleep(1);
printf("cnt=%d\n",cnt);
}
}
运行结果如图:(经过ps指令查询,子进程不一定是僵尸进程,我这边不是僵尸进程,老陈的是僵尸进程)
孤儿进程
下图是孤儿进程的概念:
编程验证:
新API :在子进程中获取父进程的pid号函数是(getppid())
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
pid_t pid;
int cnt = 0;
pid = fork();
if(pid > 0)
{
printf("this is father print,father pid = %d\n",getpid());//父进程提早结束
}
else if(pid == 0){
while(1){
printf("this is child print,child pid = %d,my father pid=%d\n",getpid(),getppid());
sleep(1);
cnt++;
if(cnt == 5){
exit(3);
}
}
}
return 0;
}
运行结果如下图:(孤儿进程被图形界面收了,所以pid是1693,所以需要进入字符界面运行,才会被init的子进程接管(https://blog.csdn.net/u012206617/article/details/93088996介绍init知识),如何进入字符界面运行?)
补充一个linux系统编程的书写的比较好的demo