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分钟带你熟悉进程的等待与替换!!!的主要内容,如果未能解决你的问题,请参考以下文章