C语言中的fork

fork是什么

C语言中的函数,fork在英文中是分叉的意识,在C语言中,执行过fork的进程会分叉出一个新的进程。

新进程被成为子进程,原来的进程被称为父进程。

fork的功能

这里举两个例子说明一下fork的功能

//fork.c

    

#include <unistd.h>

#include <stdio.h>


int main(void)

{

   int i = 0;

   fork();

   i++;

   printf("i = %d \n", i);


}

执行:

gcc -o fork fork.c

./fork

i = 1 

i = 1


分析

程序在执行到fork的时候克隆出了子进程,子进程具有当前进程的一切资源。


子进程从fork开始执行,并不执行fork前面的代码。


fork之后的代码有两个进程在执行,因此输出了两次。


而且fork之后父子进程不共享数据,因此两边数据不互相影响,一开始i=0,i++之后i都等于1。


同时由于父子进程不共享数据,因此父子进程要通信只能通过进程间通信。


例子2


#include <unistd.h>

#include <stdio.h>

int main(void)

{

    int i = 0;

    printf("i son/pa ppid  pid   fpid\n");

    //ppid指当前进程的父进程pid

    //pid指当前进程的pid,

    //fpid指fork返回给当前进程的值

    for (i = 0; i < 2; i++)

    {

        pid_t fpid = fork();

        if (fpid == 0)

            printf("%d child  %4d %4d %4d\n", i, getppid(), getpid(), fpid);

        else

            printf("%d parent %4d %4d %4d\n", i, getppid(), getpid(), fpid);

    }

    return 0;

}


执行

./fork2

i son/pa ppid  pid   fpid

0 parent 5973  31644 31645

0 child  31644 31645    0


1 parent 5973  31644 31646

1 parent 31644 31645 31647

1 child  31644 31646    0

1 child  31645 31647    0



分析

for循环执行了两次,第一次执行fork变成两个进程,第二次执行fork变成四个进程。


在父进程中fork执行的返回值是子进程pid,在子进程中为0。因此子进程执行else段代码,父进程执行if段代码。


第一次循环:


在这里插入图片描述


第二次循环



在第二次循环中,31645变成了父进程,它也创建了对应的子进程。


因为第一次循环有两个进程,第二次循环有四个进程,每个进程执行一次输出。所以最后输出了2+4=6行。


总结

fork的返回值有三种


0:子进程返回0

大于0:父进程返回子进程pid

-1:创建失败

fork一次调用会返回两次


程序执行fork后会产生子进程,子进程具有父进程的一切资源,父子进程同时执行。


父子进程不共享数据


fork的使用场景

可以应用在服务器中,服务器在接收到连接请求的时候可以fork出进程来处理新的连接请求,父进程等待下一个连接请求到达。


fork的原理

fork在创建进程的时候,把内核中的task_struct(每个进程在内核中都是task_struct结构)复制了一份,还创建了新的进程地址空间和堆栈。父子进程除了极少数不一样之外,其他都一样。


父子进程使用的内存实际上是同一个物理内存,实际上是共享的。


但是共享的内存被内核设置成了只读,因此不会出现问题。


如果有一方尝试写入,就会触发异常机制,内核发现异常之后会分配一个新的空间让父子进程分开使用,这个过程就叫做写时拷贝机制。


一个进程可以有多个线程,但是fork复制时只会复制当前的一个线程。


这是历史遗留问题,早期进程都是单线程的,这样fork没有问题,但是到了多线程时代,fork的技术依然没有改进。