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的技术依然没有改进。