UNIX 环境高级编程(八)—— fork 函数

一个现有的进程可以调用 fork 函数创建一个新进程。

#include <uinstd.h>
pid_t fork(void);
            // 返回值:
                子进程返回 0;
                父进程返回子进程 id;
                若出错,返回 -1

由 fork 创建的新进程被称为子进程(child process)。fork 函数被调用一次,但返回两次。两次返回的区别在于,子进程的返回值是0,二父进程的返回值是新创建的进程的 ID。

将子进程的 ID 返回给父进程的理由是:因为一个进程的子进程可以有多个,且没有一个函数使一个进程可以获得其所有子进程的进程 ID。

fork 使子进程返回值 0 的理由是:一个进程只会有一个父进程,所以子进程总是可以调用 getppid 来获取其父进程的进程 ID(进程 ID 0总是由内核交换进程使用,所以一个子进程的进程 ID 不可能为0).

子进程和父进程(均)继续执行 fork 调用之后的指令。子进程是父进程的副本。例如,子进程获得父进程的:

  • 数据空间(数据段)
  • 堆和栈的副本

注意,这些是子进程自己所拥有的副本,不是共享关系。子进程和父进程共享正文段(text)。

由于在 fork 之后经常跟着 exec(表示执行),所以现在的很多实现并不执行一个父进程数据段、栈和堆的完全副本(有点缓执行 lazy evaluation 的意思)。作为替代,使用了写时复制(Copy-On-Write,COW)技术。这些区域由父进程和子进程共享,而且内核将它们的访问权限改变为只读。如果父进程和子进程的任何一个试图这些区域,则内核只为修改区域的那块内存制作一个副本,通常是虚拟存储系统中的一“页”。

一个实例

#include "apue.h"

char buf[] = "a write to stdout!";
int globvar = 6;

int main(void){
    int var = 0;
    pid_t pid;
    if (write(STDOUT_FILENO, buf, sizeof(buf)-1) != sizeof(buf)-1)
        err_sys("write error!");
    if ((pid = fork()) < 0){
        err_sys("fork errror!");
    } else if (pid == 0) {
        ++globvar;
        ++var;
    } else {
        sleep(2);
    }
    printf("pid = %ld, globvar = %d, var = %d\n", (long)getpid(), globvar, var);
    return 0;
}

输出为:

a write to stdout!
before fork
pid = 9344, glob = 7, var = 1
                // 子进程的变量值改变了
pid = 9343, glob = 6, var = 0
                // 父进程的变量值未发生改变

一般来说,在 fork 之后是父进程先执行还是子进程先执行是不确定的,这取决于内核所使用的调度算法。如果要求父进程和子进程之间相互同步,则要求某种形式的进程间通信。上述程序中,父进程使自己休眠 2s,以此使子进程先执行。但并不保证 2s 已经足够。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

五道口纳什

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值