生活工程体验信仰哲学精神
投稿投诉
精神世界
探索历史
哲学文学
艺术价值
信仰创造
境界审美
体验技术
技能工具
工程信息
医学生产
生活运用
操作能力

fork(Linux中fork函数详解)

5月5日 溷元楼投稿
  fork,vfork,clone
  Unix标准的复制进程的系统调用时fork(即分叉),但是Linux,BSD等操作系统并不止实现这一个,确切的说linux实现了三个,fork,vfork,clone(确切说vfork创造出来的是轻量级进程,也叫线程,是共享资源的进程)
  forkincludeincludeincludeincludeintmain(void){intcount1;childfork();if(child0){perror(forkerror:);}elseif(child0)forkreturn0inthechildprocessbecausechildcangethidPIDbygetpid(){printf(Thisisson,hiscountis:d(p)。andhispidis:dn,count,count,getpid());}elsethePIDofthechildprocessisreturnedintheparent’sthreadofexecution{printf(Thisisfather,hiscountis:d(p),hispidis:dn,count,count,getpid());}returnEXITSUCCESS;}
  从运行结果里面可以看出父子两个进程的pid不同,堆栈和数据资源都是完全的复制
  子进程改变了count的值,而父进程中的count没有被改变。
  子进程与父进程count的地址(虚拟地址)是相同的(注意他们在内核中被映射的物理地址不同)
  写时复制
  有人认为这样大批量的复制会导致执行效率过低。其实在复制过程中,linux采用了写时复制的策略。
  子进程复制了父进程的taskstruct,系统堆栈空间和页面表,这意味着上面的程序,我们没有执行count前,其实子进程和父进程的count指向的是同一块内存。而当子进程改变了变量时候(即对变量进行了写操作),会通过copyonwrite的手段为所涉及的页面建立一个新的副本。
  所以当我们执行count后,这时候子进程才新建了一个页面复制原来页面的内容,基本资源的复制是必须的,而且是高效的。整体看上去就像是父进程的独立存储空间也复制了一遍。
  写入时复制(Copyonwrite)是一个被使用在程式设计领域的最佳化策略。其基础的观念是,如果有多个呼叫者(callers)同时要求相同资源,他们会共同取得相同的指标指向相同的资源,直到某个呼叫者(caller)尝试修改资源时,系统才会真正复制一个副本(privatecopy)给该呼叫者,以避免被修改的资源被直接察觉到,这过程对其他的呼叫只都是通透的(transparently)。此作法主要的优点是如果呼叫者并没有修改该资源,就不会有副本(privatecopy)被建立。
  第一代Unix系统实现了一种傻瓜式的进程创建:当发出fork()系统调用时,内核原样复制父进程的整个地址空间并把复制的那一份分配给子进程。这种行为是非常耗时的,因为它需要:
  为子进程的页表分配页帧为子进程的页分配页帧初始化子进程的页表把父进程的页复制到子进程相应的页中
  这种创建地址空间的方法涉及许多内存访问,消耗许多CPU周期,并且完全破坏了高速缓存中的内容。在大多数情况下,这样做常常是毫无意义的,因为许多子进程通过装入一个新的程序开始它们的执行,这样就完全丢弃了所继承的地址空间。
  现在的Linux内核采用一种更为有效的方法,称之为写时复制(CopyOnWrite,COW)。这种思想相当简单:父进程和子进程共享页帧而不是复制页帧。然而,只要页帧被共享,它们就不能被修改,即页帧被保护。无论父进程还是子进程何时试图写一个共享的页帧,就产生一个异常,这时内核就把这个页复制到一个新的页帧中并标记为可写。原来的页帧仍然是写保护的:当其他进程试图写入时,内核检查写进程是否是这个页帧的唯一属主,如果是,就把这个页帧标记为对这个进程是可写的。
  当进程A使用系统调用fork创建一个子进程B时,由于子进程B实际上是父进程A的一个拷贝,
  因此会拥有与父进程相同的物理页面。为了节约内存和加快创建速度的目标,fork()函数会让子进程B以只读方式共享父进程A的物理页面。同时将父进程A对这些物理页面的访问权限也设成只读。
  这样,当父进程A或子进程B任何一方对这些已共享的物理页面执行写操作时,都会产生页面出错异常(pagefaultint14)中断,此时CPU会执行系统提供的异常处理函数dowppage()来解决这个异常。
  dowppage()会对这块导致写入异常中断的物理页面进行取消共享操作,为写进程复制一新的物理页面,使父进程A和子进程B各自拥有一块内容相同的物理页面。最后,从异常处理函数中返回时,CPU就会重新执行刚才导致异常的写入操作指令,使进程继续执行下去。
  vfork
  如果fork简单的vfork()的做法更加火爆,内核连子进程的虚拟地址空间结构也不创建了,直接共享了父进程的虚拟空间,当然了,这种做法就顺水推舟的共享了父进程的物理空间
  includeincludeincludeincludeintmain(void){intcount1;childvfork();printf(Beforecreateson,thefatherscountis:dn,count);if((childvfork())0){perror(forkerror:);}elseif(child0)forkreturn0inthechildprocessbecausechildcangethidPIDbygetpid(){printf(Thisisson,hiscountis:d(p)。andhispidis:dn,count,count,getpid());exit(0);}elsethePIDofthechildprocessisreturnedintheparent’sthreadofexecution{printf(Afterson,Thisisfather,hiscountis:d(p),hispidis:dn,count,count,getpid());exit(0);}returnEXITSUCCESS;}
  从运行结果可以看到vfork创建出的子进程(线程)共享了父进程的count变量,2者的count指向了同一个内存,所以子进程修改了count变量,父进程的count变量同样受到了影响。
  由vfork创造出来的子进程还会导致父进程挂起,除非子进程exit或者execve才会唤起父进程
  由vfok创建出来的子进程共享了父进程的所有内存,包括栈地址,直至子进程使用execve启动新的应用程序为止
  由vfork创建出来得子进程不应该使用return返回调用者,或者使用exit()退出,但是它可以使用exit()函数来退出
  如果我们使用return来退出,你会发现程序陷入一种逻辑混乱的重复vfork状态
  参见下面的代码
  includeincludeincludeincludeintmain(void){intcount1;childvfork();printf(Beforecreateson,thefatherscountis:dn,count);if((childvfork())0){perror(forkerror:);}elseif(child0)forkreturn0inthechildprocessbecausechildcangethidPIDbygetpid(){printf(Thisisson,hiscountis:d(p)。andhispidis:dn,count,count,getpid());}elsethePIDofthechildprocessisreturnedintheparent’sthreadofexecution{printf(Afterson,Thisisfather,hiscountis:d(p),hispidis:dn,count,count,getpid());sleep(2);}returnEXITSUCCESS;}
  我们会发现vfork的子进程在使用return后,返回到了调用处,因此父进程又创建出一个新的vfork进程,
  解决这种问题的方法就是不要在进程中使用return,而是使用exit或者exit来代替
  fork与vfork
  区别与联系
  vfork()用法与fork()相似。但是也有区别,具体区别归结为以下3点
  fork()子进程拷贝父进程的数据段,代码段。
  vfork()子进程与父进程共享数据段。
  fork()父子进程的执行次序不确定。
  vfork():保证子进程先运行,
  vfork()保证子进程先运行,在她调用exec或exit之后父进程才可能被调度运行。如果在
  调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁。
  在调用exec或exit之前与父进程数据是共享的,在它调用exec或exit之后父进程才可能被调度运行。如果在调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁。当需要改变共享数据段中变量的值,则拷贝父进程
  vfork用于创建一个新进程,而该新进程的目的是exec一个新进程,vfork和fork一样都创建一个子进程,但是它并不将父进程的地址空间完全复制到子进程中,不会复制页表。因为子进程会立即调用exec,于是也就不会存放该地址空间。不过在子进程中调用exec或exit之前,他在父进程的空间中运行。
  如果在调用vfork时子进程依赖于父进程的进一步动作,则会导致死锁。由此可见,这个系统调用是用来启动一个新的应用程序。其次,子进程在vfork()返回后直接运行在父进程的栈空间,并使用父进程的内存和数据。这意味着子进程可能破坏父进程的数据结构或栈,造成失败。
  为了避免这些问题,需要确保一旦调用vfork(),子进程就不从当前的栈框架中返回,并且如果子进程改变了父进程的数据结构就不能调用exit函数。
  子进程还必须避免改变全局数据结构或全局变量中的任何信息,因为这些改变都有可能使父进程不能继续。通常,如果应用程序不是在fork()之后立即调用exec(),就有必要在fork()被替换成vfork()之前做仔细的检查。
  为什么会有vfork
  因为以前的fork当它创建一个子进程时,将会创建一个新的地址空间,并且拷贝父进程的资源,而往往在子进程中会执行exec调用,这样,前面的拷贝工作就是白费力气了,这种情况下,聪明的人就想出了vfork,它产生的子进程刚开始暂时与父进程共享地址空间(其实就是线程的概念了),因为这时候子进程在父进程的地址空间中运行,所以子进程不能进行写操作,
  并且在儿子霸占着老子的房子时候,要委屈老子一下了,让他在外面歇着(阻塞),一旦儿子执行了exec或者exit后,相当于儿子买了自己的房子了,这时候就相当于分家了。此时vfork保证子进程先运行,在她调用exec或exit之后父进程才可能被调度运行。
  因此vfork设计用以子进程创建后立即执行execve系统调用加载新程序的情形。在子进程退出或开始新程序之前,内核保证了父进程处于阻塞状态
  用vfork函数创建子进程后,子进程往往要调用一种exec函数以执行另一个程序,当进程调用一种exec函数时,该进程完全由新程序代换,而新程序则从其main函数开始执行,因为调用exec并不创建新进程,所以前后的进程id并未改变,exec只是用另一个新程序替换了当前进程的正文,数据,堆和栈段。
  clone
  clone函数功能强大,带了众多参数,因此由他创建的进程要比前面2种方法要复杂。
  clone可以让你有选择性的继承父进程的资源,你可以选择想vfork一样和父进程共享一个虚存空间,从而使创造的是线程,你也可以不和父进程共享,你甚至可以选择创造出来的进程和父进程不再是父子关系,而是兄弟关系。
  先有必要说下这个函数的结构
  cintclone(int(fn)(void),voidchildstack,intflags,voidarg);
  这里fn是函数指针,我们知道进程的4要素,这个就是指向程序的指针,就是所谓的剧本,childstack明显是为子进程分配系统堆栈空间(在linux下系统堆栈空间是2页面,就是8K的内存,其中在这块内存中,低地址上放入了值,这个值就是进程控制块taskstruct的值),flags就是标志用来描述你需要从父进程继承那些资源,arg就是传给子进程的参数)。下面是flags可以取的值
  下面的例子是创建一个线程(子进程共享了父进程虚存空间,没有自己独立的虚存空间不能称其为进程)。父进程被挂起当子线程释放虚存资源后再继续执行。
  includeincludeincludeincludeincludeincludedefineFIBERSTACK8192intdosomething(){printf(Thisisson,thepidis:d,theais:dn,getpid(),a);free(stack);这里我也不清楚,如果这里不释放,不知道子线程死亡后,该内存是否会释放,知情者可以告诉下,谢谢exit(1);}intmain(){a1;stackmalloc(FIBERSTACK);为子进程申请系统堆栈if(!stack){printf(Thestackfailedn);exit(0);}printf(creatingsonthread!!!n);clone(dosomething,(char)stackFIBERSTACK,CLONEVMCLONEVFORK,0);创建子线程printf(Thisisfather,mypidis:d,theais:dn,getpid(),a);exit(1);}clone,fork,vfork区别与联系
  实现方式思路
  系统调用服务例程sysclone,sysfork,sysvfork三者最终都是调用dofork函数完成。
  dofork的参数与clone系统调用的参数类似,不过多了一个regs(内核栈保存的用户模式寄存器)。实际上其他的参数也都是用regs取的
  具体实现的参数不同
  1、clone:
  clone的API外衣,把fn,arg压入用户栈中,然后引发系统调用。返回用户模式后下一条指令就是fn。
  sysclone:parenttidptr,childtidptr都传到了dofork的参数中
  sysclone:检查是否有新的栈,如果没有就用父进程的栈(开始地址就是regs。esp)
  2、fork,vfork:
  服务例程就是直接调用dofork,不过参数稍加修改
  cloneflags:
  sysfork:SIGCHLD,0,0,NULL,NULL,0
  sysvfork:CLONEVFORKCLONEVMSIGCHLD,0,0,NULL,NULL,0
  用户栈:都是父进程的栈。
  parenttidptr,childctidptr都是NULL。
  需要CCLinux服务器架构师学习资料私信资料(资料包括CC,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCPIP,协程,DPDK,ffmpeg等),免费分享
投诉 评论 转载

fork(Linux中fork函数详解)热文聚热点网 fork,vfork,cloneUnix标准的复制进程的系统调用时fork(即分叉),但是Linux,BSD等操作系统并不止实现这一个,确切的说linux实现了三个,fo……南京中医药大学(2022“中医药大学”排行榜)热博聚热点网 哪些大学是2022年中国中医药类实力最强、水平最高、最具竞争力的高校?为了给2022年全国考生及家长选择报考中国中医药类高等学校提供参考指南,2022年4月,全国第三方大……【歌词】SUMMERANTHEM歌手:BaseBallBea BaseBallBearSUMMERANTHEM作詞:小出祐介作曲:小出祐介編曲:小出祐介玉井健二制作夏来太陽季節夏来永遠季節君僕笑……【歌词】荒废的乐园歌手:许志安热文聚热点网 曲:许志安词:黎芷珊编:张兆鸿从前是雨天晴天也一样没有情趣天天探访白天夜深也一样没有宏志心中也没方向没方向但是某天遇着你后改变极……【歌词】唐会(DJ版)歌手:DJ舞曲热评聚热点网 歌曲名称:唐会(DJ版)歌手:网络歌手专辑:时间补偿值:0唐会(DJ版)网络歌手RelaxtakeiteasyForthereisnothi……【歌词】续写FEEL。夏一相信夏一会让你瞬间爱上此歌。动感女 音乐无处不在续写、FeeL里需要你的聆听加入我们音乐随你动无限UP!“LoveMusicLoveBlack。”(爱音乐爱黑色)生活就象一场录影,把……【歌词】NothingNew歌手:Labyrinth〔迷宫乐 PeteYornLongTimeNothingNew活在當下制作sameoldtownloosesensesurroundsmealwaysdragsy……【歌词】一言难尽张宇歌手:华纳群星热博聚热点网 你给我一场戏你看着我入迷被你从心里剥落的感情痛得不知怎么舍去不要这场记忆不要问我结局心底的酸楚和脸上的笑容早就合而为一迟迟不能相……【歌词】Vanishingblue歌手:TangerineD TakeItEasy演唱:BrightEyes专辑:DigitalAshinaDigitalUrnFirstwithyourhandsthenwithy……【歌词】陪你看花歌手:伍华热博聚热点网 陪你看花伍华,孙莞词:孙莞曲:伍华混音:殇小谨篱笆院老树旁冒出的新芽那年我们种下的樱花你说要等它洁白无瑕花瓣洒满新月牙滴答滴答滴……你是如何看待李嘉诚这个人的?热评聚热点网 不是经商的,本不关心李嘉诚,近期不时有李嘉诚信息,看着看着,才知道香港的大商人们也是各不相同的。而有儒相的李嘉诚却让我知道了是亇真商精。其言商够精炼。如回报、投资、风险、清仓说……【歌词】weatherman歌手:Plus44热议聚热点网 ViennaTengStrayItalianGreyhoundLRCbylzh,fromjiangxipingxiangohnonotnowpleasenot……
林志颖父子死里逃生为何电动汽车碰撞后易着火?热评聚热点网 一瓶维生素2元B2。是这三种疾病的克星!高血压也不例外热传聚 小学生节约用水的方法热评聚热点网 金桔梨子水禁忌两者可以一起食用吗热议聚热点网 陈乔恩再次代言《笑傲江湖》,9年后又演东方不败,居然一点都没 带哥们去了广州这家高端桑拿洗浴男士会所,你可以和我一样值得拥 关于古尔邦节家庭的风俗作文(作文家庭的风俗450字古尔邦节) 第一次点亮小灯泡小学作文热文聚热点网 经济学啊经济学,你是妓女还是修女热传聚热点网 情人关系靠什么维持(情人关系如何维系长久)热闻聚热点网 《舒克贝塔》会有第二季吗?热闻聚热点网 北京冬奥场馆相继对外开放热评聚热点网
中国历史上四大千古奇谜是哪几个莳萝的介绍:香气特殊,促进消化为什么说2020年的新年比往年早十多天呢?医用吸痰器的使用注意事项是什么祠堂的对联大全人教版高中第五册二单元作文勇辄行者无疆那年桃花作文500字麦富迪带着敬畏心做好品控主持人歌手梦幻联动善待民间舆论激发幼儿学习兴趣促进幼儿全面发展论文邓伦事件对他以后的发展有影响吗?啤酒与什么食物相克

友情链接:中准网聚热点快百科快传网快生活快软网快好知文好找新乡渭南铜川松江山南雅安松原荃湾淮北昭通平凉鞍山赤峰苗栗保亭池州渝北株洲陇南濮阳三沙秀山密云鸡西