浅谈fork

如有问题,还请多多指教!

  • fork是什么?(PCB 进程唯一描述符)

浅谈fork()

     一个进程,包括代码、数据和分配给进程的资源。fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。
    一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。相当于克隆了一个自己。

 

  • fork()函数

头文件  #include <unistd.h>

函数原型 pid_t fork(void);

pid_t 是fork的函数值,类似于int 类型,返回值有三种

  1. >0 pid_t 就是父进程执行fork后,创建的子进程的id
  2. =0 证明是子进程
  3. <0 创建子进程失败。

函数的使用:

浅谈fork()浅谈fork()

fork()下的程序是并发运行的。

getpid()得到当前程序的pid  getppid() 得到父进程的pid。

 

  • fork() 在实际中的使用

  • 如上图所示:

浅谈fork()

 父子进程中的n是同一块地址吗?

首先,我们肯定知道的是 n肯定不是同一块地址,这里我们看到的都是逻辑地址,即虚拟地址,因为两个的程序的逻辑是一样的,所以他们的逻辑地址是一样的。每一个进程都有自己的内存,所以他们对应的实际地址不是同一个哦。

 

  • 数一数有几个A?

      浅谈fork()            浅谈fork()          浅谈fork()

                           6                                             8                                               3

这里我们需要知道的是,printf() 这个函数,并不是我们一调用就会里面显示出来,printf()函数有一个缓冲区,我们先将数据写到缓冲区中,然后打印缓冲区的条件,来进行是否打印。

打印缓冲区的条件:1. 缓冲区放满 2. 被强制刷新   3. 程序结束时

  • Fork的写时复制

fork采用的是写时拷贝技术,这一块为什么用写时拷贝技术呢,我就简单总结一下,我们复制两个一样的程序如果只是为了看,那我们就不复制了,谁想看 谁就看,如果我们想要修改一个的时候,我们在将其复制,这样可以节省时间和空间。

fork()会产生一个和父进程完全相同的子进程,但子进程在此后多会exec系统调用,出于效率考虑,linux中引入了写时复制技术,也就是只有进程空间的各段的内容要发生变化时,才会将父进程的内容复制一份给子进程fork之后exec之前两个进程用的是相同的物理空间(内存区),子进程的代码段、数据段、堆栈都是指向父进程的物理空间,也就是说,两者的虚拟空间不同,但其对应的物理空间是同一个

当父子进程中有更改相应段的行为发生时,再为子进程相应的段分配物理空间,如果不是因为exec,内核会给子进程的数据段、堆栈段分配相应的物理空间(至此两者有各自的进程空间,互不影响),而代码段继续共享父进程的物理空间(两者的代码完全相同)。而如果是因为exec,由于两者执行的代码不同,子进程的代码段也会分配单独的物理空间。

 

fork时子进程获得父进程数据空间、堆和栈的复制,所以变量的地址(当然是虚拟地址)也是一样的。

每个进程都有自己的虚拟地址空间,不同进程的相同的虚拟地址显然可以对应不同的物理地址。因此地址相同(虚拟地址)而值不同没什么奇怪。


写时复制的具体过程是这样的:

fork子进程完全复制父进程的栈空间,也复制了页表,但没有复制物理页面,所以这时虚拟地址相同,物理地址也相同,但是会把父子共享的页面标记为只读(类似mmapprivate的方式)

如果父子进程一直对这个页面是同一个页面,直到其中任何一个进程要对共享的页面写操作,这时内核会复制一个物理页面给这个进程使用,同时修改页表。而把原来的只读页面标记为可写,留给另外一个进程使用。

这就是所谓的写时复制

 

  • 僵尸进程

子进程先于父进程结束,父进程没有调用 wait 获取子进程的退出码,此时,子进 程变成僵死进程。

解决办法:父进程调用 wait 获取退出码,或 父进程先  结束,让 init 进程接管子进 程。

在Linux系统中,一个进程结束了,但是他的父进程没有等待调用(wait/waitpid)他,那么他将变成一个僵尸进程。通过ps命令查看其带有defunct的标志。僵尸进程是一个早已死亡的进程,但在进程表(processs table)中仍占了一个位置(slot)。
但是如果该进程的父进程已经先结束了,那么该进程就不会变成僵尸进程。因为每个进程结束的时候,系统都会扫描当前系统中所运行的所有进程,看看有没有哪个 进程是刚刚结束的这个进程的子进程,如果是的话,就由Init进程来接管他,成为他的父进程,从而保证每个进程都会有一个父进程。而Init进程会自动 wait其子进程,因此被Init接管的所有进程都不会变成僵尸进程。

 

浅谈fork()

 

  1. 使用wait:

浅谈fork()     浅谈fork()

我们可以看见 wait会阻塞父进程,要等到子进程结束才会执行,如果想一起执行还需要改进的。

 

  1. 使用init进程(pid为1)接管

浅谈fork()      浅谈fork()

 

 

 

 

相关文章:

  • 2021-11-27
  • 2021-08-24
  • 2022-12-23
  • 2021-09-23
  • 2021-11-19
  • 2021-11-29
  • 2022-01-07
猜你喜欢
  • 2021-03-31
  • 2021-06-20
  • 2022-12-23
  • 2021-11-19
  • 2021-11-06
  • 2021-08-20
  • 2021-08-13
相关资源
相似解决方案