Linux:fork()函数

fork()系统调用会通过复制一个现有进程来创建一个全新的进程。进程被存放在一个叫做任务队列的双向循环链表中,链表中的每一项都是进程PCB。

fork()运行时做的事情

#include<stdlib.h>
#include<unistd.h>
#include<stdio.h>

int main()
{
    printf("leetcode
");
    pid_t pid = fork();
    if(pid == -1)
        printf("error in fork!");
    printf("pid = %d, returnVal = %d
", getpid(), pid);
    return 0;
}
/*
运行结果:
leetcode
pid = 22754, returnVal = 22755
pid = 22755, returnVal = 0
注意:
*/

当进程调用fork()后,当控制转移到内核中的fork()代码后,内核会做:

1.分配新的内存块和内核数据结构给子进程

2.将父进程部分数据结构内容(数据空间,堆栈等)拷贝至子进程

3.添加子进程到系统进程列表中

4.fork()返回,开始调度器调度

为什么fork()成功调用后返回两个值?

由于在复制时复制了父进程的堆栈段,所以两个进程都停留在fork函数中,等待返回。所以fork函数会返回两次,一次是在父进程中返回,另一次是在子进程中返回,这两次的返回值不同,其中父进程返回子进程pid,而子进程返回0。

从fork函数开始以后的代码父子共享,既父进程要执行这段代码,子进程也要执行这段代码.

父子进程文件共享问题

子进程是父进程的副本。例如,子进程获得父进程数据空间、堆和栈的副本。注意,这是子进程所拥有的副本。父进程和子进程并不共享这些存储空间部分。父进程和子进程共享正文段。

fork后子进程和父进程共享的资源还包括:打开的文件

fork()和vfork()的比较

vfork()的诞生是在fork()还没有写时拷贝的时候,因为那个时候创建一个子进程的成本太大了,如果一下子创建好多那么程序的效率一定会下降。

vfork()的实现原理非常简单,就是子进程,父进程完全共用一个资源。即使有人修改了内容,甚至main()函数退出了也不会新开辟一个空间。

fork()和vfork()之间的区别

1.fork父子进程交替运行,vfork保证子进程先运行,父进程阻塞,直到子进程结束(或子进程调用了exec或exit)

2.fork实现了写时拷贝; 而vfork直接让父子进程共用公用资源,避免多开辟空间拷贝

3.vfork必须使用exit或者excl退出

4.就算是fork使用了写时拷贝,也没有vfork性能高

僵尸进程和孤儿进程

在进程结束后,Linux系统会自动回收进程消耗的 内存和IO,但是进程本身占用的资源(task_struct和栈内存)不会被回收,需要被父进程来进行回收

僵尸进程:子进程比父进程先结束!如果父进程没有显式调用wait或waitpid函数的话,直到父进程结束时才会回收子进程的资源!这样的子进程,就是僵尸进程!

孤儿进程:父进程先于子进程结束,子进程于是成为进程1(init进程)的子进程,直到关机才会回收!

原文地址:https://www.cnblogs.com/xiaobaizzz/p/12398484.html