计算机底层知识拾遗,计算机底层知识拾遗(二)深入理解进程和线程

关于进程和线程,大家总是说的一句话是“进程是操作系统分配资源的最小单元,线程是操作系统调度的最小单元”。这句话理论上没问题,我们来看看什么是所谓的“资

关于进程和线程,大家总是说的一句话是“进程是操作系统分配资源的最小单元,线程是操作系统调度的最小单元”。这句话理论上没问题,我们来看看什么是所谓的“资源”呢。

什么是计算机资源

经典的冯诺依曼结构把计算机系统抽象成 CPU + 存储器 + IO,那么计算机资源无非就两种:

1. 计算资源

2. 存储资源

CPU是计算单元,单纯从CPU的角度来说它是一个黑盒,它只对输入的指令和数据进行计算,然后输出结果,它不负责管理计算哪些”指令和数据“。 换句话说CPU只提供了计算能力,但是不负责分配计算资源。

计算资源是操作系统来分配的,也就是常说的操作系统的调度模块,由操作系统按照一定的规则来分配什么时候由谁来获得CPU的计算资源,比如分时间片

存储资源就是内存,磁盘这些存储设备的资源。在这篇计算机底层知识拾遗(一)理解虚拟内存机制 我们说了操作系统使用了虚拟内存机制来管理存储器,从缓存原理的角度来说,把内存作为磁盘的缓存。进程是面向磁盘的,为什么这么说呢,进程表示一个运行的程序,程序的代码段,数据段这些都是存放在磁盘中的,在运行时加载到内存中。所以虚拟内存面向的是磁盘,虚拟页是对磁盘文件的分配,然后被缓存到物理内存的物理页中。

所以存储资源是操作系统由虚拟内存机制来管理和分配的。进程应该是操作系统分配存储资源的最小单元。

再来看看线程,理论上说Linux内核是没有线程这个概念的,只有内核调度实体(Kernal Scheduling Entry, KSE)这个概念。Linux的线程本质上是一种轻量级的进程,是通过clone系统调用来创建的。何谓“轻量级”会在后面细说。进程是一种KSE,线程也是一种KSE。所以“线程是操作系统调度的最小单元”这句话没问题。

什么是进程

进程是对计算机的一种抽象,

1. 进程表示一个逻辑控制流,就是一种计算过程,它造成一个假象,好像这个进程一直在独占CPU资源

2. 进程拥有一个独立的虚拟内存地址空间,它造成一个假象,好像这个进程一致在独占存储器资源

这张图是进程的虚拟内存地址空间的分配模型图,可以看到进程的虚拟内存地址空间分为用户空间和内核空间。用户空间从低端地址往高端地址发展,内核空间从高端地址往低端地址发展。用户空间存放着这个进程的代码段和数据段,以及运行时的堆和用户栈。堆是从低端地址往高端地址发展,栈是从高端地址往低端地址发展。

内核空间存放着内核的代码和数据,以及内核为这个进程创建的相关数据结构,比如页表数据结构,task数据结构,area区域数据结构等等。

69d48ef4945e8eb6ba78888ae6885dae.png

从文件IO的角度来说,Linux把一切IO都抽象成了文件,比如普通文件IO,网络IO,统统都是文件,利用open系统调用返回一个整数作为文件描述符file descriptor,进程可以利用file descriptor作为参数在任何系统调用中表示那个打开的文件。内核为进程维护了一个文件描述符表来保持进程所有获得的file descriptor。

每调用一次open系统调用内核会创建一个打开文件open file的数据结构来表示这个打开的文件,记录了该文件目前读取的位置等信息。打开文件又唯一了一个指针指向文件系统中该文件的inode结构。inode记录了该文件的文件名,路径,访问权限等元数据。

操作操作系统用了3个数据结构来为每个进程管理它打开的文件资源

2d5180e60492b1c2d25edae2b80eac36.png

fork系统调用

操作系统利用fork系统调用来创建一个子进程。fork所创建的子进程会复制父进程的虚拟地址空间。

要理解“复制”和“共享”的区别,复制的意思是会真正在物理内存复制一份内容,会真正消耗新的物理内存。共享的意思是使用指针指向同一个地址,不会真正的消耗物理内存。理解这两个概念的区别很重要,这是进程和线程的根本区别之一。

那么有人问了如果我父进程占了1G的物理内存,那么fork会再使用1G的物理内存来复制吗,相当于一下用了2G的物理内存?

答案是早期的操作系统的确是这么干的,但是这样性能也太差了,所以现代操作系统使用了 写时复制Copy on write的方式来优化fork的性能,fork刚创建的子进程采用了共享的方式,只用指针指向了父进程的物理资源。当子进程真正要对某些物理资源写操作时,才会真正的复制一块物理资源来供子进程使用。这样就极大的优化了fork的性能,并且从逻辑来说子进程的确是拥有了独立的虚拟内存空间。

b833001f0d377b5d9bfecd1e89e528be.png

fork不只是复制了页表结构,还复制了父进程的文件描述符表,信号控制表,进程信息,寄存器资源等等。它是一个较为深入的复制。

从逻辑控制流的角度来说,fork创建的子进程开始执行的位置是fork函数返回的位置。这点和线程是不一样的,我们知道Java中的Thread需要写run方法,线程开始后会从run方法开始执行。

既然我们知道了内核为进程维护了这么多资源,那么当内存进行进程调度时进行的进程上下文切换就容易理解了,一个进程运行要依赖这么些资源,那么进程上下文切换就要把这些资源都保存起来写回到内存中,等下次这个进程被调度时再把这些资源再加载到寄存器和高速缓存硬件。

进程上下文切换保存的内容有:

页表 -- 对应虚拟内存资源

文件描述符表/打开文件表 -- 对应打开的文件资源

寄存器 -- 对应运行时数据

信号控制信息/进程运行信息

进程间通信

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值