1.进程创建(fork)
#include<iostream>
#include<unistd.h>
using std::cout;
using std::endl;
int main()
{
if(fork()==0)
{
cout<<"child:"<<"I am child"<<endl;
}
else
{
cout<<"parent:"<<"I am parent"<<endl;
}
return 0;
}
fork的头文件unistd.h
fork的返回值子进程:0,父进程:子进程的pid
1.1子进程的PCB、代码和数据
写实拷贝原理
2.进程终止
2.1进程退出的三种场景:
正常退出,结果正确;
正常退出,结果错误;
代码异常终止
2.2常见退出方法:return
正常退出,结果正确:正常执行,return 0;
正常退出,结果错误:return 非0;
echo $?//返回最近一次执行的返回值
将错误码转化为错误信息
#include<iostream>
#include<string.h>
using std::cout;
using std::endl;
int main()
{
for(int i=0;i<140;i++)
{
cout<<strerror(i)<<endl;
}
return 0;
}
2.3代码异常退出,下面这个除零错误就是一个简单异常终止
#include<iostream>
using std::cout;
using std::endl;
void func(int* tmp)
{
*tmp=*tmp/0;//除零错误
}
int main()
{
int tmp=10;
func(&tmp);
cout<<"hello world";
return 0;
}
执行结果:
2.4_exit和exit.return的区别
#include<iostream>
#include<stdlib.h>
using std::cout;
using std::endl;
int main()
{
cout<<"hello world";
exit(1);
return 0;
}
#include<iostream>
#include<unistd.h>
using std::cout;
using std::endl;
int main()
{
cout<<"hello world";
_exit(1);
return 0;
}
exit的头文件<stdlib.h>,_exit的头文件<unistd.h>;
exit和return结束会做收尾工作例如:刷新输出缓冲区,_exit则不会;
exit和return和_exit都是正常退出,结果正确或者不正确
3.进程等待
3.1进程等待的必要性
原因:当子进程退出,父进程不管不顾(父进程是等待状态),就会进入“僵尸进程“,即使使用信号“kill -9“也杀不死它,一直等待父进程接收子进程的执行结果,导致内存泄漏;
3.2查看man手册
3.3学习waitpid()接口
3.3.1waitpid的第一个参数:子进程的pid或者-1
waitpid的返回值:等待成功子进程pid,等待失败-1;
[ldj@VM-24-5-centos process_control]$ cat mycode.cc
#include<iostream>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
using namespace std;
int main()
{
int pid=fork();//创建子进程
if(pid==0)
{
for(int i=0;i<5;i++)//子进程执行5s
{
cout<<"I am child "<<getpid()<<endl;
sleep(1);
}
}
else
{
cout<<"wait begin!"<<endl;
int ret=waitpid(-1,NULL,0);//
if(ret>0)
cout<<"wait success!"<<ret<<endl;
else
cout<<"wait fail!"<<endl;
}
return 0;
}
3.3.2waitpid的第二个参数:
获取子进程退出结果(场景):
正常退出,结果正确;
正常退出,结果错误;
代码异常终止
解释:退出信号为0,退出状态的值才有价值,如果退出信号非0那么退出状态不采用;退出信号非0说明是异常退出
#include<iostream>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>
using std::cout;
using std::endl;
int main()
{
int pid=fork();
if(pid==0)
{
for(int i=0;i<5;i++)
{
cout<<"I am child "<<getpid()<<endl;
sleep(1);
}
exit(20);
}
else
{
cout<<"wait begin!"<<endl;
int status=0;
int ret=waitpid(pid,&status,0);
if(ret>0)
{
cout<<"exit status:"<<((status>>8)&0xFF)<<" exit signal:"<<(status&0x7F)<<endl;
cout<<"wait success!"<<ret<<endl;
}
else
cout<<"wait fail!"<<ret<<endl;
}
return 0;
}
执行结果:
3.3.2.1.WIFEXITED和WEXITSTATUS当没有信号时可以使用这样的接口来获得子进程的返回值
#include<iostream>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>
using std::cout;
using std::endl;
int main()
{
int pid=fork();
if(pid==0)
{
for(int i=0;i<5;i++)
{
cout<<"I am child "<<getpid()<<endl;
sleep(1);
}
exit(20);
}
else
{
cout<<"wait begin!"<<endl;
int status=0;
int ret=waitpid(pid,&status,0);
if(ret>0)
{
cout<<"wait success!"<<ret<<endl;
if(WIFEXITED(status))//没有信号为真
cout<<WEXITSTATUS(status)<<endl;//输出子进程的返回值
//cout<<"exit status:"<<((status>>8)&0xFF)<<" exit signal:"<<(status&0x7F)<<endl;
}
else
cout<<"wait fail!"<<ret<<endl;
}
return 0;
}
3.3.3waitpid的第三个参数:控制等待方式阻塞等待和非阻塞等待
#include<iostream>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>
using std::cout;
using std::endl;
int main()
{
int pid=fork();
if(pid==0)
{
for(int i=0;i<5;i++)
{
cout<<"I am child "<<getpid()<<endl;
sleep(1);
}
exit(20);
}
else
{
cout<<"wait begin!"<<endl;
int status=0;
while(1)
{
int ret=waitpid(pid,&status,WNOHANG);
if(ret==0)
{
cout<<"I'm woking."<<endl;
}
else if(ret>0)
{
cout<<"wait success!"<<ret<<endl;
if(WIFEXITED(status))
cout<<"exit code:"<<WEXITSTATUS(status)<<endl;
break;
}
else{
cout<<"wait fail!"<<ret<<endl;
break;
}
sleep(1);
}
}
return 0;
}
执行结果:
4.进程程序替换
Q:为什么需要进程程序替换?
A:当进程想执行一个新程序;
原理:代码和数据都采用写实拷贝的方法
#include<iostream>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
using namespace std;
int main()
{
cout<<"command begin."<<endl;
execl("/usr/bin/ls"/*全路径*/,"ls","-a","-l"/*和命令行上执行写法一样,要使用NULL结尾*/,NULL);
cout<<"command end."<<endl;
return 0;
}
执行结果:
4.1进程程序替换接口的规律
exec接口有很多
带'l'的,第二个参数使用可变参数列表;
带'v'的,的二个参数使用数组;
#include<iostream>
#include<unistd.h>
using namespace std;
int main()
{
cout<<"command begin."<<endl;
char* argv[]={"ls","-a","-l",NULL};
execv("/usr/bin/ls",argv);
cout<<"command end."<<endl;
return 0;
}
执行结果:
带'p'的,第一个参数不在用写全路径,写文件名,在环境变量PATH中找
#include<iostream>
#include<unistd.h>
using namespace std;
int main()
{
cout<<"command begin."<<endl;
execlp("ls"/*τ???*/,"ls","-a","-l",NULL);
cout<<"command end."<<endl;
return 0;
}
带'e'的,进程程序替换不使用默认环境变量,手动输入;
//打印环境变量程序
#include<iostream>
using namespace std;
int main()
{
extern char** environ;
for(int i=0;environ[i]!=NULL;i++)
{
cout<<environ[i]<<endl;
}
return 0;
}
#include<iostream>
#include<unistd.h>
using namespace std;
int main()
{
cout<<"command begin."<<endl;
char* env[]={"hello wolrd","hello world",NULL};
execle("./printfenv"/*τ¼þĻ*/,"./printfenv",NULL,env);
cout<<"command end."<<endl;
return 0;
}
执行结果: