记得以前初次接触fork()函数的时候,一直被“printf”输出多少次的问题弄得比较晕乎。不过,“黄天不负留心人"。哈~ 终于在学习进程和进程创建fork相关知识后,总算是大致摸清了其中的来龙去脉。废话不多讲,下面来谈谈本人的一点小小积累
一个现有的进程可以调用fork函数创建一个新进程。原型如下:
#include<unistd.h>
pid_t fork(void);
返回值:自进程中返回0,父进程返回进程id,出错返回-1
fork()系统调用会通过复制一个现有进程来创建一个全新的进程. 进程被存放在一个叫做任务队列的双向循环链表当中.链表当中的每一项都是类型为task_struct成为进程描述符的结构.也就是我们写过的进程PCB.
小知识:内核通过一个位置的进程标识值或PID来标识每一个进程.同时其最大值默认为32768,short int短整型的最大值. 他就是系统中允许同时存在的进程最大的数目.可以去linux下的proc目录中寻找一个 pid_max的文件,并打开它加以验证. 如
fork()运行时做的事情
--------------------------------------------------------------------------------
首先我们来看一段代码,不过这里会有一点奇怪的现象:
/*************************************************************************
> File Name: 1.c
> Author: tp
> Mail:
> Created Time: Mon 07 May 2018 07:57:28 PM CST
************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main( void)
{
printf("change world!\n");
pid_t pid = fork();
if( pid == -1) {perror("fork"),exit(1); }
printf( "pid=%d, returnVal=%d\n", getpid(), pid);
sleep( 1);
exit(0);
}
~
这段代码的运行结果,大家如果像我当时不了解fork的时候,一定会以为输出结果是两个"change world!",然后2个printf里面的内容. 因为
我们复制出来了两个一模一样的进程,那么他们就应该做同样的事情. But!!! 我们看运行结果:
结果并非我们想的那样,这个时候我们就需要知道fork出子进程之后,程序的运行细节。可以来一张图帮助我们理解:
一般来说,在fork之后是父进程先执行还是子进程先执行是不确定的.这取决于内核所使用的调度算法.如果要求父,子进程之间相互同步.则要求某种形式的进程间通信. 好了我们继续,当进程调用fork后,当控制转移到内核中的fork代码后,内核会做4件事情:
1.分配新的内存块和内核数据结构给子进程
2.将父进程部分数据结构内容(数据空间,堆栈等)拷贝至子进程
3.添加子进程到系统进程列表当中
4.fork返回,开始调度器调度
为什么fork成功调用后返回两个值?
由于在复制时复制了父进程的堆栈段,所以两个进程都停留在fork函数中,等待返回。所以fork函数会返回两次,一次是在父进程中返回,另一次是在子进程中返回,这两次的返回值不同,
其中父进程返回子进程pid,这是由于一个进程可以有多个子进程,但是却没有一个函数可以让一个进程来获得这些子进程id,那谈何给别人你创建出来的进程。而子进程返回0,这是由于子进程可以调用getppid获得其父进程进程ID,但这个父进程ID却不可能为0,因为进程ID0总是有内核交换进程所用,故返回0就可代表正常返回了。
从fork函数开始以后的代码父子共享,既父进程要执行这段代码,子进程也要执行这段代码.(子进程获得父进程数据空间,堆和栈的副本. 但是父子进程并不共享这些存储空间部分. (即父,子进程共享代码段.)。现在很多实现并不执行一个父进程数据段,堆和栈的完全复制. 而是采用写时拷贝技术(不懂可以戳进去看一看).这些区域有父子进程共享,而且内核地他们的访问权限改为只读的.如果父子进程中任一个试图修改这些区域,则内核值为修改区域的那块内存制作一个副本, 也就是如果你不修改我们一起用,你修改了之后对于修改的那部分内容我们分开各用个的.
父子进程文件共享问题
--------------------------------------------------------------------------------
来看个例子
/*************************************************************************
> File Name: 2.c
> Author: tp
> Mail:
> Created Time: Mon 07 May 2018 12:40:39 PM CST
************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int set = 110;
int main( void)
{
printf( "before fork\n");
pid_t pid = fork( );
if( pid < 0){ perror(" fork"),exit( 1);}