Linux基础之进程-fork()函数的详解

目录

一、前言

二、fork()函数

2.1 fork()函数的基本概念

2.2 问题一的解答

2.3 问题二的解答

2.4 问题三的解答

2.5 问题四的解答

2.6 问题五的解答


一、前言

       在上节内容中我们已经学会了使用我们的getpid()和我们的getppid()去查看我们进程的pid,并且学习到了两种查看进程的方式,在我们的正文开始之前,我先对上文的一些内容做一些补充。

       在第二种查看进程的方式中我们看到一个进程的详细信息:

即上图的右半部分,这里我们主要对其中的  cwd 和  exe 进行说明。
       我们经常会遇到一个概念,叫做文件的当前目录,那什么是当前目录呢,这是什么意思呢,子在进程中 cwd 就指向该进程的当前目录,其实就是在默认情况下,进程启动所在的路径,就是当前的工作目录。
       那 exe 又是什么呢,exe 指向的是可执行程序的位置。
       在Linux中我们可通过指令来修改当前的工作目录:

chdir  +  路径 :修改工作目录

二、fork()函数

2.1 fork()函数的基本概念+提出问题

       这里有一个问题,那就是如何去创建一个进程呢,最简单的方法就是执行我们的可执行程序,系统就会生成一个进程。我们还可以通过fork()这个函数在代码去主动去创建进程。

我们可以先用man指令查询一下这个函数:

       简单来说,fork()函数的作用就是创建一个当前进程的一个子进程。那我们要怎么理解去创建一个进程这样的一个概念呢?
       创建一个进程,其实就是系统申请内存,保存我们当前的可执行程序,生成其对应的一个PCB,并将该可执行程序及其PCB加入到我们的进程列表中。
       接下来就让我们先来看看fork()创建的进程有和特征。

      先写一段这样的代码,我们去观测一下该子进程的信息,至于为什么要这样写,我们等会再说,先跑起来看看。

我们会发现确实出现了两个进程,且其中一个进程的ppid是我们一个进程的pid。

       很明显,在我们的红色框框内第一个进程跟我们上节所讲的进程差不多,其父进程会是我们的命令行解释器。而这个第二个进程就应该是我们fork()函数所创建出来的子进程了。那我们再来看一段代码。

再来看看这段代码的运行结果。

       不难发现听音乐和下电影在同时运行,可是我们printf函数明明就只执行了一次为什么会输出两个不同的值,难道是我们的子进程继承了我们父进程的代码吗?就算是继承我父进程的代码,那我的fork()为什么会有两个返回值呢,这也太不符合我们之前所学了。那我们不妨把我们所有的问题全部抛出了一个一个解决。
        问题:

  1. fork()函数干了什么事,我们为什么要使用它?
  2. 为什么fork()函数会有两个不同的返回值?
  3. 为什么fork()的返回值会给父进程返回子进程的pid,而给子进程返回0?
  4. fork之后,子进程和父进程谁先执行?
  5. 如何去理解一个变量却又不同的返回值?

2.2 问题一的解答

       刚刚我们有讲,fork()函数的作用是创建了一个子进程,那这个子进程有什么用呢?
       我们创建子进程主要为了使用其有两个返回值的特性,来帮助我们实现多任务并行运行这一场景。举个例子,比如上文我们想边下载电影边听音乐的场景。可是我们新建出来的子进程按道理来说是不会有代码的,那其是如何满足我们的需求的呢?

       所以在fork之后,我们的父进程和我们的子进程会执行一样的代码,那这里可能就有人会问了,那fork之前的代码呢,子进程就不执行了吗? 是的,确实是不执行了,但是不是因为其看不到父进程的所有代码,而是子进程同样继承了父进程的寄存器。

2.3 问题二的解答

       问题又来了,为什么fork()会返回两个不同的值呢? 在上文我们有提到,一个进程是怎么被创建出来的,即当其可执行程序和PCB被加入到进程队列时,该进程就已经被创建成功了,那这个时候,fork()函数有没有结束呢? 没有,只能说其创建进程的任务完成了,但其还又一个语句没有执行呢,什么呢,那就是return。再回答第一个问题的时候我们就有提到,子进程会继承父进程的寄存器,那么此时寄存器是不是指向的是return语句呢? 很显然是的,那么父进程返回一个值,子进程返回一个值,不就有两个返回值了吗? 放张图,加深一下理解。

2.4 问题三的解答

为什么fork()的返回值会给父进程返回子进程的pid,而给子进程返回0呢?
这是因为父进程它很有可能不只有一个子进程,那其要是想对它的子进程进行管理该怎么做呢?那肯定是要根据其子进程的标识符呀,那么进程的标识符是什么呢,就是其pid。给自己的返回自己的pid没什么意义,所以fork就给子进程返回0。

2.5 问题四的解答

       创建完成子进程,这仅仅是一个开始,创建完成子进程之后,系统的其他进程,父进程,子进程都等着被系统调度执行。当父子进程的PCB都被创建且都在运行队列(操作系统会把其将要执行的代码放入一个队列中进行排队,这个队列就叫运行队列)中排队的时候,哪个进程先被调度,那个进程就先被执行。但是这个调度顺序是不确定的,由操作系统本身决定,由各自PCB中的调度信息(时间片、优先级等)+ 调度器算法共同决定的。

2.6 问题五的解答

为什么有两个返回值我们已经知道了,但是为什么这两个返回值不相同呢?
       这里就要提到,进程的独立性,其独立性首先就体现在各自的PCB是不会相互影响的。虽然父子进程共享代码,但是代码是只读的,无法被修改。但是数据却是可以被修改的,如果存在一个全局变量,我父进程需要根据这个全局变量作为判断条件,而我的子进程却会修改这个全局变量,那这不就影响到了我们的父进程了吗?所以数据每个进程都得想办法私有一份,怎么私有都拷贝下来吗,如果代码量很大的话,拷贝的代价就有点大了,而我们的操作系统是不会允许这样降低效率的事情发生的,那么我们该怎么办呢?
       这里主要是使用到了写时拷贝的方法,父子进程还是一样的共享代码,只有当自己要修改其中的某个变量时,才把这个变量拷贝过来,以达到不会相互影响的状态。

  • 31
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值