设为首页 加入收藏

TOP

浅析fork()和底层实现(一)
2018-07-13 06:07:04 】 浏览:166
Tags:浅析 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()运行时做的事情


--------------------------------------------------------------------------------


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


/*************************************************************************
  > 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);}


    if( pid == 0)
    {
        ++set;
        printf( "son pid=%d, %d\n", getpid(), set);
    }
    else
    {
        sleep( 1);
        printf( "parent pid=%d , %d\n", getpid( ), set);
    }
    exit( 0);
}


首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇SpringBoot中Async异步方法和定时.. 下一篇用信号量为共享内存添加同步机制

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目