天天看点

Linux创建进程fork和vfork函数分析进程创建之fork函数 进程创建之vfork函数

进程创建之fork函数

头文件:#include<unistd.h>

               #include <sys/types.h>

函数原型:pid_t fork(void);             pid_t实际就是int,在sys/types.h中被宏定义的

返回值:1.创建进程失败---返回-1(失败原因有两个:1)当前的进程数已经达到了系统规定的上限,这时errno的值被设置为EAGAIN。2)系统内存不足,这时errno的值被设置为ENOMEM)

                2.创建成功:子进程返回0,父进程返回子进程ID

函数说明:一个现有的进程调用fork函数创建一个新的进程,新进程称之为子进程,调用fork函数的进程称为父进程。子进程是父进程的副本,它将获得父进程数据空间、堆栈等资源的副本。子进程持有的是上述存储空间的“副本”,父子进程间不共享这些存储空间。linux将复制父进程的地址空间内容给子进程,因此,子进程有了独立的地址空间。在Linux下,一个进程在内存里有三部分的数据---"代码段"、"堆栈段"和"数据段"。"代码段",存放程序代码的数据,假如机器中有数个进程运行相同的一个程序,那么它们就可以使用相同的代码段。"堆栈段"存放的是子程序的返回地址、子程序的参数以及程序的局部变量等。“数据段”则存放程序的全局变量,常数以及动态数据分配的数据空间(比如用malloc之类的函数取得的空间)。调用fork函数,父子进程共用代码段,但是数据段和堆栈段是独立的。也就是说数据段和堆栈段的数据是相互不影响的

示例代码:

#include <stdio.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

static int s_nTmp1 = 0;
static int s_nTmp2 = 0;

int main(void)
{
	int nTmp = 0;
	pid_t pid = 0;

	printf("Before fork\n");

	pid = fork();

	printf("This is a test\n");
	
	if(pid > 0)
	{
		nTmp   = 1;
		s_nTmp1 = 1;
		s_nTmp2 = 1;
		printf("这是父进程,它的pid=%d,他的子进程的pid=%d\n", getpid(), pid);
		printf("父进程--nTmp = %d\n", nTmp);
		printf("父进程--s_nTmp1 = %d\n", s_nTmp1);
		printf("父进程--s_nTmp2 = %d\n", s_nTmp2);
		while(1);
	}	
	else
	{
		printf("这是子进程,它的pid=%d,它的父进程的pid=%d\n", getpid(), getppid());
		printf("子进程--nTmp = %d\n", nTmp);
		printf("子进程--s_nTmp1 = %d\n", s_nTmp1);
		printf("子进程--s_nTmp2 = %d\n", s_nTmp2);		
		while(1);
	}
	
	return 0;
}
           

执行结果:

Before fork

This is a test

这是父进程,它的pid=2515,他的子进程的pid=2516

父进程--nTmp = 1

父进程--s_nTmp1 = 1

父进程--s_nTmp2 = 1

This is a test

这是子进程,它的pid=2516,它的父进程的pid=2515

子进程--nTmp = 0

子进程--s_nTmp1 = 0

子进程--s_nTmp2 = 0

结果分析:执行结果中可以看到,在fork之前的代码只会被执行一次(Before fork只会被打印一次),而fork之后的代码都会被执行两次(This is a test打印了两次),这是因为父进程和子进程共享代码段,各执行了一次fork之后的代码。在fork之后,父进程和子进程都有自己独立数据段和堆栈段(子进程的数据段和堆栈段都是父进程的拷贝),所以,在任意进程中(无论是父进程还是子进程)对全局变量(数据段)和局部变量(堆栈段)的操作都不会影响到另一个进程中的值。

注意点:

1)执行结果也可能是

Before fork

This is a test

这是子进程,它的pid=2516,它的父进程的pid=2515

子进程--nTmp = 0

子进程--s_nTmp1 = 0

子进程--s_nTmp2 = 0

This is a test

这是父进程,它的pid=2515,他的子进程的pid=2516

父进程--nTmp = 1

父进程--s_nTmp1 = 1

父进程--s_nTmp2 = 1

即先执行父进程还是子进程是未知的,这点千万要注意。(无论哪个先执行,fork之前的代码肯定都在之前)

2)在进程的结束时,也可以不加while(1),此时会退出一个进程,然后又继续执行另一个进程,即

Before fork

This is a test

这是父进程,它的pid=2635,他的子进程的pid=2636

父进程--nTmp = 1

父进程--s_nTmp1 = 1

父进程--s_nTmp2 = 1

[email protected]:~/home/progress$ This is a test

这是子进程,它的pid=2636,它的父进程的pid=1

子进程--nTmp = 0

子进程--s_nTmp1 = 0

子进程--s_nTmp2 = 0

此时父进程先退出,然后子进程继续执行

进程创建之vfork函数

头文件:同fork函数

函数原型:pid_t vfork(void);             pid_t实际就是int,在sys/types.h中被宏定义的

返回值:同fork函数

函数说明:vfork函数和fork函数的作用都是创建一个新的进程,但是他们有以下几点不同

1)vfork函数---父子进程共享代码段和以及数据空间(数据段和堆栈段);fork函数---父子进程共用代码段,但是数据段和堆栈段是独立的。

2)vfork函数---保证子进程先运行,只有子进程调用exit等函数退出进程时,父进程才能运行,否则阻塞;fork函数---不保证父子进程哪个先执行

示例代码:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

static int s_nTmp1 = 0;
static int s_nTmp2 = 0;

int main(void)
{
	int nTmp = 0;
	pid_t pid = 0;

	printf("Before fork\n");

	pid = vfork();

	printf("This is a test\n");
	
	if(pid > 0)
	{
		printf("这是父进程,它的pid=%d,他的子进程的pid=%d\n", getpid(), pid);
		printf("父进程--nTmp = %d\n", nTmp);
		printf("父进程--s_nTmp1 = %d\n", s_nTmp1);
		printf("父进程--s_nTmp2 = %d\n", s_nTmp2);
	}	
	else
	{
		nTmp   = 1;
		s_nTmp1 = 1;
		s_nTmp2 = 1;
		printf("这是子进程,它的pid=%d,它的父进程的pid=%d\n", getpid(), getppid());
		printf("子进程--nTmp = %d\n", nTmp);
		printf("子进程--s_nTmp1 = %d\n", s_nTmp1);
		printf("子进程--s_nTmp2 = %d\n", s_nTmp2);		
		sleep(10);
		exit(0);
	}
	
	return 0;
}
           

执行结果:

Before fork

This is a test

这是子进程,它的pid=2234,它的父进程的pid=2233

子进程--nTmp = 1

子进程--s_nTmp1 = 1

子进程--s_nTmp2 = 1

This is a test

这是父进程,它的pid=2233,他的子进程的pid=2234

父进程--nTmp = 1

父进程--s_nTmp1 = 1

父进程--s_nTmp2 = 1

结果分析:

调用vfork函数肯定是先执行子进程的代码,只有子进程退出后,父进程才得以运行,因此在执行完

printf("子进程--s_nTmp2 = %d\n", s_nTmp2);后,子进程休眠10s,等待10s之后子进程退出,此时才执行父进程代码。
           

执行结果只有上面这一种。在子进程中改变了数据段和堆栈段的数据,都会影响父进程。

注意点:调用vfork函数创建子进程,要防止互锁导致程序无法继续往下执行。比如

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main(void)
{
	int nTmp = 0;
	pid_t pid = 0;

	printf("Before fork\n");

	pid = vfork();

	printf("This is a test\n");
	
	if(pid > 0)
	{
		nTmp = 1;
	}	
	else
	{
		while(1)
		{
			if(1 == nTmp)
			{
				printf("执行子进程\n");
				exit(0);
			}
		}
	}
	
	return 0;
}
           

这段代码在打印完This is a test之后,将阻塞,子进程一直等待nTmp等1时才会退出,而nTmp只在父进程中被赋值为1,父进程只有在子进程结束时才能执行,导致了父子进程之间互锁。

初学者笔记,若有错误,请指出,谢谢!

继续阅读