linux学习笔记5 进程

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;
}

结果

猜你喜欢

转载自blog.csdn.net/qq_34489443/article/details/87300096