浅谈fork函数
2021-10-22
·
hexer

谈谈进程

从程序员的角度进程有三种状态:

  • 进行 正在执行或者等待执行且最终被调度
  • 停止 进程被挂起且不会被调度,
  • 终止 三种可能:收到终止信号,从主程序返回,调用exit函数

关于进程的函数有exit,fork,wait等,本篇只描述fork

谈谈fork

fork是一个很特殊的函数,用于克隆父进程,创建子进程

一个函数,却有两个返回,挺奇妙的

fork返回的是子进程的PID,即fork之后,子进程是返回的是0(子进程没有子进程了),而父进程返回子进程的PID

0:在子进程中,pid变量保存的fork( )返回值为0,表示当前进程是子进程 >0:在父进程中,pid变量保存的fork( )返回值为子进程的id值(进程唯一标识符) -1:创建失败

内核为fork( )完成以下操作:

  1. 为新进程分配一进程表项和进程标识符 进入fork( )后,内核检查系统是否有足够的资源来建立一个新进程。若资源不足,则fork( )系统调用失败;否则,内核为新进程分配一进程表项和唯一的进程标识符。

  2. 检查同时运行的进程数目 超过预先规定的最大数目时,fork( )系统调用失败。

  3. 拷贝进程表项中的数据 将父进程的当前目录和所有已打开的数据拷贝到子进程表项中,并置进程的状态为“创建”状态。

  4. 子进程继承父进程的所有文件 对父进程当前目录和所有已打开的文件表项中的引用计数加1。

  5. 为子进程创建进程上、下文 进程创建结束,设子进程状态为“内存中就绪”并返回子进程的标识符。

  6. 子进程执行 虽然父进程与子进程程序完全相同,但每个进程都有自己的程序计数器PC(注意子进程的PC开始位置),然后根据pid变量保存的fork( )返回值的不同,执行了不同的分支语句。

特点

1.调用一次返回两次

父进程返回一次子进程返回一次

2.并发执行

通过计算机调度

3.相同却又独立的地址空间

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
    int pid;
    int x = 1;
    pid = fork();
    if (pid == 0) {
        printf("chind : x=%d address=%d\n", ++x, &x);
        exit(0);
    }
    printf("parent : x=%d address=%d\n", --x, &x);
    exit(0);
}

这样的一个函数 我们输出的是

[root@Cai test]# ./test1
parent : x=0 address=-1911367976
[root@Cai test]# chind : x=2 address=-1911367976

由此可见,内存地址完全一样,但是由于是不同的进程,虽然有相同的栈,相同的堆,相同的变量值和地址,但是由于是独立的,对x的改变也是独立的

4.共享文件

例如stdout文件,子进程继承了父进程的打开文件,并指向屏幕,所以父进程和子进程的输出都是指向屏幕

练习1

题目来源:csapp练习8.2

问:父进程和子进程输出什么

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
    int x = 1;
    if (fork() == 0) 
        printf("p1: x=%d\n", ++x);
    printf("p2: x=%d\n", --x);
    exit(0);
}

原来的想法:

如果fork等于0,那么并没退出,还需要执行p2的输出,所以:先进行父进程的输出,只输出p2,再进行子进程的输出,p1和p2

p2: x=0
p1: x=2
p2: x=1

事实上:

进程终止的时候,内核并非立即把它删除,而是保持在一种已终止的状态,直到被他的父进程回收.终止了但还没回收的进程叫僵尸进程

由于父进程终止,子进程还在运行,那么内核会将安排init进程成为它孤儿进程的父进程,init进程是所有进程的祖先(进程),僵尸进程没有运行,但是仍然消耗内存资源

[root@Cai terminateParent]# ./test
p2: x=0
[root@Cai terminateParent]# p1: x=2
p2: x=1

佐证代码:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
    int x = 1;
    if (fork() == 0) {
        printf("parent pid=%d\n", getppid());
        printf("p1: x=%d\n", ++x);
    }
    printf("p2: x=%d\n", --x);
    exit(0);
}

结果:

[root@Cai terminateParent2]# ./test
p2: x=0
[root@Cai terminateParent2]# parent pid=1
p1: x=2
p2: x=1

参考

C语言编译过程 深入理解计算机系统csapp第三版第8章8.4(P513)


OS