前言
本文我们介绍C语言系统编程中关于进程的创建、执行程序和进程结束。我们的进程都会有一个唯一的标识PID,和我们的身份证一样是唯一的数字,我们可以根据这个PID对进程进行控制,结束、开始、挂起、运行等操作。接下来我们先看一下进程实现需要用到的几个基本函数。
fork函数
一个运行的进程可以通过调用fork函数创建一个新的进程,由fork创建出来的进程我们叫子进程,我, fork函数被调用一次会返回两次,子进程返回的值为0,父进程返回的是子进程的PID,为什么呢?因为一个进程可以有很多的子进程但是没有函数可以获取子进程的PID。为了方便管理子进程所以fork会返回给父进程自己创建出来子进程的PID。那我们回想一下为什么我们子进程返回的是0,理由是我们子进程只会有一个父进程,而父进程的PID是可以通过函数getppid来获取的,对于每一个进程想知道自己的pid可以使用函数getpid来获取。
子进程与父进程的关系
我们在这里说一个知识点,虚拟内存和物理内存的关系,我们32位系统进程的产生0-4G(可用)的虚拟内存空间,1-3G是我们的用户区,3-4G是我们的内核区,但是我们有些电脑的内存没有那么大,那这时候怎么办?我们CPU里面有一个MMU模块可用帮我们把0-4G的虚拟内存映射到我们的真实物理内存上,下图就是把两个进程0-4G的虚拟内存映射到只有512M的物理内存条上,映射的时候我们要注意的是,内核段的虚拟内存是共用的,两个进程的内核区映射到物理内存上是用一个地址,但是我们的用户区虚拟内存映射到物理内存上是独立的。
我们看下边的图就会更好的去理解,两个进的内核段是共用的,那你可能就会问了那我们不会相互影响么?我们的进程由我们的进程控制模块PCB来控制,每一个进程都有自己的PCB控制模块,里面存放着自己进程的一些相关的信息,我们的PCB实际上是一个结构体,每一个进程一个结构体,那经过MMU的映射把这些结构体存放相同的物理内存上也是可以的并不会相互干扰,而且也只有这样,两个进程之间才能实现通信。
执行顺序
一般来说我们进程执行是没有先后顺序的,换言之就是子进程和父进程谁先执行并不清楚,这取决于内核使用的跳读算法,如果我们需要按照我们自己的想法去实现进程顺序,那我们可以使用休眠,举个例子:现在我们不知道父进程还是子进程先执行,我们想让子进程先执行完了后在执行父进程,那我们的父进程加一个sleep(2)休眠2秒钟等待,休眠的时候CPU就把子进程执行了,等休眠时间到才开始执行父进程。
实现代码
结果
我们看一下没加sleep之前的打印情况,明显进程执行的顺序是杂乱的,然后加上休眠后我们再来看看结果
很明显是先执行完父进程然后在执行子进程,我们看一下圈起来的两个红圈,我们看一下代码,代码 printf("******进程后的代码***** ");只有一次,但是为什么会打印出两次,子进程打印一次父进程打印一次?
在这里我们讲一个进程的知识点,在父进程fork一个子进程后,创建出来的子进程其实是以父进程复制出来的一份,父进程里面的代码子进程都有,所以这就为什么一个打印语句执行两次了,那有些读者可能就会疑问了,那你不是说代码一模一样嘛,那为什么子进程不打印下面的代码?
这是因为在父进程执行这些语句的时候子进程还没有被创建,程序是从上往下执行的,创建出的子进程也是往下走,而不是返回去从头执行,你可以理解为子进程共享父进程 fork之后的代码。
现在你对C语言系统编程的进程有一定的了解了么?我们设想一下,如果我父进程声明一个变量,然后按理说我们fork创建的子进程也会拿到这个变量,那如果我们在子进程改变了这个值后会影响到父进程的变量值么?
如果你不知道我建议你在往上翻一下看一下我刚刚说的内存映射部分再好好理解进程之间的关系。