进程知识点

目录

1、进程是什么?

2、PCB(进程管理块)

2.1、简介

2.2、PCB类的成员(或者说PCB的属性)

2.3、struct task_struct

3、运行队列

4、进程的状态(status,state)

4.1、理论上的泛型的进程状态

4.1.1、(了解即可)新建状态

4.1.2、(重点)运行状态

4.1.3、(重点)阻塞状态

4.1.4、(重点)挂起状态

4.1.5、(了解即可)挂起阻塞

4.2、具体的进程状态(Linux系统下进程的状态)

4.2.1、S(sleeping):阻塞状态

4.2.2、R(running):运行状态。

4.2.3、T(stopped):停止状态(暂停状态)。

4.2.4、t(stopped):停止状态(暂停状态)。

4.2.5、D(disk sleep):磁盘睡眠状态。

4.2.6、Z(Zombies):僵死状态

4.2.7、X:dead终止状态。

5、前台进程和后台进程

6、孤儿进程

6.1、是什么?

6.2、为什么孤儿进程要被领养?

7、使用电脑时一般有许多软件同时运行,也就是会同时创建很多个进程,操作系统是怎么管理这些进程的呢?

8、查看进程信息的指令

8.1、ps

8.2、top

8.3、ls /proc(process,进程,过程)

8.3.1、上图中的cwd(current working directory)

8.3.2、上图中的exe

9、获取进程ID的方法

9.1、函数pid_t getpid ()

9.2、函数pid_t getppid ()

9.3、指令pidof

10、bash

11、kill -9 xx

12、创建子进程的函数pid_t fork()

12.1、简介

12.2、fork函数的注意事项

12.2.1、为什么调用一次fork函数能返回两次返回值呢?

12.2.2、fork会创建一个新的进程出来,那么父子进程共享代码是共享所有的,还是只共享fork之后的呢?(包括对EIP的解释)

12.2.3、两次返回值的值分别是什么?

12.2.4、fork失败的原因

12.3、fork该怎么使用

12.4、父子进程被创建出来,哪个进程先运行呢?

13、ps-l显示出来的信息分别代表什么?​编辑

13.1、PRI(进程优先级)

13.2、NI

13.2.1、用top命令更改已存在进程的nice

13.2.2、为什么要有优先级?

13.3、UID

13.4、PID

13.5、PPID

14、进程的性质

14.1、竞争性

14.2、独立性

14.3、并行性

14.4、并发性

14.4.1、时间片

15、上下文数据(保存在cpu的寄存器中)

15.1、上下文数据可以被丢弃吗?

15.2、同时运行多个进程本质是反复的切换进程,即进程的并发,那在切换进程时,进程对应的上下文数据是怎么保证不丢失的呢?


1、进程是什么?

进程=程序的代码和数据+内核数据结构(比如进程对应的PCB类的对象。PCB类的对象看下文。)

我们自己启动一个软件,软件被加载到内存中,本质其实就是启动了一个进程。由于Linux中的各种命令本质上就是可执行文件,所以Linux机器中执行一条命令,./XXX,运行的时候,也是在系统层面创建了一个进程。

2、PCB(进程管理块)

2.1、简介

是一个存放进程的管理信息和控制信息的struct结构体(或者说类),包含了进程的所有属性。每一个进程在创建时,都会建立PCB类的对象,伴随进程运行的全过程,直到进程结束才会销毁。由于PCB类的对象中存储了对应进程的所有属性,包括“地址”,所以是可以通过PCB类的对象,找到对应的进程的。

2.2、PCB类的成员(或者说PCB的属性)

PCB一般包括:

1.程序ID(PID(进程ID即process ID)、进程句柄):它是唯一的,一个进程都必须对应一个PID。PID一般是整形数字

2.特征信息:一般分系统进程、用户进程、或者内核进程等

3.进程状态:运行、就绪、阻塞,表示进程现的运行情况

4.优先级:表示获得CPU控制权的优先级大小

5.通信信息:进程之间的通信关系的反映,由于操作系统会提供通信信道

6.现场保护区:保护阻塞的进程用

7.资源需求、分配控制信息

8.进程实体信息,指明程序路径和名称,进程数据在物理内存还是在交换分区(分页)中

9.其他信息:工作单位,工作区,文件信息等 

2.3、struct task_struct

不同的操作系统中PCB的名字不同,Linux中,PCB名为 struct task_struct。

3、运行队列

用于调度进程。操作系统和cpu合作调度一个进程时,本质就是从运行队列中挑选一个PCB类的对象,并且执行这个对象对应的进程的代码。

1.注意这个队列不一定是用数据结构意义上的队列实现的,因为这个队列可以完成许多非法操作,比如插队。在LInux中,运行队列是一个双向循环链表,链表中的每一个节点就是struct task_struct结构体对象即PCB类的对象。

2.运行队列在操作系统中不止一条,系统中分给cpu一条,其他设备如磁盘,网卡也会分配。注意这个队列是在操作系统中,而不是在外设上。

4、进程的状态(status,state)

4.1、理论上的泛型的进程状态

阅读下文首先要明确:

1.cpu是被操作系统控制的,即操作系统下达指令,cpu去执行,如果系统不下达指令,cpu就什么也不做。

2.进程的状态存储在进程对应的PCB结构体中。

4.1.1、(了解即可)新建状态

进程刚被创建出来,即刚创建PCB类的对象,但没有进入运行队列,还不能运行。在Linux中,进程是不存在新建这一状态的,但进程的状态是一个泛型的概念,Linux中的进程没有新建状态不代表其它环境也没有。

4.1.2、(重点)运行状态

task_struct结构体在运行队列中排队就叫运行状态,也就是说虽然此时cpu没有执行一个PCB类对象所对应的进程的代码,但只要这个进程的PCB类的对象在运行队列里排队,那这个PCB对应的进程也是运行状态。当然正在被cpu执行其代码的进程肯定也是运行状态。为什么在排队的时候也叫他运行状态呢?因为cpu轮转的速度太快了,就是说即使一个PCB类的对象此时在排队,这个排队的时间往往是一瞬间就结束了,造成运行队列(本质是双向循环链表)中的每个PBC所对应的进程同时在运行的效果。某个被阻塞的进程被唤醒的含义就是将该进程的PCB重新放回分配给CPU的运行队列中排队,并将该进程的状态从S恢复成R,之后进程就会被CPU继续调度,执行后序代码了。。

4.1.3、(重点)阻塞状态

在操作系统分配给非cpu设备的运行队列中,如键盘设备的运行队列中排队就叫阻塞状态。cpu执行代码是很快的,如scanf(“%d”,&a)是一个函数,函数体中的若干条不需要键盘资源的代码在一瞬间就执行完毕,此时需要键盘设备的资源,所以操作系统将这个PCB类的对象从cpu的运行队列转移到键盘设备的运行队列,此时cpu执行其他PCB对应的进程的代码,而需要键盘资源的进程在等待用户输入,此时这个进程的状态就是阻塞状态,直到重新返回系统分配给cpu的运行队列中才能恢复成运行状态。在生活中,如果某个进程卡死了,一般是因为:1.系统中的资源(其他进程)太多,把分给cpu的运行队列占满了,导致进程长时间得不到cpu调度。2.进程阻塞了,一直在等待某个设备的资源,导致得不到cpu的调度。

关于等待队列的补充知识点:通过后序学习,我们说Linux下一切皆文件,所以键盘等外设也是一个文件,前面说PCB在OS分配给非CPU设备(文件)的运行队列(或者叫等待队列)中等待,就叫做阻塞,这里我要说的是,PCB在等谁,谁就一定要提供一条等待队列,比如未来要说的在等待某个条件变量,那么条件变量就得提供一条等待队列,如果是等待某个文件描述符,更准确的说是如果等待某个文件(该文件可能对应某个外设,也可能不是),那么这个文件也要提供一条等待队列,即在文件对应的内核数据结构struct file中得有这个队列,如果PCB需要等待该文件,就将PCB链接进这条等待队列中。

进一步理解某进程或者线程被阻塞的含义:将某个进程阻塞,或者说挂起,就是将该进程的PCB从CPU的运行队列中移除,然后被谁阻塞就将PCB链接进谁的等待队列中,因为该进程的PCB不在CPU的运行队列,所以该进程也就无法被调度进而执行后序代码了。所以我们说检测到read或者write读写数据不就绪、或者检测到条件变量不就绪时,我们要将该进程或者线程阻塞,其原理就是将进程的PCB从CPU的运行队列中移到其他运行队列,这样该进程就无法被调度,即无法继续执行任何代码了,所以阻塞的意思就是不让你这个进程继续执行代码。

进一步理解某进程或者线程被唤醒的含义:某个被阻塞的进程被唤醒的含义就是将该进程的PCB重新放回分配给CPU的运行队列中排队,并将该进程的状态从S恢复成R,之后进程就会被CPU继续调度,执行后序代码了,所以唤醒的意思就是让你这个进程继续执行代码。那被谁唤醒呢?操作系统检测到条件就绪时会自动唤醒该进程或者线程,将进程放到CPU的运行队列中。操作系统如何检测呢?比如说,如果是因为文件缓冲区数据不就序导致阻塞,那么唤醒时,OS只需要找到该进程的PCB在哪个struct file的等待队列中等,PCB中会有这个struct file的记录,所以通过PCB即可找到该struct file,进而找到struct file中提供的文件缓冲区,然后就可以检测文件缓冲区是否就绪了。如果是因为条件变量不就绪导致阻塞,前面也说过,PCB等谁就绪,谁就要提供一条等待队列,所以条件变量也要提供,条件变量是一个结构体类型,所以结构体中一定有等待队列这个成员,所以唤醒一个被条件变量阻塞的进程时,OS只需要找到该进程的PCB在哪个pthread_cond_t变量的等待队列中等,PCB中会有这个pthread_cond_t条件变量的记录,所以通过PCB即可找到该条件变量,然后就可以检测条件变量是否就绪了。

4.1.4、(重点)挂起状态

当内存不足时,操作系统会适当将某些进程的代码和数据(比如长时间不是运行状态的进程的代码和数据)移出内存并放到磁盘上的swap区域(一定有,但不一定能被我们看到),并只将进程的PCB保存在内存中,这样的进程的状态就叫做挂起状态。所以如果想运行挂起状态的进程,要先将磁盘上这个进程的代码和数据重新加载到内存,然后将挂起状态设置成运行状态。由于挂起状态的进程本质也是长时间处于非运行状态即阻塞状态的进程,所以在具体的某个操作系统中如Linux里,挂起状态就是用S状态(阻塞状态)表示,毕竟挂起状态对比阻塞状态只有一个区别:“ 处于挂起状态的进程,进程的代码和数据不在内存中。” 但进程的状态是存储在进程对应的PCB当中的,和代码以及数据无关。

4.1.5、(了解即可)挂起阻塞

有些教材会有挂起阻塞的概念,简而言之就是既处于阻塞状态,又处于挂起状态。比如一个进程一直处于阻塞状态,此时又满足成为挂起状态的条件,所以操作系统就挂起了这个进程,此时就成了挂起阻塞。

4.2、具体的进程状态(Linux系统下进程的状态)

4.2.1、S(sleeping):阻塞状态

在等待某种资源就绪。是一种可中断的睡眠,即可以直接结束这个S状态的进程。或者可以发送信号将进程唤醒,恢复成R状态,强制继续工作,但之前S状态时等待的其他设备的资源没有拿到。某个被阻塞的进程被唤醒的含义就是将该进程的PCB重新放回分配给CPU的运行队列中排队,并将该进程的状态从S恢复成R,之后进程就会被CPU继续调度,执行后序代码了。

4.2.2、R(running):运行状态。

即进程在分配给cpu的运行队列中正在运行或者排队就叫运行状态。某个被阻塞的进程被唤醒的含义就是将该进程的PCB重新放回分配给CPU的运行队列中排队,并将该进程的状态从S恢复成R,之后进程就会被CPU继续调度,执行后序代码了。

4.2.3、T(stopped):停止状态(暂停状态)

kill -19 加进程PID可以暂停进程,kill -18恢复。对比S的休眠,S状态是因为在等待某种资源,资源就绪后会自动恢复成R,而T需要手动输入指令才能恢复成R。

4.2.4、t(stopped):停止状态(暂停状态)

和上面一样,也是停止状态,只不过是调试下的停止状态,比如进入gdb模式并且打了断点后,此时输入run指令,就可以在进程的状态中观察到 t 状态,如上图。gdb是一个程序,我们用gdb打断点让进程停下来本质就是gdb发送了19号信号让进程停下来。所以现在我们也能对调试的认识更加深刻,调试本质上就是运行可执行程序,让它成为进程,只不过可以发送信号让进程处于停止状态。

4.2.5、D(disk sleep):磁盘睡眠状态

对比S状态,D状态是不可中断睡眠,即不可以被唤醒,其他性质等同于S状态。因为涉及到磁盘的读写数据,考虑到数据的安全,必须等待磁盘读写完毕后,进程的PCB才会从分配给磁盘的运行队列自动回到cpu的运行队列,即恢复成R状态。或者必须等待磁盘读写完毕后,才可以结束该进程。那有没有什么特殊方法强制中断D状态呢?答案:拔电源,正常关机重启并且此时还有进程在读写磁盘时,有可能操作系统认为关机非法,所以关机都关不了,只能通过拔电源的方式关机。 

4.2.6、Z(Zombies):僵死状态

即一个进程已经退出,但是它的PCB还不允许被操作系统释放,处于一个被检测的状态。被谁检测呢?一般是处于僵尸状态进程的父进程,如果父进程结束了,那就是操作系统检测。一个进程为什么要维持僵尸状态呢?为了让父进程或者操作系统回收,因为一个进程被它的父进程创建肯定是为了完成某一工作,工作完成的怎么样也是需要该进程告诉它的父进程的。总的来说:子进程退出,父进程还在运行,如果父进程没有读取子进程状态,子进程进入僵尸状态。变成僵死状态后,进程的代码和数据可以被释放,但PCB不行,所以如果一个进程长时间处于僵尸状态,那会造成内存泄漏的问题。

僵尸进程危害
1.进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了。如果父进程一直不读取,那子进程就一直处于Z状态。也就是说一个进程一旦变成僵尸状态,那就刀枪不入,连杀人不眨眼的kill -9也无能为力,因为进程已经死去了,没办法再次杀死。

2.维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话说,Z状态一直不退出,PCB一直都要维护。

3.那一个父进程创建了很多子进程,就是不回收,是不是就会造成内存资源的浪费?是的!因为数据结构对象本身就要占用内存,想想C中定义一个结构体变量,是要在内存的某个位置进行开辟空间的。

4.2.7、X:dead终止状态。

进程处于X状态意为可以被操作系统回收,即进程可以被结束(被杀死)。 注意一个进程处于X状态不一定立刻就被回收,但也一般在观察进程时看不到X状态,因为X状态具有瞬时性,当一个进程处于X状态,那么操作系统大概率会立刻将他回收,当然小概率可能此时操作系统正在忙着干别的事情,不会立刻回收。虽然我们肉眼观察不到X状态,但X状态是确切存在的。

5、前台进程和后台进程

1.有时会看见进程状态为S+或者R+,这个+表示什么呢?答案:表示这个进程是一个前台进程。

2.前台进程表示占有控制终端的进程,即进程结束前无法在shell界面执行其他指令,其它称为后台进程。Ctrl+C就是用于结束前台进程。

3.切换成后台进程的方法:在启动可执行程序的命令后添加&即可,如 ./program & 。后台进程无法通过Ctrl+C结束进程。

4.后台进程的优点:无需等待上一个进程结束就可以在shell界面运行其他指令(进程)。

6、孤儿进程

6.1、是什么?

某个进程没退出,但它的父进程退出了,此时它就是孤儿进程。孤儿进程会被PID为1的进程领养,即孤儿进程的PPID会变成1。1号进程是init进程,即系统本身。

6.2、为什么孤儿进程要被领养?

因为孤儿进程得被回收。

7、使用电脑时一般有许多软件同时运行,也就是会同时创建很多个进程,操作系统是怎么管理这些进程的呢?

 如上图中:

管理时遵循一个理念:先描述,再组织,就是说想要管理某事件,必须先知道事件的信息,然后通过信息去组织该怎么做,而上图中的PCB就是这里所说的信息的集合。

操作系统是不直接管理各个进程的,而是管理各个进程创建时建立的PCB类的对象组合而成的链表,即对链表的增删查改间接管理各个进程。注意由于PCB类的对象中存储了对应进程的所有属性,包括“地址”,所以是可以通过PCB类的对象,找到对应的进程的。

8、查看进程信息的指令

每个进程都会有一个目录(路径)存储进程的相关信息,所以进入目录就可以查看进程的所有信息。

注意下面指令都是查看进程信息而不是查看可执行程序的信息的,如果一个可执行程序不是运行状态,即没有生成一个进程,那么查看不到进程的信息。如果一个进程在一瞬间就执行完毕,并且自动结束进程,那一般也是查不到这个进程存在过的,因为太快了,但它实际上确实是存在过的。

8.1、ps

用于查看当前有哪些进程,只显示瞬间的状态,并不动态连续,如果想对进程实时监控,应该用 top 工具。

-a

显示所有用户的所有进程(包括其它用户)。

-l

以长格式输出。可以查看进程更多的信息,如优先级PRI和nice值NI等。

-x

显示无控制终端的进程。

-j

用任务格式来显示进程。

8.2、top

可以理解成Windows下的任务管理器,可以进行实时监控。

8.3、ls /proc(process,进程,过程)

用于查看进程信息。比如ls /proc/30650 -al就可以查看PID为30650的进程的所有信息,如下图,下图中只截取了信息的一部分。

8.3.1、上图中的cwd(current working directory)

1.是真正意义上的当前路径,一个文件在启动变成进程时会记录启动时所在的路径,即当前工作路径。

2.指的是正在运行的PID为30650的进程运行时所在的目录(路径)。如上图中,cwd路径对比exe路径只少了最后的可执行程序pro。

8.3.2、上图中的exe

指的是正在执行的PID为30650的进程对应的可执行程序所存储的路径(目录)。注意exe不是可执行程序的名字或者后缀名,上图中的可执行程序名字为 pro

9、获取进程ID的方法

9.1、函数pid_t getpid ()

 如上图:getpid是一个函数,不需要参数,返回值pid_t本质是一个无符号整形size_t。用于查看进程的PID。用getpid函数需要包头文件<sys/types.h>。

9.2、函数pid_t getppid ()

获取的是父进程的PID,其他性质和上面函数一样。

9.3、指令pidof

通过pidof+进程的名称即可获取进程PID,如下图。

10、bash

1.-bash就是shell程序启动时产生的进程,如果kill掉bash这个进程,那么shell程序就崩溃了。

2.每一次登录都会产生一个不同的bash,即使是几个人使用同一个用户的账号,干掉相对更早登录的用户的bash,对相对更晚登录的用户的bash没有影响。

3.如上图:不管是Linux中可以直接执行的命令(本质也是可执行文件),还是用户编写最后生成的可执行文件,运行这些可执行文件时,生成的进程都是-bash的子进程。

11、kill -9 xx

xx代表进程的PID,用于结束PID为xx的进程,和Window上用任务管理器结束某进程对应。

12、创建子进程的函数pid_t fork()

12.1、简介

1.是一个函数,用于创建一个子进程,函数的声明在头文件<unistd>中,返回值是pid_t类型,本质就是无符号整形size_t,函数不需要参数。

2.fork之后会将当前执行流乘以2,比如当前只有一个执行流(父进程),那么fork之后就会有两个执行流(父进程和父进程对应的子进程)。fork之后代码是父子共享即父子都可见的,每一个执行流都会执行一次代码,也就是说有几个执行流,同一句代码就得被反复执行几次。

12.2、fork函数的注意事项

12.2.1、为什么调用一次fork函数能返回两次返回值呢?

本质是因为fork函数内部在return值之前,函数的逻辑就已经执行完毕了,即子进程已经建立了,所以return这个语句得被父子进程各自执行一次,造成了返回两次返回值的效果。

12.2.2、fork会创建一个新的进程出来,那么父子进程共享代码是共享所有的,还是只共享fork之后的呢?(包括对EIP的解释)

答案:所有的代码都共享,但是新创建的进程,即子进程只会执行fork之后的代码。

原因:因为进程随时可能破中断(可能并没有执行完)下次回来,还必须从之前的位置继续运行(不是最开始),就要要求CPU必须随时记录当前进程执行的位置,所以,CPU内有对应的寄存器记录当前进程的执行位置,还有其他寄存器记录进程的上下文数据。CPU中是有一套寄存器的,其中记录进程执行到哪的寄存器名为EIP。而创建子进程的时候,需要将记录在寄存器中的父进程的上下文数据分享给子进程,包括EIP中的数据,所以即使父子进程各自调度的时候,各自都会修改EIP,但子进程认为自己应该从当前EIP的值开始执行,即从fork之后的代码开始执行。

12.2.3、两次返回值的值分别是什么?

1.失败时给父进程返回-1。

2.成功时:给父进程返回子进程的PID,给子进程返回0。为什么要这么做呢?感性的认识是:一个父进程可能创建了多个子进程,所以fork之后,要将子进程的PID告知父进程方便管理,而子进程都只有一个父进程,所以无需将父进程的PID告知子进程。

12.2.4、fork失败的原因

1.系统中有太多的进程。
2.实际用户的进程数超过了限制,普通用户能够创建的进程是有限的。

12.3、fork该怎么使用

读了上文应该可以发现使用若干次fork创建子进程可以让一个代码重复执行若干次,而for这种循环就已经可以完成重复执行代码的功能了,为什么要用fork呢?所以fork肯定不是用于简单的重复执行代码这一功能的。

因为fork函数有返回值,并且返回给每一个执行流(进程)的值不同,所以就可以用返回值做文章,用一个变量接收返回值,会造成同一个变量同时存在几个值的效果,但实际上不是同一个变量,毕竟就8个字节的空间是不可以存放两个size_t类型的值的,(详细原因请看进程地址空间那篇文章)然后根据变量的值来执行不同的代码块,如下图,用 if else 实现许多分支。所以也就有了 if 分支和else 分支都会被执行,这在我们学习进程之前显然是不合常理的,会认为进入 if 的代码块后就不会再进入 else 的代码块。但学习进程之后,就能够理解这是多个执行流执行各自的代码块产生的效果。

12.4、父子进程被创建出来,哪个进程先运行呢?

答案是都有可能,因为谁先运行是由操作系统的调度器决定的。比如上图的child process和parent process都有可能先执行。

13、ps-l显示出来的信息分别代表什么?

13.1、PRI(进程优先级)

代表这个进程可被执行的优先级,用一个数字表示,其值越小越早被执行。PRI(new) = PRI(old) + nice。每次利用NI值调整PRI的时候,PRI(old)都是80,所以前面的公式可以认为是PRI(new) = 80 + nice.

13.2、NI

代表这个进程的nice值。其表示进程可被执行的优先级的修正数值。PRI值越小越快被执行,那么加入nice值后,将会使得PRI变为:PRI(new) = PRI(old) + nice,这样,当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行,所以,调整进程优先级,在Linux下,就是调整进程nice值。nice其取值范围是 - 20至19,一共40个级别。需要强调一点的是,进程的nice值不是进程的优先级,他们不是一个概念,但是进程nice值会影响到进程的优先级变化。

13.2.1、用top命令更改已存在进程的nice

进入top后按“r”,然后输入进程PID,最后输入nice值。如果不是超级管理员,设置正的nice值可以通过,但是负的nice值会失败。

13.2.2、为什么要有优先级?

权限是能不能的问题,而优先级是谁先谁后的问题。需要优先级本质是因为cpu是有限的,但进程太多,需要某种方式竞争cpu资源。

13.3、UID

代表执行者的身份。

13.4、PID

代表这个进程的代号。

13.5、PPID

代表这个进程是由哪个进程发展衍生而来的,即父进程的代号。

14、进程的性质

14.1、竞争性

系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高
效完成任务,更合理竞争相关资源,便具有了优先级。

14.2、独立性

多进程运行,需要独享各种资源,多进程运行期间互不干扰。

14.3、并行性

多个进程在多个CPU下分别,同时进行运行,这称之为并行。

14.4、并发性

多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发。

14.4.1、时间片

是分时操作系统分配给每个正在运行的进程微观上的一段CPU时间。现代操作系统(   Windows、Linux、Mac OS X等)允许同时运行多个进程 —— 例如,你可以在打开音乐播放器听音乐的同时用浏览器浏览网页并下载文件。事实上,虽然一台计算机通常可能有多个CPU,但是同一个CPU永远不可能真正地同时运行多个任务。在只考虑一个CPU的情况下,这些进程“看起来像”同时运行的,实则是轮番穿插地运行,由于时间片通常很短(在Linux上为5ms-800ms),用户不会感觉到。

时间片由操作系统内核的调度程序分配给每个进程。首先,内核会给每个进程分配相等的初始时间片,然后每个进程轮番地执行相应的时间,当所有进程都处于时间片耗尽的状态时,内核会重新为每个进程计算并分配时间片,如此往复。
进程会抢占cpu资源或者让出cpu资源,比如进程1此时正在被cpu执行,但假如此时有优先级更高的进程2,进程2就可以抢占cpu资源,即使进程1的时间片还没有结束。

15、上下文数据(保存在cpu的寄存器中)

cpu中有许多的寄存器,之前常说cpu需要从内存中读取进程的代码和数据,本质上就是将进程的代码和数据拷贝到cpu的寄存器里临时保存。这些寄存器里的临时数据就是进程的上下文数据。

15.1、上下文数据可以被丢弃吗?

绝对不可以,如果被丢弃了,这个进程也就废掉了,cpu会不知道该怎么执行,因为上下文数据记录了许多重要信息如该进程执行到了哪一步等。

15.2、同时运行多个进程本质是反复的切换进程,即进程的并发,那在切换进程时,进程对应的上下文数据是怎么保证不丢失的呢?

每次切换进程时,比如进程A暂时被切下来的时候,需要进程A顺便带走自己的上下文数据。带走暂时保存的目的就是为了下次回来的时候,能恢复上去,就能继续按照之前的逻辑继续向后运行,就如同进程没有被切换过一样。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值