进程创建详解与父子进程资源的管理

1. 父子进程执行顺序问题

父进程在使用fork函数创建子进程后父进程与子进程互相不关联,以独立身份抢占CPU 资源,具体谁先执行由调度算法决

定,用户空间没有办法干预。子进程执行代码的位置是fork/vfork 函数返回的位置。

2. 子进程资源申请问题

在使用fork函数创建子进程后,子进程重新申请新的物理内存空间,复制父亲进程地址空间所有的信息,子进程复制父亲进程的代码段,数据段,BSS 段,堆,栈所有用户空间的信息,在内核中操作系统为其重新申请了一个PCB,并且使用父亲进程的PCB 来初始化,除了pid 等特殊信息外,几乎所有的信息都是一样的。

比如下面这个例子:

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

int glob = 100;

int main(){

	int temp = 20;
	
	pid_t pid = fork();
	if(pid == -1) {
		perror("fork");
	} else if(pid == 0) {
		glob = 200;
		temp = 10;
		printf("child:glob:%d,temp:%d\n",glob,temp);
		printf("child:&glob:%p,&temp:%p\n",&glob,&temp);
	} else {
		sleep(1);
		printf("parent:glob:%d,temp:%d\n",glob,temp);
		printf("parent:&glob:%p,&temp:%p\n",&glob,&temp);
		wait(NULL);
	}
	
	return 0;
}

运行结果:

[root@ye linux-code]# ./fork_basic 
child:glob:200,temp:10
child:&glob:0x60104c,&temp:0x7ffe8c8a77f8
parent:glob:100,temp:20
parent:&glob:0x60104c,&temp:0x7ffe8c8a77f8
以上代码看,在子进程中先修改变量的值,并不影响父亲进程,说明数据段和栈(当然也包括其它用户空间内存)是子进程申请的新物理空间。
但是从打印的地址来看,好像是一样的,又是同一段内存?其实不一样,这里打印的是虚拟地址,而不是物理地址编号;两个进程的虚拟地址空间是没有任何联系的。

3. 子进程对文件流缓冲区的处理

在创建子进程是,子进程的用户空间将复制父进程用户空间的所有信息,当然,也包括流缓冲区的内容。

先看一个例子:

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

int glob = 100;

int main()
{
	int temp = 20;
	printf("hello\nworld");
	fork();
	printf("bye\n");
	return 0;
}

运行的结果:

[root@ye linux-code]# ./fork_file 
hello
worldbye
worldbye
文件流的缓冲区会缓存没有刷新的信息,且缓冲区在用户空间中,虽然子进程创建后从fork 返回处执行,但缓冲区被子进程复制了一份,这样存储在缓冲区中的“world”也被复制了一份,因此,输出了两份“world”。

再看一个例子:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
int main()
{
	int temp = 20;
	int i=0;
	for(i = 0 ;i<2;i++)
	{
		fork();
		printf("*");
	}
	sleep(5);
	return 0;
}

运行结果:

[root@ye linux-code]# ./fork_printf 
********
打印为什么是8 个星?
(1)i = 0 时,执行 fork,创建一个进程,父子进程各自打印一个*,父亲进程pid=100,子进程pid=101;
(2)i=2 时,父亲进程再创建一个子进程pid=102,再打印一个*,pid=102 的进程复制了子进程的输出流的缓冲区,拷贝了一个*,自己再打印一个*;
(3)pid=101 的子进程在i=1 后也要执行fork 函数,创建一个新的子进程Pid=102,pid=101 的进程再执行一次打印,pid=102 的子进程复制了pid=101 进程在i=0 时打印的*。

因此,上述共4 个进程,每个进程都打印两个*,因此是8 个,只是有两个进程是复制了一个*而已。

4. 子进程对文件描述符的处理

扫描二维码关注公众号,回复: 2033925 查看本文章

在同一个进程中,两次打开(使用Open)同一个文件(只要没有对文件上锁),分别写入文件会存在覆盖的情况。

而使用 fcntl/dup 复制文件描述,分别使用这两具文件描述符写文件并不会出现覆盖,而是交叉写入。

原因:两次 Open 打开实际上在内核中创建了两个互不相关的文件表项,也就记录了两个读写位置。而复制文件描述符则在内核中使用同一个文件表项(struct flie),因此,共用一个读写位置。

而在子进程创建成功后,父子进程共享一个文件表项,也就共用同一个读写位置。

看下面这个例子:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <fcntl.h>
int main()
{
	int fd = open("test.txt",O_CREAT|O_RDWR,0644);
	if(fd == -1) {
		perror("open");
	}
	write(fd,"hello",5);
	pid_t pid =fork();
	if(pid == 0) {
		write(fd,"world",5);
	} else {
		sleep(1);
		write(fd,"abc",3);
	}
	close(fd);
	
	return 0;
}

运行结果:

[root@ye linux-code]# cat test.txt 
helloworldabc



猜你喜欢

转载自blog.csdn.net/yzy1103203312/article/details/80963782