操作系统 — fork()函数的使用与底层原理

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Dawn_sf/article/details/78709839

fork()函数的使用与底层原理







在我第一次接触到fork函数的时候,那个时候我在牛客网刷题然后呢碰到一个关于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()运行时做的事情



首先我们来看一段代码,不过这里会有一点奇怪的现象:



这段代码的运行结果,大家如果像我当时不了解fork的时候,一定会以为输出结果是两个doing,然后2个printf里面的内容. 因为

我们复制出来了两个一模一样的进程,那么他们就应该做同样的事情. But!!! 我们看运行结果:



结果并非我们想的那样,这个时候我们就需要知道fork出子进程之后,程序的运行细节.这里我画一张图帮助我理解:





一般来说,在fork之后是父进程先执行还是子进程先执行是不确定的.这取决于内核所使用的调度算法.如果要求父,子进程之间相

互同步.则要求某种形式的进程间通信. 好了我们继续,当进程调用fork后,当控制转移到内核中的fork代码后,内核会做4件事情:

1.分配新的内存块和内核数据结构给子进程

2.将父进程部分数据结构内容拷贝至子进程

3.添加子进程到系统进程列表当中

4.fork返回,开始调度器调度

第一个问题,为什么frok成功调用后返回两个值? 

由于在复制时复制了父进程的堆栈段,所以两个进程都停留在fork函数中,等待返回.因为fork函数会返回两次,一次是在父进程中返回,

另一次是在子进程中返回,这两次的返回值不同. 

从fork函数开始以后的代码父子共享,既父进程要执行这段代码,子进程也要执行这段代码.(子进程获得父进程数据空间,堆和栈的副

本. 但是父子进程并不共享这些存储空间部分. 父,子进程共享代码段.)现在很多现实并不执行一个父进程数据段,堆和栈的完全复制. 

而是采用写时拷贝技术(不懂可以戳进去看一看).这些区域有父子进程共享,而且内核地他们的访问权限改为只读的.如果父子进程中任一

个试图修改这些区域,则内核值为修改区域的那块内存制作一个副本, 也就是如果你不修改我们一起用,你修改了之后对于修改的那部分

内容我们分开各用个的.


fork()函数在底层中做了什么?



linux平台通过clone()系统调用实现fork(). fork(),vfork()和clone()库函数都根据各自需要的参数标志去调用clone(),然后由clone()去

调用do_fork(). 再然后do_fork()完成了创建中的大部分工作,他定义在kernel/fork.c当中.该函数调用copy_process(). 然后重点来了,

我们看看这个copy_process函数到底做了那些事情?? 我画一张图帮我们理解:



vfork和fork的之间的比较:



vfork()的诞生是在fork()还没有写时拷贝的时候,因为那个时候创建一个子进程的成本太大了,如果一下子创建好多了那么程序的效率

一定会下降. 然后就有人提出了vfork(). vfork的实现原理非常简单,就是子进程,父进程完全公用一个资源. 就是是有人修改了内容,

甚至main()函数退出了也不会新开辟一个空间. 这里里会有问题的,如果你的一个子进程没有使用exit()退出,那么程序就会出现段错误. 

不相信可以去试一试~ 

为什么会出现段错误? 

在函数栈上面,子进程运行结束了,main的函数栈被子进程释放了,然后父进程在使用的时候,就访问不到了,一旦vfork出子进程,

退出的时候需要使用exit来结束.


vfork和fork之间的区别:

1.fork父子进程交替运行,vfork子进程运行,父进程阻塞,直到子进程结束.

2.fork实现了写时拷贝. vfork直接让父子进程公用资源然后无论如何也不会多开辟空间拷贝了,

3,vfork必须使用exit或者excl退出.

4.就算是fork使用了写时拷贝,也没有vfork性能高.

5.每个系统上的vfork都有问题,推荐不要使用.

展开阅读全文

fork()函数入门级问题:fork()与进程数

09-05

这样一段代码:rn最后还剩2个进程?为什么呢?rn我的意见是 初始父进程 在第一次fork()后终止;第一子进程在第二次fork()后终止,然后就只剩下第二子进程了???rn[code=C/C++]rnif ( (pid=fork())<0 )rn printf("error fork()\n");rn exit(1) ;rn rnrn else if (pid!=0) rn exit(0);rn rnrn// ...............................................................some other ,but have no fork() rnrn if ( (pid=fork()<0) )rn fputs("error fork()\n",stdout);rn exit(1) ;rn rn else if (pid!=0) rn exit(0);rn rn[/code]rnrn这是完整的源码:rn[code=C/C++]rnvoid daemonize(const char *cmd)rnrn int i,fd0,fd1,fd2;rn pid_t pid;rn struct rlimit rl;rn struct sigaction sa;rnrn umask(0);rnrn if ( getrlimit(RLIMIT_NOFILE,&rl)<0 )rn printf("%s can't get file limit.\n",cmd);rn exit(1) ;rn rnrn if ( (pid=fork())<0 )rn printf("error fork()\n");rn exit(1) ;rn rnrn else if (pid!=0) rn exit(0);rn rnrn setsid();//creates a session and sets the process group ID.rnrn sa.sa_handler = SIG_IGN;rn sigemptyset(&sa.sa_mask);rn sa.sa_flags=0;rn if (sigaction(SIGHUP,&sa,NULL)<0)rn printf("error sigaction()");rn exit(1) ;rn rnrn if ( (pid=fork()<0) )rn fputs("error fork()\n",stdout);rn exit(1) ;rn rn else if (pid!=0) rn exit(0);rn rn/*rn if ( chdir("/tmp")<0 )rn FILE *fp=fopen("err","w");rn fprintf(fp,"%d",errno);rn fprintf(fp,"woj");rnrn printf("error chdir()\n");rn exit(1) ;rn rn*/rnrn if (rl.rlim_max == RLIM_INFINITY)rn rl.rlim_max = 1024;rn rn for(i=0;i 论坛

没有更多推荐了,返回首页