Linux fork和vfork

fork 系统调用

可以通过fork系统调用创建新的进程。调用进程称为父进程,被创建的进程称为子进程。

fork函数复制当前进程,会在内核进程表中创建一个新的进程表项。新的进程表项有很多属性和原进程相同,如堆指针、栈指针、标志寄存器的值。也有许多属性被赋予新的值,如子进程PPID为原来进程PID。

原型

#include <unistd.h>

pid_t fork(void);

fork的特点是:
1)调用一次,返回2次。
2次返回区别:子进程返回的是0,父进程返回的是子进程的pid(进程id)。
原因:一个进程可以有多个子进程,但一个进程却只有一个父进程,并且没有一个函数使得进程可以获得其所有子进程的pid。

2)子进程和父进程继续执行fork调用之后的指令。

3)子进程是父进程的副本,子进程获得父进程的数据空间、堆、栈 副本。但子进程和父进程不共享这些存储空间。
典型的进程存储空间安排如下:

也就是说,调用fork之后,子进程完全是父进程的副本,进程的存储空间完全一样。不过,由于fork之后经常跟着调用exec,所以很多实现并不执行一个父进程数据段、栈完全一样的副本。

fork,exec与信号处理函数:fork创建子进程后,子进程具有父进程一样存储空间,信号捕捉函数位于text代码段,在子进程中也是有意义的,子进程会继承父进程的信号处理方式。不过,调用exec后,运行新的程序会覆盖从父进程继承而来的text段,那么信号捕捉函数在新程序中无意义,所以exec会将原先已经设置过的信号捕捉函数重新设置为默认值。

fork示例程序:
子进程和父进程同时对变量a自增,然后打印其值

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

int main()
{
    int a = 1;
    pid_t pid;

    if ((pid = fork()) == -1) { /* error */
        perror("fork error");
        exit(1);
    }
    else if (pid == 0) { /* child */
        ++a;
        printf("child process a = %d\n", a);
    }
    else {
        sleep(1);
        ++a;
        printf("parent process a = %d\n", a);
        wait(pid);
    }

    return 0;
}

运行结果:
可以看到父子进程变量a是相互独立的

child process a = 2
parent process a = 2

vfork 系统调用

vfork也用来创建新进程。
原型

#include <sys/types.h>
#include <unistd.h>

pid_t vfork(void);

跟fork区别:
1.调用序列同fork,但语义不同:vfork用于创建一个新进程,但并不将父进程的地址空间完全复制到子进程中,因为子进程会立即调用exec(或exit)。子进程调用exec或exit之前,在父进程的进程空间中运行,也就是说,vfork子进程和父进程共享进程空间。
如果没有调用exec或exit,直接return返回,可能会带来未知的结果。

2.vfork保证子进程先运行,调用exec或exit之后,父进程才可能被调度运行。期间,父进程阻塞。fork不能保证父、子进程谁先执行,fork返回后,父子进程都开始并发执行,不存在由于fork导致的阻塞。

PS:vfork子进程不能return,只能通过exec,再return,或者直接exit。

vfork示例程序:
子进程和父进程同时对变量a自增,然后打印其值

int main()
{
    int a = 1;
    pid_t pid;

    if ((pid = vfork()) == -1) { /* error */
        perror("fork error");
        exit(1);
    }
    else if (pid == 0) { /* child */
        ++a;
        printf("child process a = %d\n", a);
        exit(1);
    }
    else {
        //sleep(1);
        ++a;
        printf("parent process a = %d\n", a);
        wait(pid);
    }

    return 0;
}

运行结果:
可以看到父子进程共享了变量a的值

child process a = 2
parent process a = 3
posted @ 2021-07-21 23:40  明明1109  阅读(161)  评论(0编辑  收藏  举报