3分钟带你熟悉进程的等待与替换!!!

Posted  落禅

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了3分钟带你熟悉进程的等待与替换!!!相关的知识,希望对你有一定的参考价值。

进程的相关操作

main函数返回值:返回给操作系统

echo $?//查看最近一次程序退出码

进程退出码:进程退出时的返回值

0:正常退出
!0:退出异常

各个退出码所对应的各个文字信息,例如下面:

#include<stdio.h>
#include<string.h>
int main()
{
    for(int i=0;i<100;i++)
    {
        printf("%d:%s\\n",i,strerror(i));
    }
    return 0;
}

进程等待:

为了回收子进程资源,获得子进程退出信息

如果子进程退出了,而父进程没有回收子进程的资源,对子进程不管不顾,那么子进程就会变成僵尸进程,就算是kill
-9也无法杀死,,所以必须对子进程资源进行回收,而这个回收工作通常由父进程来完成,父进程完成子进程资源回收的这个过程叫做进程等待

关于子进程资源回收我们经常用wait/waitpid进行回收

父子进程谁先运行不一定,但是在进程等待之后,子进程先退出,父进程在回收子进程资源后再退出

wait操作

pid_t wait(int status)
返回值:
返回成功返回子进程pid,失败返回-1
参数:
输出型参数,不关心设置为NULL
#include<stdio.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<unistd.h>

int main()
{
    pid_t id=fork();
    if(id==0)
    {
        int count=0;
        while(count<5)
        {
            printf("I am child ,pid:%d,ppid:%d\\n",getpid(),getppid());
            sleep(1);
            count++;
        }
        exit(1);
    }
    else
    {
        int ret=wait(NULL);
        if(ret>=0)
        {
            printf("child exit success:pid:%d\\n",ret);
        }
        printf("Father is running\\n");
        sleep(5);

    }
    return 0;
}

wait获取子进程相关信息成功,返回子进程的pid,返回失败返回-1

waitpid(pid_t id int*status,int options),头文件是<sys/wait.h>

waitpid当正常返回时返回子进程的id,失败时返回0

阻塞等待的方式

#include<stdio.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
    pid_t id=fork();
    if(id==0)
    {
        int count=0;
        while(count<5)
        {
            printf("I am child,pid:%d,ppid:%d\\n",getpid(),getppid());
            sleep(1);
            count++;
        }
        exit(10);
    }
    else
    {
        int status=0;
        //waitpid(id,&status,0),最后一个参数为0代表的是阻塞状态,代表父进程在等待子进程的时候没有做其他的事情
        //status次低八位代表退出时的退出码(status>>8)&0xFF代表退出时的退出码
        //status低七位代表退出时的信号,status&0x7F获得进程退出时的信号
        pid_t ret=waitpid(id,&status,0);
        if(ret>=0)
        {
            printf("child exit success,ret:%d\\n",ret);
            printf("child exit code:%d\\n",(status>>8)&0xFF);
            printf("child exit signal:%d\\n",status&0x7F);
        }
        printf("Father is running\\n");
        sleep(5);
    }
    return 0;
}

等待成功,并不意味着进程运行成功了,仅仅代表着进程退出了,要看进程是否运行成功还要看进程退出时发出的信号,例:

如上所示,我们利用kill -9强行杀死子进程,但是上面依然显示等待成功,所以退出码并不能代表进程运行是否成功了,还要看退出信号

上面我们利用status来说明退出信息和退出码比较繁琐,我们一般不使用,经常使用下面这种

WIFEXITED(status):如果正常终止子进程返回的状态,则为真(查看进程是否正常退出),本质是查看信号是否正常

WEXITSTATUS(status):若WIFEXITED为真,则提取子进程的退出码(查看进程的退出码)(这时候的退出码就代表子进程正常执行执行完毕了)
#include<stdio.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
    pid_t id=fork();
    if(id==0)
    {
        int count=0;
        while(count<20)
        {
            printf("I am child,pid:%d,ppid:%d\\n",getpid(),getppid());
            sleep(1);
            count++;
        }
        exit(10);
    }
    else
    {
        int status=0;
        //使用waitpid进行进程等待
        pid_t ret=waitpid(id,&status,0);
        //WIFEXITED(status):如果进程正常退出的,那么返回值就为真,异常退出返回值为假
        if(WIFEXITED(status))
        {
            printf("child exit success\\n");
            //打印退出码
            printf("child exit code:%d\\n",WEXITSTATUS(status));
        }
        //走到这代表子进程异常退出
        else
        {
        	//打印子进程退出时的退出信号
            printf("child exit signal:%d\\n",status&0x7F);
        }
        printf("Father is running\\n");
        sleep(5);
    }
    return 0;
}

非阻塞等待:

父进程在等待子进程是不等待,还在做其他事情

int main()
{
    pid_t id=for();
	if(id==0)
    {
        int count=0;
        while(cout<10)
        {
            printf("I am child,pid:%d,ppid%d",getpid(),getppid());
            sleep(1);
            count++;
        }
        exit(1);
    }
    while(1)
    {
        int status=0;
        pid _t ret=waitpid(id,&status,WNOHANG);
        if(ret>0)
        {
            printf("watit success\\n");
            printf("exit codfe:%d",WEXITSTATUS(staus));
            break;
        }
        else if(ret==0)
            //子进程没有退出,等待是成功的
            printf("father do   other thing ret:%d\\n",ret);
        else
        {
            printf("waitpid 
                  error\\n");
                   break;
        }
    }

    return 0;
}
WNOHANG:
ret=waitpid(id,&status,WNOHANG);
ret==0代表等待成功,但是子进程还在执行
ret==-1等待失败
ret>0等待成功,子进程还在运行
ret==0,当前子进程还没有退出

进程替换

用fork创建子进程后执行的是和父进程相同的程序(但是可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序,当进程调用一种exec函数时,该进程的用户空间的代码和数据就会被新程序替换,从新程序的启动例程开始执行,调用exec并不会创建心得进程,只是发生了进程替换,所以调用exec前后该进程的id并未发生变化

替换函数

#include<unistd.h>
execl("/usr/bin/ls","ls","-al",NULL);
execlp("ls","ls","-a","-l",NULL);
char*envp[]={"/usr/bin/ls",NULL};
execle("ls","ls","-a","-l",NULL,envp);
char*argv[]={"ls","-l","-a",NULL};
execv("/usr/bin/ls",argv);
execvp(“ls”,argv);

execve("/usr/bin/ls",argv,envp);

函数理解:

这些函数如果调用成功则加载新的程序从开始代码开始执行,不在返回

如果调用出错返回-1

所以exec函数只有出错时的返回值而没有成功是的返回值

命名规则:(exec+后缀)

l:代表列表,将所有选项列出来,需要自己写路径,

execl("usr/bin/ls","ls","-a","-l",NULL);

p:代表环境变量,也就是可以不用写环境变量

execlp("ls","ls","-a","-l",NULL);

e:envp表示自己配置环境变量,将环境变量保存到数组中

char*envp[]={"/usr/bin/ls",NULL};
execle("ls","ls","-a","-l",envp);

v:代表参数用数组

char*argv[]={"ls","ls","-a","-l",NULL};
execv("usr/bin/ls",argv);

vp:有环境变量了

char*argv[]={"ls","ls","-a","-l",NULL};
execvp(argv);

ve:带e,需要自己写全路径

execve("/bin/ls",argv,envp);

ps:通过替换函数我们可以将两个不同类型的程序相连接,exec函数可以连接,c++,python,java等语言所写的程序

简易shell的实现
本质是通过exec系列函数实现系统接口的调用

#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<sys/types.h>
#define Len 1021
int main()
{
    char cmd[Len];
    while(1)
    {
        printf("sjw@iZ2zedu4njy79sqivntvprZ test_9_29_execl$");//伪装Linux输入命令时的开头
        fgets(cmd,Len,stdin);//将输入内容保存到cmd数组中
        cmd[strlen(cmd)-1]='\\0';
        char *myargv[Len];
        pid_t id=fork();
                int i=1;
            myargv[0]=strtok(cmd," ");//以空格进行分割
            while(myargv[i]=strtok (NULL," "))
            {
                i++;
            }
        if(id==0)
        {
    		execvp(myargv[0],myarg);//调用execvp函数
            exit(1);
        }
        else
        {
        	//父进程wait,等待子进程退出
            int status=0;
            pid _t ret=waitpid(id,&status,0);
            id(WIFEXITED(status))
            {
                printf("exit code:%d\\n",WEXITSTATUS(status));
            }
        }
        
    }
    return 0;
}

以上是关于3分钟带你熟悉进程的等待与替换!!!的主要内容,如果未能解决你的问题,请参考以下文章

3分钟带你熟悉进程的等待与替换!!!

3分钟带你熟悉进程的等待与替换!!!

Linux详解 --- 进程管理3 (进程创建进程终止进程等待进程替换)

Linux进程控制--进程的等待与替换

Linux进程控制--进程的等待与替换

五分钟带你了解 计算机操作系统——进程与线程(万字详解·图文)