对进程的初步认识以及fork()函数的理解

对进程的初步认识以及fork()函数的理解_第1张图片

什么是进程 

进程是什么呢?其实解释的通俗浅显一点就是我们运行到内存的程序。我们知道运行一个磁盘里的程序时,会将该程序先加载(将磁盘的数据拷贝)到内存当中,因此该程序就可以称为一个进程。首先我们以Windows操作系统为例, 可以快捷键查看当前正在运行的进程:Ctrl+shift+Esc

对进程的初步认识以及fork()函数的理解_第2张图片

进程PCB 

 其实在程序加载到内存之前操作系统这个软件早早的就已经加载到内存当中,而当你双击运行程序加载到内存的过程都是操作系统帮你完成的,而你的内存当中肯定不止有一个进程,所以此时操作系统就需要对你的进程做管理。而管理进程并不是管理这款运行起来的软件,而是管理该进程的PCB(process control block)进程控制块。而对进程PCB的理解是这样的,想要管理进程肯定需要该进程的各种属性以及实时状态,所以操作系统就将该进程以一个结构体的形式存储好该进程的所有属性,而该结构体就称为进程PCB。每一个进程都有对应的PCB,最终都由操作系统进行管理。而cpu处理进程时,拿到的就是进程对应的PCB。


所以最终我们就可以初步了解:进程=可执行程序( 代码和数据)+内核数据结构(PCB就是其中之一,方便OS对进程管理) 

 Linux下的进程PCB

我们知道每个进程都有对应的PCB,而PCB其实是一个统称,就像Linux下的进程PCB具体就是struct task_struct的一个结构体。其实每个进程的PCB都是通过类似于双链表的形式衔接起来得,所以可以说,每个人进程的PCB还存放着两个指针,分别指向前后两个节点的数据。

进程id

而这个struct task_struct(Linux下的PCB)存放的核心数据有哪些呢?自然少不了每个进程对应的标识符:pid(进程id)以及所属标识符:ppid(父进程id)

对一个进程的pid 查看可以用getpid指令,而对一个进程的父进程查看可以getppid指令。

对进程的初步认识以及fork()函数的理解_第3张图片


但是有一点:每次运行同一个进程时,该进程的id都会发生改变,但是父进程id却不变

对进程的初步认识以及fork()函数的理解_第4张图片 其实我们的命令行当中,父进程一般都是命令行解释器:shell(Linux下的bash)

所以当我们在命令行中输入的所有指令,运行的程序时,对应的父进程都是bash。

 


进程查看 

1

对进程的查看可以采用 ps axj 指令,查看当前进程。所以可以进行测试:

倒计时的一个程序:

对进程的初步认识以及fork()函数的理解_第5张图片

执行结果: 

 运行结果如上,我们知道可执行程序运行起来就算是一个进程了,所以进行查看该进程时就可以查看到,而一旦运行结束,该进程也就没了。

2

还可以通过ls/proc查看所有进程,proc其实就是根目录结构下的一个动态目录结构,该目录下存放着所有存在的进程,而proc下的各个目录都是以存在的进程id命名的,而这里面的内容就是关于对应进程在内存当中的信息以及属性等数据。

这里其实有一个进程属性需要提一下,cwd(当前工作目录),默认情况下,进程启动所处的路径就是当前路径。可以通过chdir()函数更改当前进程的工作目录

Linux中进程的创建

  1. 命令行中直接启动进程
  2. 通过代码来创建进程

其实启动进程的本质就是创建进程,而进程一般是以父进程为模版创建的,进程大部分的属性与父进程相同,只有少部分不同于父进程。

初识fork()

通过代码来创建子进程,就要使用到fork函数,这是一个系统调用。

所以在调用fork函数,只有父进程会执行上面的代码,fork之后会以当前进程(父进程)为模版创建子进程,之后系统会执行两个进程,也就是父子进程一起执行。而且该函数会为父进程返回子进程的pid,为子进程返回0。如果fork失败的话就不会创建子进程,则会为父进程返回-1。

测试用例:

对进程的初步认识以及fork()函数的理解_第6张图片

执行结果:不断循环

对进程的初步认识以及fork()函数的理解_第7张图片

这个执行结果肯定会充满很多疑问???

对进程的初步认识以及fork()函数的理解_第8张图片

前言:

执行可执行程序会先将程序加载进内存中,其实在fork函数的内部会以父进程为模版创建子进程,也就是会将父进程的大部分属性都拷贝到子进程的PCB中。然而子进程并不是从磁盘来的,而是在内存创建的,所以父子进程会共享代码,也就是子进程与父子进程指向同一份代码,但是内部的数据却是独立成各自一份(为了防止一方数据修改而影响另一方的执行结果)。进程之间具有独立性,尽管是父子进程,删除一个进程并不会影响另一个进程,并且进程指向的代码是只读的,并不能做任何的修改,不会受到PCB的影响。

但是为什么子进程不会执行fork之前的代码呢??

其实代码是按照顺序依次执行的,执行当前代码时,操作系统内部寄存器(eip)每次都会保存下一次执行的代码数据,所以fork系统调用执行之后会分流,所以eip指向的就是fork之后的代码,而eip这样的程序计数器也会被子进程继承,所以子进程记录的读写位置就是fork之后。

为什么变量i接受了两个返回值??

fork函数内部会为子进程创建PCB,将父进程的核心代码数据都拷贝给子进程,最后返回。但是实际并不是在fork函数完全执行完以后发生分流的,而是在执行完fork的核心代码之后就已经分流了,所以fork的return会被执行两次,分别为父子进程返回相应的值。     

返回实际就是写入数据 ,将数据写入变量i中,而变量i起初是父进程定义的变量,属于数据,而父子进程数据独立私有,避免数据量过大,造成空间浪费,所以满足写时拷贝特性,因此返回的时候发生了写时拷贝 ,导致同一个变量会有不同的值。                            


你可能感兴趣的:(Linux,Linux,进程)