目录
1. 操作系统进程状态
操作系统也是一门学科,操作系统可以说是计算机界的哲学,因为它的概念能用到很多方面。
操作系统中的进程状态在 Linux 内核中就是个整数,这个整数在进程的 task_stuct 中,类似:
int status
我们先来看看一些操作系统的书上是如何讲解的,以 《OSTEP》 为例:
一个进程通常有三种状态:就绪、运行、阻塞。
Ready(就绪):进程已经准备好运行。
在就绪状态下,进程已经准备好运行,但由于某种原因,操作系统选择不在此时运行。
Running(运行):进程正在处理器上运行。
在运行状态下,进程正在处理器上运行(这意味着它正在执行指令)。
Blocked(阻塞):一个进程执行了某个操作(比如I/O),其它进程可以使用处理器。
在阻塞状态下,一个进程执行了某种操作,直到发生其他事时才会准备运行。比如进程向磁盘发起 I/O 请求时,它会被阻塞。因此其他进程可以使用处理器。
这些操作系统教材的理论,必须保证在 Linux 下是正确的,在 Windows 下也是正确的。这也是操作系统这门学科,为什么称之为 " 计算机学科的哲学 "
1.1 运行态
运行态:进程在运行队列中,代表我已经准备好了,随时可以调度。
进程只要在运行队列中,就叫做 运行态。
每一个 task_struct 都能找到对应的代码和数据,让进程排队。(相当于队列)
只有在运行队列里的进程才叫做运行态,而运行态不代表我正在运行,而代表已准备就绪。
1.2 终止态
终止态:进程还在,只不过永远不会被调度器调度运行了,它随时等待被释放。
进程都终止了,为什么不立马释放对应的资源,而要维护一个中止态呢?
释放要花时间吗?有没有可能,当前你的操作系统很忙呢?
释放也需要成本和时间。所以说,你退出程序系统直接退出,只是理想状态罢了。既然操作系统不一定能立马释放,那我们就必须得维护一个终止态,来告诉操作系统自己已经退出了,等操作系统不忙的时候再来释放。
1.3 阻塞态
阻塞态:进程等待某种资源(非CPU),资源没有就绪的时候,进程需要在该资源的等待队列中进行排队,此时进程的代码并没有运行,此时进程所处的状态就叫做阻塞。
为了讲解进程阻塞,我们先了解两个知识点:
- ① 一个进程使用资源的时候,可不仅仅是在申请 CPU 资源
- ② 进程可能会申请其它资源:磁盘、网卡、显卡,显示器资源……
如果我们申请 CPU 资源无法暂时无法得到满足,这就需要排队的 "运行队列" 。那么如果我们申请其他慢设备的资源呢?也是需要排队的(task_struct 在进程排队)。
CPU 的轮转周期既然很快,CPU 得多快呢?
CPU 太快了,所以才会有内存这样的设备。操作系统的核心工作叫做先描述再组织,通过这样的方式来对软硬件资源作管理。
当访问某些资源(磁盘,网卡等),如果该资源暂时没有准备好,或者正在给其他进程提供服务,那么此时:
- ① 当前进程要从 runqueue 中逐出。
- ② 将当前进程放入对应设备的描述结构体中的等待队列。
上面这些任务都是由操作系统完成的,实际上就是对进程的管理任务,仍然是 "管理" 的本质。当我们对应的设备就绪,在硬件层面上准备完毕,一定会通过某种方式让操作系统知道。(这种方式我们放到后面说)
如果已经可以入场了,会把该进程的PCB从等待队列放入运行队列中。放到运行队列中,CPU 就可以去处理这个进程了。
当我们的进程此时在等待外部资源的时(处于等待队列),该进程的代码不会被执行。当前进程去等待某种资源就绪而导致并不运行时所处的状态,就叫做进程阻塞。操作系统要进行管理,必须先描述再组织,所有的资源都是需要排队的,本质上都是PCB在排队。
1.4 挂起态
挂起态:一个进程对应的代码和数据被操作系统因为资源不足而导致操作系统将该进程的代码和数据临时地置换到磁盘当中,此时叫做进程挂起。
挂起和阻塞很像,最终挂起也是卡住,但是挂起和阻塞在操作系统的定义上是不一样的。
如果内存不足了怎么办?操作系统就要帮我们进行交换 内存。
短期内不会调度(你等的资源,短期内不会就绪)进程,它的代码和数据依旧在内存中,那岂不是在白白的浪费空间?
操作系统就会把该进程的代码和数据置换到磁盘上,这样的进程就是进程挂起。
往往内存不足的时候,伴随着磁盘被高频率访问,就可能是因为操作系统一直在做辗转操作。
2. Linux 进程状态
为了研究 Linux 进程状态,我们把源码先拿出来看看:
/*
* The task state array is a strange "bitmap" of
* reasons to sleep. Thus "running" is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
static const char * const task_state_array[] = {
"R (running)", /* 0 */
"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */
"T (stopped)", /* 4 */
"t (tracing stop)", /* 8 */
"X (dead)", /* 16 */
"Z (zombie)", /* 32 */
};
看了上面操作系统进程状态应该只知道运行态R,剩下的 SDTTZX 是什么?
2.1 运行态R
R运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。
这里先演示运行态R,运行态应该很好演示,进程跑起来应该就是了吧?这里创建一个linux_8目录用于演示,然后写Make file:process.c生成process:
我们还是用上一章学的 ps 方式,当时是为了看pid和ppid的。
这里我们也能拿 ps 指令来看进程的状态,我们像上节课一个开两个窗口,一个运行代码,一个输入:ps axj | head -1 && ps axj | grep process | grep -v grep
这个 STAT 栏记录的就是该 process 可执行程序进程的状态了,
为什么是S+?
声明:状态后面跟加号,表示是一个 前台进程,你只需要知道的是,能够在键盘上 Ctrl+c 暂停的都可以叫前台进程。
我们的 process 不是在运行吗,怎么不是R+?
"不要用你的猜测和感受去衡量 CPU 的速度"
我们 process.c 里的代码值得执行的也就一个 printf 输出语句而已,很快时间就完了,所以大部分时间你都在 sleep(1) ,如果把sleep(1)去掉呢?:
几秒,printf已经刷了几十万次, STAT 栏还是S+状态?
还是这句话:"不要用你的猜测和感受去衡量 CPU 的速度"
你说 printf 就 printf 吗?这个死循环在不断像显示器打印的时候,显示器本身是个外设,它非常慢,即便它闲着呢,准备好刷新它也要花时间的,所以这个进程它看起来像死循环地进行 printf 打印,实际上这个进程约 90% 的情况都在等所对应的显示器就绪进行打印,因为显示器太慢了。只是因为打印的东西很快一瞬间就完成,所以我们 ps 查看到的这个进程,大部分情况在内核中都处于S状态。
不要认为刚才刷的那么猛,很快,那就错了,人怎么能和机器比呢?你的 printf 代码就那么一点,CPU 一瞬间就跑完了,跑完之后发现显示器设备没有就绪,没办法给你刷新,冯诺依曼那一章我们说过,打印数据可不是直接打印到外设上的,是刷到我们对应的内存里的,你 pritnf 里面带了 \n 所以要刷盘,所以显示器不一定就绪,几遍就绪它也很慢。所以大部分情况下我们的进程都在等待显示器的资源。
我们虽然还没开始介绍 ,但是我们能猜测出来了:" S 是一个休眠状态 "
那我现在就是想看运行态R呢?
好,现在我们再修改一下 process.c,我们就把printf注释掉,单纯的死循环:
这个代码也没有访问其他外设资源,也没有读文件也没有读磁盘也没打印,就纯纯的死循环。所以这个进程不访问任何资源,只等你 CPU,只要你被运行期间不访问外设,就不会被阻塞。不访问外设,一直在等待队列中,这就让 process 达成 R+ 运行状态。
2.2 睡眠态S
S睡眠状态(sleeping):意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠 (interruptible sleep))。
若一个进程是S状态,那么它也能称作是阻塞状态,这也意味着它一定是在等待某种资源。" 这就对应了我们刚才讲的阻塞状态 的概念了 "
阻塞状态除了上面的例子还有许多例子,比如:
我们目前等待的是硬件,刚才举的 process.c 代码例子中 sleep 其实就是等待软件。是阻塞状态,其实是一种休眠状态,在 Linux 中它是以休眠状态的方式,让我们的进程去等待某种资源。
为什么睡?因为它要等待资源不就绪。既然睡了,那谁会去叫醒它呢?操作系统会去做叫醒它,将它的 S 状态设置为 R 状态,将其列入运行队列中。
我们一般把 S 状态叫作 浅度睡眠,也叫做 可中断睡眠。
- 顾名思义,当进程处于 S 状态,它可以随时被唤醒。
- 不仅仅是操作系统可以唤醒,你也可以唤醒,甚至你想杀掉它都行:kill -9 进程pid
有浅度睡眠就有深度睡眠咯?那就是下面的:磁盘休眠态D
2.3 磁盘休眠态D
D磁盘休眠状态(Disk sleep):有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的 进程通常会等待IO的结束。
下面我们来看 D 状态,D 状态也是一种阻塞状态,它也是要我们得进程等待某种资源。资源不就绪,就处于某种等待状态。那么与 S 状态有什么区别呢?我们细看:
S (sleeping)
D (disk sleep)
disk sleep?disk 表示磁盘,那应该适合磁盘有关联了。
一般而言,在 Linux 中,如果我们等待的是磁盘资源,我们进程阻塞所处的状态就是 D 状态。
如果一个进程中,有大量的数据需要存放到磁盘上,这个进程的PCB同样会被操作系统放在磁盘的等待队列中,因为数据量非常大,磁盘的速读又很慢,所以PCB需要很长的时间来等待磁盘的应答信号。
当内存中又加载进来很多进程时,内存空间就会吃紧,此时操作系统为了维护正常运行,就会将一些长时间处于睡眠态S(浅睡眠)的进程杀掉,向磁盘传送大量数据的进程就会被杀掉。
当磁盘存储数据出现了问题,它就会向内存中原本的进程发送应答信号,但是此时这个进程已经被操作系统杀掉了,所以就无法接收信号也无法给出相应的指示。磁盘没有收到指示后会将这些数据放弃掉,如此一来就会导致数据丢失,如果是一些很重要的客户数据就会导致很大的损失。
- 用户给可能被杀掉的进程一个免死金牌(设置一个标志位),此时操作系即使在内存吃紧的情况下也不会杀掉处于磁盘休眠态D的进程。
被给与免死金牌,并且处于睡眠状态的进程,就被叫做深度睡眠状态。深度睡眠状态的进程无论是使用Ctrl + C还是使用kill -9都无法杀死,只能等它自己醒来或者重启系统,再不行就只能拔电源了,所以这里就不演示了。
实际上,如果一个系统中存在大量的 D 状态进程,关机是关不掉的,要长时间关都关不掉,最后只能是强制断电拔插头,才能关掉。有时候强制断电你可能会发现有些东西坏掉了,这其实并不是硬件坏掉了,而是系统的某个软件就不能用了,可能在你强制断电的时候 操作系统中的某些数据正在让磁盘写,你强制断电就是将所有设备都断电这也当然会包括磁盘,所以导致数据丢了。
2.4 暂停态T
其实给用户呈现的都是大T,我们前面给的小t只是为了区别,两T在内核当中实际上没有区别,只是 tracing stop 比较特殊一些。
我们先介绍一下这个 暂停 究竟是什么,什么情况进程会被暂停呢?
进程暂停与进程休眠(阻塞) 没有关系,只是单纯不想让这个进程跑了。比如有些进程在执行任务时,用户想让这个进程暂停一下,这其实很好理解。当你点击暂停的时候下载对应的代码就不跑了,此时这个进程你就可以认为是暂停状态。
再比如说我们调试程序,让程序打断点之后让程序运行起来,程序在打断点处停住的时候是将进程暂停了,所以在 gdb 调试或在 VS 下调试时你会发现程序会停下来,这就是暂停。所以,暂停具备很强的功能性。
演示暂停态T:在上一章介绍 kill 命令的时候我们说后面跟 -9 就行了,具体的内容我们放到后续的信号章节去讲解,这里我们暂且再讲一下,我们可以输入 kill -l 列出所有的信号:
我们可以看到 -9 实际上就是 SIGKILL,我们现在需要用一下 -19 SIGSTOP的信号。这就是用来暂停的。我们让一个进程跑起来,然后输入 kill -19 进程的pid
此时,我们查看一下该进程的状态,发现变为 T 状态了。我们将该进程暂停了,现在如何将进程解除暂停呢?用 -18 SITCONT信号。CONT 就是 continue,继续的意思,输入 kill -18 进程的pid 即可解除暂停:
此时发现S后面没有+,所以不是前台进程,在右边输入Ctrl c是不能结束进程的:
(用kill -9才行)
2.5 死亡态X
X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。
死亡态X很简单,dead 代表死亡,所以 X 状态对应的就是 死亡状态。
当一个进程执行结束或者是被操作系统杀掉,它的task_struct结构体对象从PCB中删除,并且对应加载到磁盘上的二进制代码也被删除,此时这个进程就处于死亡状态。
- 当一个进程所占内存的所有资源被回收以后,这个进程就处于死亡状态。
进程的死亡状态是无法看到的,因为只有在回收完成的那一刻才会出现,所以无法演示。
2.6 僵尸态Z
僵尸状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程(使用wait()系统调用,后面讲)没有读取到子进程退出的返回代码时就会产生僵尸(僵死)进程。
僵尸进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态
僵尸状态:当一个 Linux 中的进程退出的时候,一般不会直接进入X状态(死亡,资源可以立马被回收),而是进入Z状态。Z状态是一种已经死亡的状态,僵尸,什么是僵尸?
僵尸又称为活死人,是一种半死不活的东西。
就是一个进程死了之后,我们等一等,不要它立马把资源释放,阻止Z立刻进入X状态。
为什么要先进入僵尸状态Z?
进程为什么被创建出来呢?一定是因为要有任务让这个进程执行,当该进程退出的时候,我们怎么知道这个进程把任务给我们完成的如何了呢?当然要了,一般需要将进程的执行结果告知给父进程或OS。
进程为Z僵尸状态,就是为了维护退出信息,可以让父进程或者 OS 读取的,退出信息会写入 test_struct。
通过进程等待来进行读取的:父进程可以通过进程的 中的 exit_state, exit_code, exit_signal 变量查看进程的退出状态。至于 如何读取 和 如何等待 的问题,我们后面会讲。
僵尸进程存在的意义:表示进程退出时是因为什么原因而退出的。
现在我们来模拟一下僵尸进程,很简单:
如果创建子进程,子进程退出了,父进程不退出也不等待子进程(回收),此时子进程退出之后所处的状态就是 僵尸态Z。
程序运行后一直输入查看指令:
或者写一个监控脚本(一秒监视一次,输出##########只是为了好看点):
while :; do ps axj | head -1 && ps axj | grep process | grep -v grep; sleep 1; echo "###############" ; done
(先复制粘贴到左边运行监控脚本,也是Ctrl c退出)
长时间僵尸,会引发什么问题?
如果没有人收尸,该状态会一直维护,该进程的相关资源 (task_struct) 不会被释放。内存就会泄露,一般必须要求父进程进行回收,如何回收的问题我们会在进程控制章节讲解。
僵尸进程的危害:
进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了。可父进程如果一直不读取,那子进程就一直处于Z状态?是的。
维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话说,Z状态一直不退出,PCB一直都要维护?是的。
那一个父进程创建了很多子进程,就是不回收,是不是就会造成内存资源的浪费?是的。因为数据结构对象本身就要占用内存,想想C/C++中定义一个结构体变量(对象),是要在内存的某个位置进行开辟空间。
没人收那我 kill 掉它可以吗?不行,他本来就死掉了。。。
至此,值得关注的进程状态全部讲解完成,下面来认识另一种进程
2.7 孤儿进程
孤儿进程顾名思义,就是父进程先退出了,子进程还在的情况。
我们来模拟一下孤儿进程的情况,模拟让子进程一直不退出,父进程倒计时很快退出即可:
还是用我们刚才写的监控脚本,监控一下:
while :; do ps axj | head -1 && ps axj | grep process | grep -v grep; sleep 1; echo "###############" ; done
父进程退出,为什么父进程没有变成僵尸?我们怎么没有看到父进程为Z?
那是因为父进程的父进程是-bash,它会自动回收它的子进程,也就是这里的父进程。这里之所以没有看到父进程变成僵尸,是因为被 -bash回收了,Z到X的状态很快,所以你没看到。
那为什么刚才代码中的父进程创建的子进程,父进程没有回收子进程呢?那是因为代码压根就没有写回收,所以你的子进程就没有回收。
说回孤儿进程,既然子进程需要父进程回收,如果父进程先提前退出了,那孤儿进程如何处理?细心的同学已经发现了,上图中父进程退出后,子进程 PPID 变为了 1,其实就是被 "领养" 了。
也就是说,如果父进程提前退出,子进程还在运行,子进程会被 1 号进程领养。
1 号进程其实就是操作系统。我们把被 1 号进程领养的进程,称之为 孤儿进程 。
细心的同学还发现了准备 Ctrl + c 也没能终止孤儿进程。前面我们说了,STAT 后有加号的属于前台进程,只要能被 Ctrl + c 干掉的都是前台进程。
而我们的孤儿进程是没有加号,所以是后台进程。后台进程其实还是在运行,只是会影响命令行输入,既然 Ctrl + c 都无法奈其何,kill 试试:
我们刚才说的是僵尸进程 kill 杀不死,但是这是孤儿进程,孤儿还是人,所以能被kill。。。
至此,所有的 RSDTZX全部讲解完毕
- 运行态:R
- 终止态:Z,X
- 阻塞态:S,D
- 挂起态:S,D,T
以上就是操作系统原理和具体操作系统实现之间的查别。
3. 进程优先级
3.1 优先级的概念
什么是优先级?
先想想:权限 是什么?权限的本质是谈论 "能" 还是 "不能" 的问题。
那什么是 优先级 ?优先级是进程获取资源的先后顺序。
优先权高的进程有优先执行权利。配置进程优先权对多任务环境的 Linux 很有用,可以改善系统性能,还可以把进程运行到指定的 CPU 上,这样一来就可以把不重要的进程安排到某个 CPU,可以大大改善系统整体性能。
为什么会存在优先级?
先思考下我们日常生活中排队的本质,排队的本质可以说是 "确定优先级" ,而插队行为就是更改优先级。因为排队造就了优先级,那我们为什么要排队?
我们之所以要排队,其实最主要的原因是因为资源不够。200 名学生要去食堂吃饭,但窗口就 20 个,当然需要排队。如果窗口有 200 个,理论上不用排。
因为系统里面永远都是进程占大多数,资源是少数。 这就导致了进程竞争资源是常态。排队和进程资源竞争都是一定要确认先后的,它们的本质都是 确认优先级 。
本章我们要讲的是 Linux 的进程优先级,Linux 下的优先级有很多方式,包括设置和修改。我们不建议修改优先级,如果你不懂调度器的调度算法,你随便修改优先级其实就是变相地 "插队" 了。
你可以让你的进程尽快地得到了某种 CPU 资源或其它资源,凡是可能会打破调度器的平衡。其实你在用户层再怎么设置,也不会对调度器的调度策略产生什么影响。
3.2 查看的进程:ps -al
查看系统进程:在 Linux 或者 Unix 系统中,输入 ps -l 命令则会输出内容:
把前面写的代码放出来:
运行起来,然后左边输入 ps -l 查看:
没反应是因为 ps -l 只能显示当前终端下进程的相关信息,然后输入了 ps -al就看到了当前进程。此时我们的进程 process 就显示出来了,我们重点关注 PRI和NI,上面是80和0
Linux 中的进程优先级由两部分组成:PRI和NI
PRI:优先级 (priority),默认进程优先级为80。
NI:nice 值 (nice value) ,进程优先级的修正属性,取值区间为[-20,19],默认值为 0。Linux下 数字越小,表示优先级越高;数字越大,优先级越低。
优先级的部分我们在 task_struct 中也是可以找到的。它的优先级和我们上一章讲的进程状态一样,也是个整数,在 task_struct 中表示:
3.2 进程优先级的修改
要更该进程的优先级,需要更改的是NI,而非PRI。
因为 nice 值是进程优先级的修正数据,所以一个进程不管是在启动前还是在运行中,想要修改优先级,都是通过修改它的 nice 值来达到目的。
其实,我们系统中是存在nice命令的,对应的还有renice 。它们可以让我们在启动一个进程时直接指定优先级,或者启动中或启动前设置优先级。
感兴趣可以自行查阅,我们还是主要学习如何使用 top 命令去修改:
进入top后按“r”–>输入进程PID–>输入nice值
输入top 后我们键入 r,此时会发出询问,再输入要修改的进程的PID:
renice就是设置nice值,这里给-20看看:然后修改失败了:Permission denied (没有权限) ?前面说到:"一个进程的优先级不能轻易被修改,因为会打破调度器的平衡"。我们切换到在左边切换root用户重复上面的操作看看:
成功修改
值得强调的是,Linux 不允许用户无节制地设置优先级,设置的优先级范围不能逾过下列区间: [-20,19],超过了也只能给你按最外的范围设置。
PRI值越小越快被执行。
Linux 的优先级是这样设置的:prio = prio_old + nice;(优先级 = 老的优先级 + nice值)
每次设置优先级,这个 old 优先级都会被恢复成为 80 (跟上一次没关系)我们这里重复上面步骤,给进程25850的nice设置成100试试:
最高还是99:
无论什么时候nice设置成10的话,PRI就是80 + 10 = 90,和前面无关。
3.3 进程的切换
竞争性:系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级。
独立性:多进程运行,需要独享各种资源,多进程运行期间互不干扰
并行:多个进程在多个CPU下分别,同时进行运行,这称之为并行
并发:多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为 并发
竞争性前面的优先级就是。
进程运行具有独立性,不会因为一个进程挂掉或者异常而导致其它进程出现问题。
那么操作系统是如何做到进程具有独立性的呢?(在后续讲解进程地址空间时揭晓)
下面来理解一下并行与并发:
一般服务器都是双 CPU 的,所以双 CPU 的系统是存在的,就会存在多个进程同时在跑的情况。如果存在多个 CPU 的情况,任何一个时刻,都有可能有两个进程在同时被运行:并行 。
但我们大家接触的、用的笔记本电脑基本都是单核的,单 CPU 的任何时刻只允许一个进程运行。我的电脑是单 CPU 的,但是我的电脑中有各种进程都可以在跑啊?
不要认为进程一旦占有 CPU,就会一直执行到结束,才会释放 CPU 资源。所以一直让它跑,直到进程执行完,是不存在的,我们遇到的大部分操作系统都是 分时 的。
操作系统会给每一个进程,在一次调度周期中,赋予一个 时间片 的概念。例:一秒钟之内每一个进程至少要被调度20次,每一次调度就是自己代码得以推进的时候。
在一个时间段内,多个进程都会通过 "切换交叉" 的方式,当多个进程的代码,在一段时间内都得到推进:并发。
还有一个拓展:
进程抢占(了解)
OS 就是简单的根据队列来进行前后调度的吗?有没有可能突然来了一个优先级更高的进程?
抢占式内核:我们现在的计算机基本都是支持 抢占 的。正在运行的低优先级进程,可能正在享受着它的时间片、推进着代码,但是如果来了优先级更高的进程,我们的调度器会直接把对应的进程从 CPU 上剥离,放上优先级更高的进程,这个操作就叫做 进程抢占。
- 不允许不同优先级的进程存在
- 相同优先级的进程,是可能存在多个的
根据不同的优先级,将特定的进程放入不同的队列中。这其实就是一张简单的哈希表,后面列入的都是队列,其原理是通过哈希根据不同的哈希值确定队列的优先级,每一种优先级 Linux 都会维护一个队列。
举个最简单的例子,下面的 z 是如何得到已经释放的临时变量 a 的数据的?
int func()
{
int a = 10 + 20;
return a;
}
int main(){
int z = func();
return 0;
}
寄存器功不可没,拷贝一份到寄存器里去,然后再mov给 z 变量。
CPU 内的寄存器是:可以临时地存储数据
寄存器分为 可见寄存器 和 不可见寄存器 。当进程在被执行的过程中,一定会存在大量的临时数据,会暂存在 CPU 内的寄存器中。
寄存器上数据的重要性:
我们把进程在运行中产生的各种寄存器数据,我们叫进程的硬件上下文数据。
当进程被剥离:需要保存上下文数据,上下文在task_struct保存 。
当进程恢复时:需要将曾经保存的上下文数据恢复到寄存器中。
注意事项:要区分,"寄存器" 和 "寄存器里的数据" 的区别。
寄存器只有一套,但是寄存器里的数据有多份。
本篇完。
下一篇:零基础Linux_9(进程)环境变量+内存地址空间。
穿越回来复习顺便贴个下篇链接:零基础Linux_9(进程)环境变量+进程地址空间+进程创建fork-CSDN博客