计算机系列之大话系统操作原理

4、大话系统操作原理

一段程序本身是不会执行的,就好像是一个做菜的食谱(食谱本身不会做菜)。

程序会由程序员写好放在硬盘中永久保存,而食谱可能放在抽屉里。

那为了做这道菜,需要厨师将食谱拿出来,放到桌子上,然后按照步骤做菜。

这相当于 CPU 来从硬盘中读取程序到内存中,方便 CPU 读取,那个这个在内存中的可执行程序实例就叫做进程。

一个程序若多次取到内存中,则变成了多个独立的进程。

硬盘中多个程序被取到内存中运行时,当然也就创建了更多的进程。

内存中任何一个地方都有相应的地址,方便访问。

而在内存中的每个进程,自己内部都有一个虚拟独立的地址空间。

在进程内就可以根据虚拟地址来访问。

那进程间怎么相互访问的呢?

首先,进程是程序执行的完整单位,所以大部分时间都是在进程内,那进程间就需要进程间通信(IPC,Inter Process Communication)。

每个进程都以为自己独占着整个内存,不需要关心其他的进程的实际位置。这样就把进程们很好地分割开来了。

每个进程首先有加载的程序,通常只有一个程序计数器(记录当前程序执行的位置),会按照程序顺序计算,这里的一个执行流就是一个线程。

如果有多个线程的话就需要多个程序计数器,每个线程会独自运行。

除此之外,每个线程还有寄存器、堆栈等程序运行时的状态信息。

同时,线程间共享的则有地址空间、全局变量、打开的文件等信息。

为什么在进程中还要有更小的“进程”——线程呢?

假如这里有个文档编辑器,自然那就是一个进程,存放着相应的程序和文档。那现在用户用键盘在第二行末尾打一个回车,我们需要交互的程序接受键盘的按下事件,然后,布局的程序将文字重新计算位置,再把它们渲染出来。另外,每隔一段时间需要写入的程序保存文档到硬盘中,所以这三个程序最好能够并行执行(键盘交互程序、渲染程序、写入文档的程序),但它们又需要访问修改同一个文档,所以肯定是在同一个进程中,所以这时候需要更轻量级的三个线程(交互线程、渲染线程、保存线程)。

线程是并行的最小单位。假如一个计算机只有一个单核 CPU,也就是一次只能执行一个线程。那就需要对每个线程轮流执行,每次单个计算的时间成为 CPU 时间片,实际只有几十毫秒(Linux:5ms~800ms),用户根本感觉不到的。

对于线程(如线程 A)来说,存在等待 CPU 的时候,称为就绪状态。一旦 CPU 过来执行(线程 A),就转变成运行状态。当 CPU 转而执行其他线程(线程B)时,线程 A 就又会变成就绪态。假如线程(线程A)正在执行中,程序向硬盘发送访问请求,然后等待,这时 CPU 就变成空转了。所以线程(线程A)变成阻塞状态。CPU 转而执行其他线程,等到硬盘的数据恢复,线程(线程A)就从阻塞状态变为了就绪状态,等待 CPU 的继续光临,然后继续执行。

如果是多个 CPU,确实可以让多个线程真正地并行计算,但是往往线程会很多,所以还是需要时间片轮转,为了简化,CPU 在内核中为每个线程提供各自的虚拟 CPU,每个线程会认为自己是一直独占着CPU,这样它们就不需要考虑时间片轮转的问题了。

总结:

1、一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程,线程依赖于进程而存在。

2、进程:是操作系统进行资源分配和调度的一个独立单位。进程拥有独立的地址空间,一般情况下至少包括文本区域(代码)、数据区域和堆栈。每个进程都有自己的内存空间和系统资源。

3、线程:是进程的执行流,是CPU调度的基本单位,也是一段程序执行的路径。一个进程中可以包含多个线程,它们共享进程的内存空间和资源,但是每个线程有自己的程序计数器(PC)、寄存器集合和栈。

互斥

计算机要执行的程序需要从硬盘中读取到内存中,形成一个个的进程。

每个进程就是一个独立的资源单位,内部有独立的虚拟地址空间,所以就按照这个地址访问,那么进程间是如何访问通信的呢?

比如这里在内存中有两个进程,还有一个负责打印的守护进程,想要打印的话,会有一个共享的打印目录,等待被打印,每一行都有相应的编号,假如现在已经有2个文档等待打印,有一个共享变量 in 指向下一个加入的位置编号 5,另一个是 out 指向的是下一个需要打印的位置编号 3。现在假如只有一个 CPU,所以要交替着运行这两个进程。那假如现在正在运行进程 A,这时进程 A 想要打印一个文档,就需要向当前的打印队列末放入,它会先从共享的全局变量 in 中读取位置 5,刚好这时 CPU 认为进程 A 运行的够长了,就转而运行 B 了,而这时进程 B 也恰好想要打印,所以和进程 A 一样会从共享的变量 in 中读到了位置 5,接着向位置 5 真的放入了 文档 B,恰好这时 CPU 认为进程 B 差不多了,又转而运行进程 A,进程 A 继续刚才的操作,既然已经读取了共享变量 in 的值 5,那就按照位置5也写入了位置5,这导致了文档 B 还没被打印,就被擦除了。当然进程A还需要把记录加入位置的变量 in 加 1,并修改全局变量 in 为 6,而打印进程只是单纯地读取out值,按照位置打印,并不会发现进程 B 的文档被遗漏了。

把上面的例子一般化就是:只有一个CPU 轮流执行两个进程,它们共享一个全局变量,当两个进程中的部分代码同时读写这个全局变量的时候,就会因为竞争而产生问题。可以把这部分代码称为临界区。

解决方法就是读写互斥,也就是进程 A 访问临界区的时候,进程B不能访问,保持等待,等进程A出了临界区后,进程B才能进入。

那么如何实现互斥呢?可以不可以加个锁,比如就是一个值,值为1的时候,代表共享已上锁,比如此时进程B的临界区在访问全局变量,已经上锁,等上锁的进程走出临界区,就解锁把值改为0,供进程A来访问。这样解决了吗?显然没有。

这个锁不也是一个共享变量吗?进程们都在读写这个锁时,还是会发生竞争现象。

那怎么办?

可以用 严格轮换法。可以通俗理解为“孔融让梨”的做法。

操作系统原理-进程-互斥

  • 12
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

碳学长

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值