相信很多人都看到过课本上写的进程的概念,那么真的理解了吗?
课本上是这样讲的,课本概念:程序的一个执行实例,正在执行的程序等。
那么进程到底是什么?我先把内核层面上的概念拿出来:内核观点:承担分配系统资源(CPU时间,内存)的实体。
相信很多人跟我一样,在学 linux 之前,每次写代码的时候,写好了一个程序,我们都讲把程序跑起来....,这个程序怎么怎么.....,但是现在这里概念要变一下了。
当我们写好一个程序,将代码运行之后,那么写的程序在底层是来看是怎么运行的?怎么执行代码等等....,那么我这里讲详细讲讲。
这里我先把进程的概念抛出来,进程是什么呢?
进程 = 内核数据结构 + 代码和数据
那么代码和数据倒是可以稍微理解,但是什么是内核数据结构?(以下我先暂且讲程序,先不讲进程)
这里先问一下?在系统看来,每次运行完一个程序(运行完指的是从最开始运行到退出,就好比你写了一串代码,从 main 运行到 return 退出)才运行下一个还是可以运行很多个?
这个问题我觉得很好理解,当你写一个死循环不退出,那么在起一个程序运行,那么这个是不是也可以运行?所以是不是就是说可以运行很多个?而不是只能一个一个运行!(我这里讲的一个一个运行依然是一个运行完毕退出才运行下一个)
所以说,系统中是不是存在很多个被运行起来的程序,那么这么多程序被运行起来,需要操作系统管理吗?需要的对吧!怎么管理呢?六字真言:先描述,在组织
这就跟我们每个人在学校一样,学校里有好几万学生,那么学校肯定也是需要管理我们这些学生,不然那就乱套了。
那么既然都要被管理起来,怎么管理?我先继续用学生的例子讲:
那么我们学生要被管理起来,是怎么管理的?是不是学校为每个学生创建了属于每个学生独有的(私有的)学生学号、班级、宿舍号等等,那么每个学生的这些信息,是不是都被在学生端的教务系统中有保存?我学校反正是这样,登录学生端的教务系统,里面保存每个学生的个人信息,有学生的学号、班级、宿舍号、身份证号等等,这些信息是可以直接标识到对应的每个学生个人的身上,而不是一个信息可以找到很多个学生!如果这样那么就乱套了。
那么问题回归操作系统上来,系统中存在大量的程序,操作系统是不是也要管理,并且也是为每个进程创建属于它自己的信息?那么是什么呢?应该可以猜到一些吧?比如像学生学号一样表示程序的号码等等。
但是学生的信息被保存在教务系统中,那么程序的信息被保存在哪里?
在操作系统中,保存每个程序信息的叫 PCB 结构体,这个 PCB 是什么?在 linux 操作系统中 PCB 是 task_struct,那么我们可以看一看源代码:
上面就是一个程序运行起来,系统为它创建的 PCB结构体,那么这里面这么多属性,方法(c语言的结构体里面不能写方法,但是可以定义函数指针), 我们目前学的就用到里面的一点点。(mm_struct、files struct)这个结构就是进程的PCB结构!!!
每个学生有他的学号,班级等等,那么跟程序相关的,文件?内存?等等.....
所以这里就要变一下程序概念了!!!程序是指你写的可执行文件(代码和数据)!!!
而 进程 是指 内核数据结构 + 代码和数据
那么内核数据结构有什么?先谈谈 mm_struct
当程序被加载起来,程序的代码和数据是要被加载到内存的,这里的内存指的是物理内存!!!
那么我们常用的不就是物理内存吗?不是的!我们常用的是虚拟内存!!!
这里又一个新的概念 虚拟地址空间,那么什么是虚拟地址空间?在PCB结构中是什么?每个进程都有吗?
这个虚拟内存就是 mm_struct,是每个进程独有的,看过我之前讲的C/C++内存管理的就应该知道,内存有3G的用户空间和1G的内核空间如下图,那么虚拟内存呢,如下图:
当进程被创建出来,那么操作系统为了管理每个进程,为每个进程创建PCB结构体,这个PCB结构体中就包括它的虚拟地址空间 mm_struct,每个进程独有的!!!,而中间的是页表,是虚拟内存映射到物理内存上的中间介质,我这里暂且不讲页表。
所以,进程是什么?
进程 = 一堆内核结构 + 代码和数据
那么这里最后解释一下程序被运行起来,在操作系统看来是怎么样做的?
当程序在编译的时候,形成的可执行程序的时候,在还没被加载内存的时候,还在磁盘上的时候,也就是处于一个.exe文件(windows)的时候,程序内部是有地址的!!
因为操作系统不仅要遵守地址空间,编译器也要遵守,所以在编译器编译代码的时候,就已经形成了代码区、全局区等等,并且采用和linux一样的编址方式,每个变量、每行代码都进行了编址,所以程序在编译的时候,每个字段早已具有了虚拟地址。
当创建进程的时候,将可执行文件加载到物理内存中,可执行文件中的代码指令依然时按照虚拟地址编码的,并且加载到物理内存的时候,虚拟地址也一并加载到了物理内存,内部时虚拟地址,但是外部是物理地址然后因为可执行文件内部是按照linux编址的方式,所以每行代码指令可以对应到虚拟地址上的各个区域,页表的左边填的就是虚拟地址,右边是物理地址,通过页表映射到物理地址
当CPu通过虚拟地址获取到物理地址中的代码指令的时候,也获取到了地址,此时的地址依然是虚拟地址