1.创建进程函数fork()
函数原型:pid_t fork(void)//pid_t是代表进程id的数据类型
该函数会产生两个进程,一个是父进程,一个是子进程,其中两个进程都有返回值。
其中父进程的返回值是子进程的id;
子进程的返回值是0;
2.创建单个进程
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
using namespace std;
int main()
{
cout<<"Begin ... \n";
pid_t pid = fork();//此时会有两个进程,可以理解为同时将这段程序以后的程序执行两次,并且每一次的pid值不一样
if (pid<0){
exit(1);
}//一般不会出现这种情况,只有当CPU资源紧张时,创建进程会失败,就会出现pid<0的情况
else if(pid==0){
cout<<"i am a child pid = "<<getpid()<<"ppid = "<<getppid()<<endl;
}//如果是子进程就执行这个判断
else if(pid>0){
cout<<"childpid = "<<pid<<"self = "<<getpid()<<"ppid = "<<getppid()<<endl;
sleep(1);//如果这个sleep加到子进程当中,就会出现父进程先结束,子进程变成孤儿进程在结束的情况,结果见下图
}//如果是父进程就执行这个判断
cout<<"End ...\n";
return 0;
}
结果图
注意:如果sleep加错了,结果如图所示
孤儿进程的显示就是这样的。
3.创建多个进程
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
using namespace std;
int main()
{
cout<<"Begin ... \n";
for (int i=0; i<5; i++){
pid_t pid = fork();
if (pid<0){
exit(1);
}
else if(pid==0){
cout<<"i am a child pid = "<<getpid()<<"ppid = "<<getppid()<<endl;
break;//如果没有break,每个子进程就继续创造出新的子进程,这样子进程的个数就不止五个了,就会有32个,所以需要加break
}
else if(pid>0){
cout<<"i am a father pid = "<<getpid()<<"ppid = "<<getppid()<<endl;
}
}
while(1){
sleep(1);
}
return 0;
}
结果图
、
多个子进程控制顺序
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
using namespace std;
int main()
{
cout<<"Begin ... \n";
int i=0;
for (; i<5; i++){
pid_t pid = fork();
if (pid<0){
exit(1);
}
else if(pid==0){
cout<<"i am a child pid = "<<getpid()<<"ppid = "<<getppid()<<endl;
break;
}
else if(pid>0){
cout<<"i am a father pid = "<<getpid()<<"ppid = "<<getppid()<<endl;
}
}
sleep(i+5);//利用标号来控制每个子进程,只能通过软件的方式来控制
if (i<5)
cout<<"i am a child, will exit, pid = "<<getpid()<<endl;
else
cout<<"i am a father, will exit, pid = "<<getpid()<<endl;
return 0;
}
4.execl函数
execl函数是调用其他程序取代进程中原有程序来执行,常用的是两个函数
函数原型 int execl(const char *path, const char *arg, ... , NULL)
int execlp(const char *file, const char *arg, ... , NULL)
这两个函数在正常执行时没有返回值,只有在出现错误的时候才会有返回值。execl的第一个参数是文件的路径,execlp的第一个参数是文件名,因为execlp默认是在环境变量的路径中查找。const char *arg代表的是调用函数的入口参数,第一个参数必须是函数名。最后一个参数必须是NULL,作为哨兵。并且除去NULL,其他参数都要加引号。
程序举例
#include <iostream>
#include <unistd.h>
int main()
{
execl("/bin/ls", "ls", "-l", NULL);
return 0;
}
int main()
{
execlp("ls", "ls", "-l", NULL);
return 0;
}
注:excel调用自定义程序有疑问,不会做(?)
5 wait函数
函数原型:pid_t wait(int *status)
函数作用:1.阻塞等待(如果子进程还在运行,就一直等待,直到子进程结束运行再回收资源)
2.回收子进程资源
3.查看死亡原因(通过status值来查看)
程序举例:
#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
using namespace std;
int main()
{
pid_t pid=fork();
int status;
if (pid==0){
cout<<"i am a child, i will die."<<endl;
sleep(2);
return 101;
}
else if (pid>0){
cout<<"i am a father."<<endl;
pid_t wpid=wait(&status);//这里的&status是表示取status变量的地址,因为这里应该输入的是指针参数,自己理解为输入一个指针参数,然后函数在该地址中填入对应的状态变量。
//如果不需要查看状态,可使用NULL为参数。
cout<<"pid = "<<pid<<"wpid = "<<wpid<<endl;
if (WIFEXITED(status))
cout<<"child exited because of "<<WEXITSTATUS(status)<<endl;
if (WIFSIGNALED(status))
cout<<"child was killed by "<<WTERMSIG(status);
//WIFEXITED是宏定义,判断是否为正常终止,如果返回值为正,则是正常终止,WEXITSTATUS是在正常终止的情况下判断是哪一种情况,一般都是子进程的返回值。
//WIFSIGNALED也是宏定义,判断是否为非正常终止,也就是信号源杀死,如果返回值为正,是非正常终止,WTERMSIG是正常终止的情况下判断哪一种情况
while(1);
}
return 0;
}
运行结果图:
6.waitpid函数
函数原型:pid_t waitpid(pid_t pid, int *status, int options)
其中第一个参数pid取值有四种情况(主要使用的是-1)
pid<-1:回收组id是pid值的绝对的值的那一组中所有的子进程
pid=-1:回收任何子进程
pid=0:回收正在运行的进程组中的子进程
pid>0:回收id号为pid值的子进程
第二个参数status与wait()函数中参数一样作用,反映结束原因
第三个参数options,主要使用以下两个参数
options=0:阻塞等待,和wait作用一样,一直等到有子进程运行完成,再去结束进程,这个函数才运行完。
options=WNOHANG:如果没有子进程运行完成,就立即离开。
注意:返回值如果设置了WNOHANG
没有子进程退出,返回0,有子进程退出,返回子进程id,不存在子进程,返回-1
返回值如果没有设置WNOHANG,就和wait()函数一样。
程序举例如下:
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;
int main()
{
pid_t pid=fork();
if (pid>0){
cout<<"i am a father, pid = "<<getpid()<<endl;
while (waitpid(-1, NULL, WNOHANG)<=0);//这一语句相当于wait(NULL)
//如果不加while循环判断,子进程就会变成僵尸进程,因为子进程和父进程是几乎同时进行的,当父进程运行到回收这一步时,子进程还没运行结束,所以回收不了子进程就退出了。
while (1);
}else if (pid==0){
cout<<"i am a child, pid = "<<getpid()<<endl;
sleep(2);
}
}
7.回收多个子进程
用wait回收
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;
int main()
{
int i=0;
for (; i<5; i++){
pid_t pid=fork();
if (pid==0){
cout<<"i am a child, pid = "<<getpid()<<endl;
break;
}
}//创建5个子进程
if (i==5){
for (; i>0; i--){
pid_t pid=wait(NULL);
cout<<"wait pid = "<<pid<<endl;
}
while (1);
}//父进程执行内容
else {
sleep(1);
}//子进程执行内容
return 0;
}
结果
用waitpid回收
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;
int main()
{
int i=0;
for (; i<5; i++){
pid_t pid=fork();
if (pid==0){
cout<<"i am a child, pid = "<<getpid()<<endl;
break;
}
}
if (i==5){
for (; i>0; i--){
while(1){
pid_t pid=waitpid(-1, NULL, WNOHANG);
if (pid==-1) break;
else if (pid==0) continue;
else if (pid>0){
cout<<"wait pid = "<<pid<<endl;
}
}//这样判断比较清楚,如果直接将判断放在while循环中,可能出现问题,推荐while(1),然后在循环中分步判断waitpid参数。
}
while (1);
}//父进程执行程序
else {
sleep(1);
}//子进程执行程序
return 0;
}
结果