天天看点

进程创建函数fork()和vfork()

Linux下使用fork()创建一个新的进程,该函数不需要参数,返回值是一个进程id。对于不同的对象,分别是:新建的子进程id(返回给父进程),0(返回给创建的子进程)或者-1(子进程创建失败,返回给父进程)。创建的新进程在得到一个新的进程id之后,将拷贝父进程空间中内容至子进程,包括父进程的数据段和堆栈段,并且子进程和父进程共享代码段。vfork()和fork()则不一样,父子进程共享地址空间,包括代码段、数据段和堆栈段。子进程对于共享数据的修稿会影响到父进程,而且子进程一定比父进程先运行完,父进程调用vfork()函数后等待子进程运行完成再接着运行。

接下来,使用fork()创建一个子进程,并且在子进程中显示父进程的进程号和子进程的进程id,在父进程中显示出父进程的进程id和子进程的进程id。

#include<iostream>
#include<stdlib.h>
#include<unistd.h>
using namespace std;

int main(){
	pid_t pid;
	pid = fork();
	if(pid < 0){
		cout << "Create fail!" << endl;
		exit(0);
	}
	else if(pid == 0){
		cout << "This is the child process! PID is: " << getpid() << endl;
		cout << "The parent process id is: " << getppid() << endl;
	}
	else{
		cout << "This is the parent process! PID is: " << getpid() << endl;
		cout << "The child process id is: " << pid << endl;
	}
	return 0;
}
           

编译并运行:

进程创建函数fork()和vfork()

发现在子进程中,由函数getppid()返回的进程号与对应的父进程的进程号并不一致。在父进程中,能够正确返回子进程的进程id。利用ps命令查看子进程返回的父进程id对应的进程:upstart,后来在博客https://blog.csdn.net/Leafage_M/article/ details/70273624中看到一个很清楚详细的解释。shell中先打印出父进程的进程信息,然后再打印出子进程的进程信息,那么就相当于父进程完成之后,被杀死了,然后再执行的是子进程的信息,而此时子进程就成了孤儿进程,它被upstart这个进程收养了,此时调用getppid()返回的当然就是upstart的pid了。对于这种问题的解决方法之一就是在父进程的执行语句中加入延时挂起sleep()操作,让父进程不至于太快销毁。

之前说到fork()和vfork()的区别,编写两个实例进行验证:

#include<iostream>
#include<stdlib.h>
#include<unistd.h>
using namespace std;

int global; //全局变量(数据段中)
int main(){
	pid_t pid;
	int stack = 1;  //局部变量(栈中)
	int* heap;  //局部变量(堆中)
	heap = (int* )malloc(sizeof(int));
	*heap = 2;
	pid = fork();
	if(pid < 0){
		cout << "Fail to fork!" << endl;
		exit(1);
	}
	else if(pid == 0){
		global ++;
		stack ++;
		(*heap)++;
		cout << "This is the child process! PID is: " << getpid() << endl;
		cout << "The parent process id is: " << getppid() << endl;
		cout << "The child data: " << global << " " << stack << " " <<  *heap << endl;
		exit(0);
	}
	else{   
		sleep(3);  
		cout << "This is the parent process! PID is: " << getpid() << endl;
		cout << "The child process id is: " << pid << endl;
		cout << "The parent data: " << global << " " << stack << " " << *heap << endl;
		exit(0);
	}
	return 0;
}
           

之后再将上面程序中fork()函数改为vfork(),并将延时语句注释掉。编译和运行结果对比:

进程创建函数fork()和vfork()

可以看出,fork()创建的子进程中三个变量都发生变化时,父进程虽然在子进程之后才访问数据,但是子进程中对变量的修改并不影响父进程的进程环境。但是vfork()则不一样。再看进程id,在加入sleep()函数之后,fork()函数创建的子进程也能够正常返回父进程的id了。而在vfork()函数创建的子进程中,父子进程的返回的父进程、子进程id皆一致。

fork()函数在两种情况下容易出错:系统中已经有很多进程,调用fork()函数的用户进程过多。