Linux(1):fork函数

         ps:每一篇博客不过为了记录学习的过程,并反思总结,若有错误,还望指正算法


       函数原型:extern __pid_t fork (void) __THROWNL;编程

       该函数包括于头文件unistd.h中。安全

       源文件里凝视:数据结构

              /* Clone the calling process, creating an exact copy.Return -1 for errors, 0 to the new process,函数

   and the process ID of the new process to the old process.  */post

            

           fork()会产生一个子进程。其子进程会复制父进程的数据与堆栈空间。并继承父进程的用户代码、组代码、环境变量、已打开的文件代码、工做文件夹和资源限制等。学习

Linux 使用copy-on-write(COW)技术,仅仅有当当中一进程试图改动欲复制的空间时才会作真正的复制动做,由于这些继承的信息是复制而来,并非指一样的内存空间,所以子进程对这些变量的改动和父进程并不会同步。优化

此外。子进程不会继承父进程的文件锁定和未处理的信号。(COW并不那么完美 COW是一种很是重要的优化手段,核心是懒惰处理实体资源请求。在多个实体资源之间仅仅是共享资源,起初是并不真正实现资源拷贝,仅仅有当实体有需要对资源进行改动时才真正为实体分配私有资源。但COW技术亦有它的优缺点。spa

操作系统

      1.COW技术科下降分配和复制大量资源时带来的瞬间延时,但其实是将这样的延时附加到了兴许的操做之中。 

      2.COW技术科下降没必要要的资源分配。比方fork进程时。并不是所有的页面都需要复制,父进程的代码段和仅仅读数据段都不被赞成改动,因此无需复制。

 

       注意,Linux不保证子进程会比父进程先运行或晚运行。所以编敲代码时要留意死锁或竞争条件的发生。


       返回值:假设fork()调用成功则在父进程会返回新创建的子进程代码(PID)。而在新创建的子进程中则返回0。

假设fork() 失败则直接返回-1,失败缘由存于errno中。失败的缘由有三个:

      1) 系统内存不够;

      2) 进程表满(容量通常为200~400)。

      3) 用户的子进程太多(通常不超过25个)。

     错误代码:EAGAIN 内存不足;ENOMEM 内存不足,没法配置核心所需的数据结构空间。

        fork建立的新进程被称为子进程(child process

该函数被调用一次,但返回两次。两次返回的差异是子进程的返回值是0,而父进程的返回值则是新进程(子进程)的进程 id

将子进程id返回给父进程的理由是:因为一个进程的子进程可以多于一个,没有一个函数使一个进程可以得到其所有子进程的进程id。对子进程来讲,之因此fork返回0给它,是因为它随时可以调用getpid()来获取本身的pid。也可以调用getppid()来获取父进程的id(进程id 0老是由交换进程使用。因此一个子进程的进程id不可能为0 )

    举个样例:

   

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
	int pid;
	pid=fork();
	printf("Hello\n");
	if(pid==0)
		printf("I'm child process.\n");
	else
		printf("I'm parent process.\n");
	return 0;
}

    运行结果为:

   

    fork以后,操做系统会复制一个与父进程全然一样的子进程。虽然说是父子关系。但是在操做系统看来,他们更像兄弟关系。这2个进程共享代码空间。因为子进程是父进程的副本。因此它拥有父进程数据空间、栈和堆的副本。它们并无共享这些存储空间。它们仅仅共享正文段。 但是数据空间是互相独立的,子进程数据空间中的内容是父进程的完整拷贝,指令指针也全然一样,子进程拥有父进程当前执行到的位置(两进程的程序计数器pc值一样,也就是说。子进程是从fork返回处開始执行的),但有一点不一样,假设fork成功。子进程中fork的返回值是0。父进程中fork的返回值是子进程的进程号,假设fork不成功。父进程会返回错误。

   

#include<unistd.h>
#include<stdio.h>
int a=5;
int main()
{
   int b=6,pid;
   pid =fork();
   if(pid<0)
   {
      printf("Error\n");
   }
   else if(pid==0)
   {
      a++;
      b++;
      printf("Child process:%d\n",getpid());
      printf("%d %d %d\n",getpid(),a,b);
   }
   else
   {
      printf("Parent process:%d\n",getpid());
      printf("%d %d %d\n",getpid(),a,b);
   }
}

    运行结果:

   

    由此看出,子进程的值发生了改变。可以说明。它们并不是共享的。

    再来看下变量的地址:

   

#include<unistd.h>
#include<stdio.h>
int a=5;
int main()
{
   int b=6,pid;
   pid =fork();
   if(pid<0)
   {
      printf("Error\n");
   }
   else if(pid==0)
   {
      a++;
      b++;
      printf("Child process:%d\n",getpid());
      printf("%d %d %d %p %p\n",getpid(),a,b,&a,&b);
   }
   else
   {
      printf("Parent process:%d\n",getpid());
      printf("%d %d %d %p %p\n",getpid(),a,b,&a,&b);
   }
}

    运行结果:

   

    由结果可以看出,变量地址是同样的!

!内容却不同,因为打印的变量的地址都是逻辑空间, 对于父子进程。它们的逻辑空间同样,都是虚拟地址,但是物理空间仍是不一样的。每个进程都有独立的4G的虚拟地址空间,映射到不一样的物理空间。比方我现在同一时候执行两个进程,他们都有可能訪问各自的虚拟地址,但是映射以后的物理内存就不会是同一个地址。

因此在多进程编程中。不要寄但愿于经过地址来推断两个变量是否一样。

因此,两个进程一直同一时候执行,而且步调一致,在fork以后,他们分别做不一样的工做。也就是分岔了。这也是fork为何叫fork的缘由至于那一个最早执行。可能与操做系统(调度算法)有关,而且这个问题在实际应用中并不重要。假设需要父子进程协同,可以经过原语的办法解决。

     最后看下父子进程的关系。看到网上有人说:

     他们的关系是管理和被管理的关系,当父进程终止时。子进程也随之而终止。但子进程终止。父进程并不必定终止。比方httpdserver执行时,咱们可以杀掉其 子进程,父进程并不会因为子进程的终止而终止。在Linux进程管理中,当咱们发现占用资源过多,或没法控制的进程时。应该杀死它,以保护系统的稳定安全执行。

    咱们可以经过程序来验证一下是否正确看看:

   

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
   int pid,v=1;
   pid=fork();
   if(pid==0)
   {
      printf("Child Process:pid=%d,ppid=%d,v=%d\n",getpid(),getppid(),v++);
      sleep(1);
      printf("Child Process:pid=%d,ppid=%d,v=%d\n",getpid(),getppid(),v++);
      exit(0);
   }
   else
   {
      printf("Parent Process:pid=%d,ppid=%d,v=%d\n",getpid(),getppid(),v++);  
}

     运行结果:

              

     可以看出父进程首先退出,退出前childPPID6707。退出后子进程的PPID变为了1613.经过ps 命令查看到1613为init进程,说明父进程退出后的子进程由 init进程领养。而该子进程是不会退出的

相关文章
相关标签/搜索