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. 子进程对文件描述符的处理
在同一个进程中,两次打开(使用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