linux进程概念

目录 

1、进程的基本概念

2、进程控制块 - PCB

task_struct内容分类

3、查看进程

通过ps命令查看进程

通过proc查看进程

4、通过系统调用获取进程标示符

5、通过系统调用创建进程-fork初识

6、进程状态

操作系统进程状态

linux进程状态

僵尸进程

孤儿进程

僵尸进程的危害

进程状态总结

7、进程优先级

基本概念

查看系统进程 

PRI and NI

通过top命令修改进程优先级(ni)

8、linux中的四个重要概念


1、进程的基本概念

课本概念:程序的一个执行实例,正在执行的程序等。

内核观点:担当分配系统资源(CPU时间,内存)的实体。

  • 假设我们在磁盘上写了一个test.c的文件代码,最终经过编译链接形成test.exe可执行程序。这个可执行程序也是一个文件,当前在磁盘当中,根据冯诺依曼体系,如果我们想要运行这个可执行程序,我们就需要将其从外设(磁盘)加载到内存中,然后CPU再和内存交互。而加载的过程就叫做此程序运行起来了,但是我们并不能说其为进程,因为加载到内存中只不过是将代码和数据加载进去,此程序只是被搬到内存中,它依旧是一个程序。

linux进程概念_第1张图片

 操作系统里面可能同时存在大量的进程,所以操作系统要把所有的进程管理起来,对进程的管理本质就是对进程数据的管理。这里就又用到了结构体和数据结构。

⭐:严格意义上的进程=可执行程序+该进程对应的内核数据结构

 2、进程控制块 - PCB

操作系统里面存在大量的数据,操作系统需要把所有的进程管理起来,而对进程的管理,本质就是对进程数据的管理,依旧是先描述,再组织。

先描述:

  • 当一个程序加载到内存时,除了加载了代码和相关数据,操作系统还要为了管理该进程创建对应的数据结构,而在linux操作系统中描述进程是用一个叫task_struct的结构体,进程的所有属性数据全部放入此结构体中。当我们有多个进程的时候,相对应的操作系统也就创建多个task_struct结构体。

再组织:

  • 这里我们将所有的task_struct用双链表进行链接起来,此时对进程的管理也就变成了对内核数据结构的管理。

在操作系统中我们把描述进程的结构体叫做PCB(进程控制块),而linux操作系统下的进程控制块叫做struct task_struct,以此来描述进程结构体

linux进程概念_第2张图片

task_struct内容分类

task_struct主要包含以下内容:

  • 标示符:描述本进程的唯一标示符,用来区别其他进程。
  • 状态:任务状态,退出代码,退出信号等。
  • 优先级:相对于其他进程的优先级。
  • 程序计数器:程序中即将被执行的下一条指令的地址。
  • 内存指针:包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
  • 上下文数据:进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。
  • I/ O状态信息:包括显示的I/O请求,分配给进程的I/ O设备和被进程使用的文件列表。
  • 记账信息:可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
  • 其他信息..............

 3、查看进程

我们现在创建一个可执行程序mytest,它现在位于磁盘上,现在我们将程序运行起来,此时程序就变成了一个进程。

linux进程概念_第3张图片

我们该如何查看现在的进程呢,主要有以下两种方法:

  • 通过ps命令查看进程
  • 通过proc查看 

通过ps命令查看进程

ps ajx | grep 'mytest'

ps ajx命令会把你系统中所有的进程显示出来,而随后的命令其目的是只把名为mytest的进程显示出来:

linux进程概念_第4张图片

此刻我们发现我们要的是mytest进程,但为什么又出现了grep的进程呢?

  • 我们自己写的代码,编译成为可执行程序,启动之后就是一个进程,同样别人写的代码,启动之后也是一个进程。例如先前学到的ls、pwd、touch、grep、chgrp……指令,这些指令就是别人写的,存在目录/usr/bin/里头, 这些指令在执行后也会成为进程,这也就是为什么上面会把grep显示出来。

那么我们如何只显示mytest的进程呢?

输入下面指令:

ps ajx | grep 'mytest' | grep -v grep

此命令就是把带有grep字符的进程屏蔽掉,只展现出来mytest进程。

通过proc查看进程

我们都知道在根目录下有许多的路径:

我们要注意上面的proc目录,它是一个内存文件系统,里面存放的是当前系统实时的进程信息。下面我们进入此目录里面看看:

linux进程概念_第5张图片 每一个进程在系统中都会存在一个唯一的标识符,就如同每个学生都有一个自己的专属学号一样,而这个唯一标识符在linux中称为pid(process id),而上面框框所圈住的蓝色数字即为进程的pid。 

我们可以查看进程的所有title列名称,我们输入下面指令:

ps ajx | head -1

这一行显示出来的就是所有的属性名,提取好了属性,我们就可以创建mytest的进程,然后显示mytest进程的具体信息。

ps ajx | head -1 && ps ajx | grep 'mytest' | grep -v grep

我们知道proc存放的是当前系统实时的进程信息,现在我们已经知道了进程mytest的pid(13261),下面我们就在proc目录里面查询一下:

linux进程概念_第6张图片

我们知道proc里面存放的是进程的实时信息,那如果我们结束进程mytest,那么我们就不能在proc里面查询到mytest了:

而当我们再次运行起mytest程序时,结果如下:

此时我们会发现mytest先前的pid失效了,由此我们知道,当一个相同的程序重新启动那么其就是一个新的进程,会给该进程重新分配一个pid。 

下面我们具体来看一下进程的两个属性:

ls/proc/27120 -al

linux进程概念_第7张图片

  • cwd表示进程当前的工作路径。
  • exe表示进程对应的可执行程序的磁盘文件。

如果我们用fopen创建一个文件,那么此文件默认就在当前路径,而当前路径指的就是当前进程所在的路径,进程会自己维护当前路径

⭐:如pid,当前路径等这些都是进程的内部属性,都在进程的进程控制块PCB(task_struct)结构体中。

4、通过系统调用获取进程标示符

通过系统调用获取pid需要用到系统调用函数getpid和getppid来分别获取进程pid和父进程ppid。

下面我们来看一下系统调用函数:

man 2 getpid

linux进程概念_第8张图片

 linux进程概念_第9张图片

下面我们运行一下该程序:

linux进程概念_第10张图片

我们可以看到mytest进程的pid为32321,下面我们通过ps命令来查看该进程的相关信息,验证一下结论是否正确。

那么我们如何杀掉进程呢??? 

  • 按下ctrl + c
  • 使用命令:kill -9 pid

下面我们来看一下进程的ppid:

linux进程概念_第11张图片

我们可以看到当我们每重启一个进程的时候,其pid是会不断的发生变化的,但其父进程ppid不会发生变化。下面我们来看一下其父进程究竟是什么:

 从图中我们可以看到父进程10026就是一个bash,几乎我们在命令行上所执行的所有指令,都是bash进程的子进程! 

5、通过系统调用创建进程-fork初识

我们知道,我们创建进程是用 ./可执行程序,那么有没有一些函数调用或者系统调用能让我创建子进程呢?下面我们来认识一下创建子进程的系统调用接口fork:

man 2 fork

linux进程概念_第12张图片

下面我们来看一下它的返回值:

linux进程概念_第13张图片 我们可以看到fork有两个返回值,如果子进程创建成功,那么就把子进程的pid返回给父进程,把0返回给子进程,如果创建失败,把-1返回给父进程。

下面我们用测试代码来证明一下:

linux进程概念_第14张图片

上面是我们的测试代码,我们知道如果按照常规思路,上面的printf应该只执行一次,下面我们看一下测试结果:

我们看到printf执行了两次,这完全不符合C语言的逻辑!!!!

下面我们再来看一下更奇怪的现象:

测试代码:

linux进程概念_第15张图片

测试结果:

linux进程概念_第16张图片

我们可以看到同一个id值,使用打印,没有修改,却打印出来了不同的值!!!

下面我们来验证一下是否创建了子进程:

linux进程概念_第17张图片

我们知道这段代码会分别按照父进程和子进程的代码逻辑执行两次,下面我们来看一下执行结果:

linux进程概念_第18张图片

我们可以看到执行结果和我们预期的一样。

结论:

  • fork之后,父进程和子进程会共享代码,一般都会执行后续的代码—解释printf为什么会打印两次的问题
  • fork之后,父进程和子进程返回值不同,可以通过不同的返回值,判断,让父子执行不同的代码块。

⭐:两个重要问题:

1.fork为什么给父进程返回子进程的pid,给子进程返回0?

  • 一个父进程可能有多个子进程,但一个子进程只能有一个父进程。父进程必须有标识子进程的方案,从而控制子进程。而这个方案就是给父进程返回子进程的pid。
  • 子进程最重要的是要知道自己创建成功了,同时子进程找父进程的成本非常的低,只需要getppid即可,所以我们只需要给子进程返回0即可。

2.为什么fork会返回两次?

在我们fork以后,os使系统多了一个进程。

  • 父进程:task_struct+进程代码和数据
  • 子进程:task_struct+进程代码和数据
  • 子进程的task_struct对象内部的数据基本上是从父进程继承下来的。
  • 子进程和父进程执行相同的代码,fork之后,父子进程代码共享,而数据要各自独立。也正是数据的不同使其有不同的返回值从而执行不同的代码。

linux进程概念_第19张图片

linux进程概念_第20张图片当我们调用一个函数,当这个函数准备return的时候,这个函数的核心功能已经完成,子进程被创建,并将子进程放入运行队列, 此时父子进程都存在且代码共享,那么return就顺理成章运行两次,自然也就有了两个返回值。

6、进程状态

进程状态本质上是一个整数,在进程的task_struct中(int status)

  • 进程状态反映进程执行过程的变化。这些状态随着进程的执行和外界条件的变化而转换。在三态模型中,进程状态分为三个基本状态,即运行态,就绪态,阻塞态。在五态模型中,进程分为新建态、终止态,运行态,就绪态,阻塞态。

下面我们将以面到点,先从操作系统层面讲进程状态,最后细化到linux层面。

操作系统进程状态

linux进程概念_第21张图片

对于操作系统,我们分运行、终止、阻塞、挂起这四个状态来进行讲解,当我们对这四个状态了解过后,就会对上图有清晰的认知。

1.进程运行:

  • 进程只要在运行队列中就叫做运行态,运行态代表进程已经准备好,随时都可以被调度。

linux进程概念_第22张图片

2.进程终止:

  • 进程终止态指的是进程还在,只不过永远不运行了,随时等待被释放!

问:进程都已经终止了,为什么操作系统不立马释放对应的资源,而要去维护一个终止态?

  • 释放也要花费时间,操作系统是一个“大忙人”,等操作系统闲暇的时候它就会把进程给释放了。

3.进程阻塞:

  • 一个进程在使用资源的时候可不仅仅是在申请CPU资源,进程可能申请更多的其他资源,如:磁盘、网卡、显卡、显示器资源、声卡/音响等等。
  • 如果我们申请CPU资源暂时无法得到满足,我们进程是需要在CPU的运行队列中进行排队的,那么同样的当我们申请其他慢设备的资源也是需要排队的!!!(task_struct在进程排队)。
  • 当进程访问某些资源(磁盘、网卡等),该资源如果暂时没有准备好或者正在给其他进程提供服务,此时:

             1. 当前进程要从runqueue中移除

             2. 将当前进程放入对应设备的描述结构体中的等待队列中。

           (操作系统对进程的管理任务)

结论:进程等待某种资源(非CPU),资源没有就绪的时候,进程需要在该资源的等待队列                  中进行排队,此时进程的代码并没有运行,进程所处的状态叫做阻塞。

linux进程概念_第23张图片

4.进程挂起:

  • 挂起和阻塞有点类似,挂起最终也是卡住了,不过挂起和阻塞在操作系统的定义上还是有点区别。进程挂起就不会申请CPU资源,也就意味着该进程此时处于一个非运行状态。

linux进程概念_第24张图片 上面面临了一个问题:如果进程过多而导致内存不足怎么办??

  • 此时操作系统就要帮我们进行辗转腾挪,将那些短期内不会被调度(你等的资源,短期内不会就绪)进程,它的代码和数据依旧在内存中!就是白白的浪费空间!此时os就会把该进程的代码和数据置换到磁盘上!如下图所示:

linux进程概念_第25张图片

  • 结论:当我们把短期内不会被调度的进程的代码和数据置换到磁盘中后,释放掉这块空间,此时内存就多出来了这块空间的容量,可以短期内让其它进程使用。操作系统通过此种方法让进程只在内存中留下PCB,剩下的代码和数据全部置换到磁盘上面(swap分区),这样的进程就叫做进程挂起。(往往内存不足的时候,伴随着磁盘被高频率访问

linux进程状态

linux进程概念_第26张图片

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 */
};

1.运行状态-R

  • 一个进程处于运行状态(running),表明这个进程要么是在运行中,要么是在运行队列里。

示例:

linux进程概念_第27张图片

此程序编译运行以后进入死循环,此程序也没有访问其他外设资源,此进程一直在cpu的队列里面,下面我们来看一下进程的状态:

我们看到当我们的代码不访问外设,只访问cpu资源,那么就是R状态。

2.浅度睡眠状态-S

示例:

linux进程概念_第28张图片

我们运行此代码,然后查看进程状态:

 我们运行了此进程以后,为什么显示出来的是S睡眠状态呢?

  • 我们这里执行了printf语句,打印信息我们需要访问外设(显示器),但是想要访问外设的进程很多,操作系统会把你的进程PCB放到显示器的等待队列里面,当轮到你的时候操作系统会把你唤醒 ,此时我们就可以打印了。这就是S睡眠状态,对应的就是上文操作系统层面上的阻塞状态

⭐:S睡眠状态又叫做浅度睡眠可中断睡眠

  • 浅度睡眠:当进程处于S状态,它可以随时被操作系统唤醒。
  • 可中断睡眠:当进程处于S状态,它可以随时被kill掉。

 3.深度睡眠状态-D

  • D状态也是一种阻塞状态,只不过这种阻塞状态与传统阻塞状态不同。在linux中,当我们等待的是磁盘资源,那么我们进程阻塞所处的状态就为D状态。

实例分析:

  • 假如我们内存中有一个500MB大小的进程,我们需要将进程的代码和数据写到磁盘上,磁盘在写入这500MB的过程中,进程在内存中默默的等待磁盘完工,此时进程所处的状态就为S状态,进程不会被运行,等待磁盘资源就绪给其反馈结果。但是在进程等待的过程中,如果内存中的进程越来越多,操作系统也就越来越忙,S状态进程只占内存不干事,操作系统就会将它kill掉(一般服务器压力过大,OS会终止用户的进程)。这时候当磁盘写完数据(不管写入成功还是失败)呼叫进程的时候发现进程已经不在了。

linux进程概念_第29张图片

无论磁盘读取成功与否,此数据都失效了,因为对应的进程被OS杀掉了,那么谁来背这个锅 ?

  • 操作系统:当内存不够,OS有权去杀掉进程,这是属于OS的权利。
  • 进程:进程将数据给磁盘是进程应该做的,进程需要等待磁盘的反馈结果。
  • 磁盘:磁盘也只是做了它该做的,将数据写到磁盘里面。

综上:OS、进程、磁盘这三者都没有错误,所以设计操作系统的人为了使这个进程不被操作系统kill掉,于是就设计出了D状态,也就是深度睡眠状态,也叫做不可被中断睡眠。此时进程在等待磁盘的过程中就由浅度睡眠S转变为深度睡眠D,就不会被OS杀掉,我们想要解决掉D状态,就需要我们关机重启或者拔电源解决。

 4.死亡状态-X

  • 死亡状态是一个返回状态,当一个进程的退出信息被读取后,该进程所申请的资源就会立即被释放,该进程也就不存在了,你不会在任务列表里看到这个状态。

 5.僵尸状态-Z

  • 假设张三和李四一起外出跑步,跑着跑着李四突然倒了,张三赶紧打110和120,医生判定李四已无生命迹象,此时医生任务已经完成,但警察还要调查李四的死因,是他杀还是自杀。在警察调查李四的死因过程中,李四所处的状态就为僵尸状态

⭐:当linux中的一个进程退出的时候,一般不会直接进入X状态(资源可以立马被回收), 而是进入Z状态。

为什么进程要进入Z状态呢??

  • 进程被创建出来,一定是因为有任务让这个进程执行,当进程退出的时候,一般需要把进程的执行结果告知给父进程或者OS以此让我们得知任务的完成结果。
  • 进程进入Z状态,就是为了维护退出信息,可以让父进程或os读取(通过进程等待来进行读取)。最后才能进入X状态。

6.暂停状态 - T/t

  • 在Linux中,我们可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。

示例:

如果我们想要将进程暂停,输入下面命令:

kill -l

linux进程概念_第30张图片

 我们可以看到第18个信号是使进程继续,第19个信号是使进程暂定。

  • 暂停进程: kill -19 "进程pid"

  • 继续运行进程:kill -18 "进程pid" 

  • t也是暂停状态,和T不一样的是它代表的是进程被调试的时候,遇到断点所处的状态,追踪状态。 

僵尸进程

前面我们提到过,当一个进程正在等待其退出信息被读取的的状态叫做僵尸状态。处于僵尸状态的进程叫做僵尸进程。

  • 僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程(使用wait()系统调用,后面讲)没有读取到子进程退出的返回代码时就会产生僵死(尸)进程。
  • 僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。

下面我们来创建一个维持30s的僵尸进程实例:
linux进程概念_第31张图片

运行该代码以后,我们可以通过下面指令来实现对进程信息的检测:

while :; do ps ajx | head -1 && ps ajx | grep process | grep -v grep; sleep 1; echo "—————————————————————————————————————————————————————————————————"; done

 通过对上面进程的pid和ppid观察,我们得知,pid为26726的是父进程,pid为26727的是子进程。运行5s后子进程退出变为Z状态。此时进程就变为了僵尸进程。

孤儿进程

我们将僵尸进程代码修改为如下代码:

linux进程概念_第32张图片

运行此程序查看进程信息:

linux进程概念_第33张图片通过上图我们可以看出子进程为8133,父进程为8132,可是当我们父进程8132退出的时候,父进程为什么没有变成僵尸状态呢??

  • 父进程8132的父进程是bash,bash会自动回收它的子进程,也就是这里的父进程8132。这里看不到僵尸进程是因为父进程8132被它的父进程bash回收了。

子进程退出的时候父进程要通过某种方式回收子进程,但这里的父进程8132被bash自动回收,但这里的子进程8133还没退出,如果我们的子进程8133退出,那谁来回收子进程呢??

  • 这里操作系统扮演了干爹的角色,如果父进程提前退出,那么子进程就会被1号进程(操作系统)领养,我们把这种被领养的进程称为孤儿进程

⭐:当我们父进程退出的时候,状态由S+变为S,S+代表的是前台进程,S代表的是后台进程,并且这里使用ctrl+c并不能结束进程,我们需要手动kill进程。

僵尸进程的危害

  • 进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了。可父进程如果一直不读取,那子进程就一直处于Z状态?是的!
  • 维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存task_struct(PCB)中,换句话说, Z状态一直不退出, PCB一直都要维护?是的!
  • 那一个父进程创建了很多子进程,就是不回收,是不是就会造成内存资源的浪费?是的!因为数据结构对象本身就要占用内存,想想C中定义一个结构体变量(对象),是要在内存的某个位置进行开辟空间!这样就会导致内存泄漏

进程状态总结

  • 进程在操作系统中的运行状态,对应linux操作系统中的运行状态R
  • 进程在操作系统中的终止状态,对应linux操作系统中的僵尸状态Z和死亡状态X
  • 进程在操作系统中的阻塞状态,对应linux操作系统中的浅度睡眠状态S和深度睡眠状态D
  • 进程在操作系统中的挂起状态,对应linux操作系统中的S、D、T状态。

7、进程优先级

基本概念

优先级vs权限

  • 优先级是进程获取资源的先后顺序。比如你中午去食堂吃饭排队,排在前面优先级高先打到饭,排到后面优先级低,后打到饭。但是总能吃到饭。
  • 权限是你能不能的问题。比如我们想要看某部电影,但需要开通VIP,普通用户不能观看,因为你没有那个权限。

为什么要存在优先级:

  • 在我们系统里面,进程占大多数,而资源始终是少数。CPU只有一个,可是进程有n个,在系统里面竞争资源是常态,所以我们要存在优先级来确定前后。

总结:

  • cpu资源分配的先后顺序就是指的进程的优先级。
  • 优先级高的进程有优先执行权利。配置进程优先级对多任务环境的linux很有用,可以改善系统性能。
  • 还可以把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能。

查看系统进程 

在linux或者unix系统中,我们运行一个无限循环的process文件,并用ps –l命令则会类似输出以下几个内容:

ps -l

 下面我们解释一下上面的几个重要信息:

  • UID : 代表执行者的身份
  • PID : 代表这个进程的标示符
  • PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的标示符
  • PRI :代表这个进程可被执行的优先级,其值越小越早被执行
  • NI :代表这个进程的nice值

PRI and NI

Linux下用PRI(priority)和NI(nice)来确认优先级:

  • PRI即进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,此值越小进程的优先级别越高,越大代表进程优先级越低。
  • NI就是我们所要说的nice值了,其表示进程可被执行的优先级的修正数值只能通过修改ni值来更改优先级。
  • Linux下默认进程的优先级是80
  • PRI值越小越快被执行,那么加入nice值后,将会使PRI变为:PRI(new)=PRI(old)+nice.当我们每次设置优先级,这个old优先级都会恢复为80。
  • 当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行。所以,调整进程优先级,在Linux下,就是调整进程nice值。
  • nice其取值范围是【-20,19】,所以pri的取值范围是【60,99】。分别40个级别。

通过top命令修改进程优先级(ni)

修改进程优先级就是在修改ni值,这里有两种方法:

  • 通过top命令修改进程的nice值。
  • 通过renice命令修改进程的nice值。

⚠:需要注意的是进程的优先级不能随意修改,它会打破调度器平衡。需要我们非要修改进程优先级,就需要使用超级用户root修改。

我们这里只讨论一种方法:使用top命令修改进程优先级

1.创建进程process,修改其优先级:

2.输入sudo top命令:

 linux进程概念_第34张图片

3.按下' r '键,此时会要求你输入待调整nice值的进程的PID

 linux进程概念_第35张图片

4.输入后按下回车,此刻会要求你输入调整后的nice值

 linux进程概念_第36张图片

5.修改后,我们再用ps - al查看是否已经修改:

我们再次修改process的优先级:

linux进程概念_第37张图片

⭐:linux不允许无节制的修改优先级,根据我们前面所学到的知识我们知道,尽管我的ni值设置为-100,但是ni最低是-20。而PRI = PRI(old)+nice = 80 - 20 = 60。我们每次设置优先级,old优先级都会恢复成80,这也正是为什么我们后续把ni设置为10,而PRI变成90的原因。

8、linux中的四个重要概念

竞争性

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

独立性

  • 多进程运行,需要独享各种资源,多进程运行期间互不干扰。进程运行具有独立性,不会因为一个进程挂掉或者异常,而导致其他进程出现问题。

并行

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

linux进程概念_第38张图片

 并发

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

⚠:多个进程在你的系统中运行 != 多个进程都在你的系统中同时运行。

  • 不要以为进程一旦占据CPU,就会一直执行到结束,才会释放CPU资源!我们遇到的大部分操作系统都是分时的!!!多个进程在一个运行队列中,操作系统在一次调度周期内会给每一个进程赋予一个时间片的概念!

linux进程概念_第39张图片

  • 对于上图,我们假设进程1进入CPU运行,假设操作系统给进程1分配10ms时间,当10ms时间到了,那么进程1就暂停运行,操作系统会把进程1从CPU上剥离下来,然后调度进程2,以此类推............假设运行队列中的进程有5个,且操作系统给每个进程都是分配10ms,那么在1s内,这五个进程平均每个进程要调度20次。

⭐:在一个时间段内,多个进程都会通过切换交叉的方式,让多个代码在一个时间段内都得到推进,而这种现象我们就称作为并发。

补充概念:

抢占式内核

  • 操作系统难道就是简单的根据队列来进行先后调度的吗??有没有可能突然来了一个优先级更高的进程呢???对于正在运行的低优先级进程,如果来个优先级更高的进程,我们的调度器会直接把进程从CPU上剥离,放上优先级更高的进程,这就是进程抢占。

进程的优先级 | 队列

  • 在我们的进程队列中,操作系统是允许不同优先级的进程存在的。且相同优先级的进程,是可以存在多个的。在这里我们借用指针数组的实现方法来实现根据不同的优先级,将特定的进程放入不同的队列中!!!

linux进程概念_第40张图片

进程间的切换

  • cpu的内部存在各种各样的寄存器,可以用来临时保存数据。而寄存器又分为可见寄存器和不可见寄存器。当进程在被执行的过程中,一定会存在大量的临时数据会暂存在CPU内的寄存器中。当我们要进行下一个进程而本进程还没结束的时候,我们就需要将本进程的历史数据拿走。

⭐:我们把进程在运行中产生的各种寄存器数据,叫做进程的硬件上下文数据。

  • 当进程被剥离,需要保存上下文数据。
  • 当进程恢复的时候,需要将曾经保存的上下文数据恢复到寄存器中。
  • 上下文数据保存在该进程的task_struct中。

你可能感兴趣的:(《linux操作系统》,数据结构,linux,操作系统,服务器)