linux 系统编程 --再谈进程(七)

本文探讨Linux中进程的五种状态及其不同划分方式,详细解析了进程创建、子进程管理以及如何避免僵尸进程。通过示例代码阐述了如何控制进程执行顺序,解释了孤儿进程和僵尸进程的概念,以及wait和waitpid函数在回收子进程资源中的作用。重点讲解了execl系列函数在执行程序中的应用。
摘要由CSDN通过智能技术生成

经典linux把进程分为5个状态,运行态,停止态,等待态(两种),僵死态。
还有一种划分法: 就绪态,运行态,挂起态,停止态。

在这里插入图片描述
如果想要父进程一次创建多个子进程,应该使用如下代码:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
    pid_t pid;
    int i;

    for(i=0;i<4;i++)
    {
        pid=fork();
        if (pid==-1){
            perror("fork");
            exit(1);
        }
        else if(pid==0)
            break;

    }

    if (i<4)
    {
        printf("i am %d child,pid=%u\n",i+1,getpid());
    }
    return 0;
}

如果想创建4个子进程,直接写fork的话,实际上会创建出2的4次方-1个,因为每一个子进程在循环时也在调用fork,创建出了孙进程。
所以必须屏蔽掉子进程本身的创建,核心在于如果子进程运行(pid=0)推出循环。
运行结果:
在这里插入图片描述
再详细梳理一下代码的执行流程,在if语句中添加

  else if(pid==0)
            break;
        else
        {
            printf("parant\n");
        }
        

执行结果:
在这里插入图片描述
应该是正在执行一次父进程,再执行一次子进程的交替过程。
这个结果不一定能保证准确,取决于调度,为了尽量让它按照顺序走,可以添加sleep语句(也不是100%)。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
    pid_t pid;
    int i;

    for(i=0;i<4;i++)
    {
        pid=fork();
        if (pid==-1){
            perror("fork");
            exit(1);
        }
        else if(pid==0)
            break;
        else
        {
            printf("parant\n");
        }
        

    }

    if (i<4)
    {
       // sleep(i);
        printf("i am %d child,pid=%u\n",i+1,getpid());
    }
    else
    {
        sleep(3);
        printf("parant\n");

    }
    return 0;
}

这样基本能保证父进程是最后一个结束的。
在这里插入图片描述
当代码有了微小变化后,其执行顺序就发生了变化:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
    pid_t pid;
    int i;

    for(i=0;i<4;i++)
    {
        pid=fork();
        if (pid==-1){
            perror("fork");
            exit(1);
        }
        else if(pid==0)
            break;
        else
        {
            printf("parant is runnig\n");
        }
        

    }

    if (i<4)
    {
       // sleep(i);
        printf("i am %d child\n,pid=%d",i+1,getpid());
    }
    else
    {
        sleep(3);
        printf("parant end\n");

    }
    return 0;
}

执行结果:
在这里插入图片描述
我们可以对子进程也使用sleep来控制它们的执行顺序,以达到相应子进程先结束,父进程最后结束。
把对子进程和父进程的处理方法,写在for循环函数之外,让架构更加清晰。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
    pid_t pid;
    int i;

    for(i=0;i<4;i++)
    {
        pid=fork();
        if (pid==-1){
            perror("fork");
            exit(1);
        }
        else if(pid==0)
            break;
       }

    if (i<4)
    {
        sleep(i);
        printf("i am %d child,pid=%d\n",i+1,getpid());
    }
    else
    {
        sleep(i);
        printf("parant end\n");

    }
    return 0;
}

在这里插入图片描述

孤儿进程:父进程先于子进程结束,内核会把这个子进程当作孤儿进程,并由init进程“领养”,由它来对子进程进行回收。

僵尸进程:子进程终止,父进程尚未回收,子进程会在内核中残留pcb,变成僵尸进程。僵尸进程无法由kill语句直接杀死,但可以通过kill杀死僵尸进程的父进程,然后僵尸进程转换为孤儿进程,并由init进程“领养”来回收。

进程回收:
一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,但它的pcb还保留着。如果是异常终止则pcb能保留终止的信号,以便查询,这个进程的父进程可以调用wait或waitpid函数获取这些信息,然后彻底清除掉这个子进程。

wait函数有3个功能:
①阻塞等待子进程退出。
②回收子进程残留资源。
③获取子进程结束状态(退出原因)。

对以上代码稍加修改,让父进程一直循环不结束(不回收),而子进程都已经结束,于是产生了僵尸进程。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
    pid_t pid;
    int i;

    for(i=0;i<4;i++)
    {
        pid=fork();
        if (pid==-1){
            perror("fork");
            exit(1);
        }
        else if(pid==0)
            break;
       }

    if (i<4)
    {
        sleep(i);
        printf("i am %d child,pid=%d\n",i+1,getpid());
    }
    else
    {
        sleep(i);
        while(1);
        //printf("parant end\n");

    }
    return 0;
}

在这里插入图片描述
调用一次wait只能回收一个子进程。调用成功返回子进程的id,错误返回-1.

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h> 
#include <sys/wait.h>


int main()
{
    pid_t pid;
    int i;

    for(i=0;i<4;i++)
    {
        pid=fork();
        if (pid==-1){
            perror("fork");
            exit(1);
        }
        else if(pid==0)
            break;
       }

    if (i<4)
    {
        sleep(i);
        printf("i am %d child,pid=%d\n",i+1,getpid());
    }
    else
    {
        while(wait(NULL));
        while(1)
        {
          
        };
        //printf("parant end\n");

    }
    return 0;
}

利用 while(wait(NULL)) 就可以完成多个子进程的回收,不会产生僵尸进程。
同时wait是阻塞父进程,那么sleep也没有必要了,能保证父进程最后结束。

waitpid函数功能更强大,pid_t waitpid(pid_t pid, int *status, int options);
第一个参数想回收哪一个子进程,如果参数是>0,那么就是指定的子进程ID;
如果参数=-1,回收任意子进程,相当于wait;
如果参数=0,回收当前调用waitpid一个组的所有子进程;
如果参数<-1,回收制定进程组内的任意子进程;

简易的轮询写法:
do
{
//
//每隔2秒钟去做
sleep(2);
}while()

无论是wait还是waitpid,参数status都保存了子进程终止时的信息,利用4个宏,就能分析该信息。
WIFEXITED()如果真,再调用WEXITSTATUS()获取子进程的退出状态,说白了就是找exit(n)里面那个小n的值。

WIFSIGNALED()如果真,再调用WTERMSIG()获取子进程终止的信号编号。

掌握execlp p–path 通常用来执行系统可执行程序调用
execl 通常用来执行用户自定义可执行程序调用

相当于c语言在windows下的system。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值