Linux操作系统及进程状态

今天想与大家谈论些关于Linux的一些相关知识,话不多说现在开始。

冯诺依曼体系结构

我们常见的计算机都遵循冯诺依曼体系结构,此结构由不同的组成构成:

输入设备:如键盘、磁盘等等。

输出设备:显示器。

外设分为输入设备和输出设备,网卡和磁盘既是输入设备也是输出设备。

设备

计算数据

速度

内存(存储器)

临时存储

外设

永久存储

注:这是速度的快慢是相对于内存来说的!!

其实cpu(在图中就是中央处理器)很笨,它只能被动的去接受指令,在接受指令之前它必须先认识指令,所有cpu它有自己的指令集。

那么为什么cpu在读取和写入的时候,在数据层面只和内存打交道,为什么呢??

因为cpu的速度很快,而外设的速度很慢,内存的速度相对较快,通过将数据load到内存中,cpu再从内存中读取数据这样可以提高整机效率。

cpu从内存中读取数据时,内存中是天然的就有数据吗??

如果内存中本来就没有数据,这时就需要将数据从外设先load到内存中,这个过程是非常缓慢的,不能提高整机效率。所以cpu要读取数据的时候内存会提前load好一批数据。例如当我们电脑开机的时候,操作系统数据就会先从磁盘中加载到内存中,这样操作系统就可以运行了。

我们可以将内存理解成一个大大的缓存,cpu从内存中读取数据,也可以将数据写到内存中去,操作系统再将内存中的数据定期刷新到外设上,这样就实现了读取和写入数据。内存跟cpu和外设打交道,解决了cpu和外设速度不匹配的问题。

我们将内存中的数据搬到外设和外设数据加载到内存在计算机中称为IO的过程(存储器和外设的工作)

结论:

  1. cpu不和外设打交道,只和内存打交道。

  1. 所有的外设有数据载入只能载入到内存中,内存写出也一定写到内存中。

操作系统

概念:是一个进行软硬件资源管理的软件。以下是计算机软硬件结构:

底层硬件是以冯诺依曼结构体系链接的!!!

操作系统为什么要进行软硬件资源的管理??

为用户提供安全稳定的运行环境。

操作系统如何进行管理?

首先我们必须要对管理者进行理解,管理者不需要和被管理者进行交互依旧额能够把被管理对象管理起来。比如在学校,校长是管理者,学生是被管理者。虽然我们不和校长打交道,但是入学之前就已经把自己的的信息上交给了学校,而且信息一直被更新(个人成绩等)因此校长对我们的管理就成了对我们数据的管理。总结操作系统管理软硬件本质就是对数据做管理!!

操作系统对硬件做管理,不需要和硬件打交道,对硬件的管理就是对硬件数据的管理。那么硬件的数据如何被操作系统获取呢?

操作系统通过驱动程序获取数据,通过驱动程序进行硬件的操作。

例如当我们插入鼠标的时候,窗口会弹出硬件驱动已经准备成功这句话,这些都是驱动程序的功劳,使我们的鼠标可以在电脑上正常使用。

我们可以将这部分知识实体化,校长是os(管理者),辅导员是驱动程序(执行者),学生是硬件(被管理者),校长通过数据对学生进行管理,辅导员负责学生数据的采集。

如果学生人数太多(硬件的数据太多),那么该如果管理好这些数据呢?

其实这些数据的本质是一样的,例如学生都有姓名、年龄、学院等。所以我们可以用一个结构体进行管理,将学生属性填入到结构体中,通过不同的学生衍生出一个个的结构体对象,我们对学生数据的管理就成了对结构体对象一个个做管理。对结构体对象做管理我们就可以用数据结构链表管理起来。(不同的情况使用不同的数据结构)所以对学生数据的管理成了对链表结点的管理(增删查改)!!

所有的“管理”本质上都是先描述再组织。

以下是操作系统对硬件做管理的伪代码:

总结:操作系统管理的方式是先描述再组织!!!(描述是结构体)(组织是使用数据结构)

操作系统是对软件资源进行管理的软件,它的目的是为用户提供服务,那么它如何对用户提供服务呢?

操作系统本质上是不相信任何人的,但为了满足用户需求它对外提供了系统调用接口(接口式服务),如果用户的请求是合法的,操作系统会帮助用户进行相应的操作。如果用户不通过操作系统直接对硬件进行非法操作就会导致严重的后果,所以提供系统调通接口既保护了操作系统本身,又满足了用户的需求!!

:用户想要访问硬件并进行操作必须经过操作系统,而且一定有操作系统的接口可以访问!!

那么用户如何使用系统调用呢?

用户使用下层的系统调用太麻烦了,想要使用就要了解操作系统,而操作系统有的选项就看不懂的。所以在系统往上就有一层软件层方便用户使用(shell:指令操作,lib:编程操作,部分指令:界面)。例如我们使用的c语言打印helloworld在显示器上,根据冯诺依曼体系结构数据从内存刷新到外设本质上是我们用了c的标准库将数据给了操作系统,操作系统再通过系统调用将数据刷新到外设上(数据刷新到外设的工作很麻烦,但通过用户操作接口却变得简单,原因就是操作系统为我们用户操作接口封装的很好,这一切的工作都是操作系统为我们做的!!)

进程的概念

进程通俗的来讲就是一个程序被加载到内存就称作是进程。根据冯诺依曼我们需要将磁盘中的程序加载到内存,cpu再从内存中运行对应代码。但是当你写好了一个程序,而磁盘中有成千上万个程序需要被加载到内存,这个时候就造成了程序在内存中管理的问题。这时我们引入PCB这个概念,PCB是一个内核数据结构,我们用PCB将进程一个个管理起来。(先描述)

进程的所有属性包括进程的优先级、进程的状态等等。每一个PCB对应一个进程,这样就可以通过PCB找到内存中的一个个进程。另外它里面还有个next指针,是方便对进程做出管理。所以以后对进程的管理就变成了对PCB做管理(链表的增删查改!!)(再组织)

黑色框框代表PCB,当程序从磁盘中加载到内存的时候变成了一个进程然后将该进程的属性和代码地址往PCB中填入,再将PCB链接起来,这样就完成了进程的增;遍历一遍PCB发现某个PCB中的属性是死亡状态,操作系统就会从链表中删除该PCB并且free掉该PCB指向的代码和数据,这样就完成了进程的改。进程的查类似于之前的操作,进程的改目前我们尚不讨论。

进程=内核数据结构+进程对应的磁盘代码。

查看进程

第一种查看进程方法:

首先可以用ps -axj的操作。

grep -v grep操作是把grep进程过滤掉,原因就是当我们用grep时,grep运行起来也是一个进程。

这时我们能看见该进程的很多属性,接下来我们用一个系统调用接口getpid()查看该进程的pid。

结果如下:

让我们再一次重新运行以下:

此时可以发现进程的pid不一样。原因很简单,当我们每次重新启动一个程序就会每次重新加载到内存,重新加载到内存就会重新变成一个新进程,接着就会重新有一个PCB,里面存储的属性会改变,也就是进程pid被改变。

当我们运行的程序停不下来或者是出问题无法退出时,我们可以用kill -9+pid杀死进程。

另一种查看进程的方法:

进程的信息可以通过 /proc 系统文件夹查看,列如我们要查看pid为11014的进程,我们命令行的代码是这样的:

这样我们就可以查到这个进程的很多属性,其中exe指向的是这个进程在磁盘上对应的可执行程序的路径。此时我将代码继续运行但我把磁盘上的文件删除了会有什么现象呢?

此时exe属性会发出红色警告,代表磁盘上的文件不存在了。但是进程依旧在进行:

所以我们知道了当一个可执行程序加载到内存变成一个进程时,进程是否结束与它磁盘上的文件毫无关系,只和当前进程状态有关。

fork函数

fork的作用是创建子进程,fork有两个返回值,子进程返回0,父进程返回子进程的pid。

fork创建子进程以后所有的代码被父子进程共享。

命令行解释器上的进程的父进程不出意外都是bash。

进程状态

(1)运行状态

一个进程想要被cpu运行,本质上是进程要先进入操作系统里的运行队列,这里的运行队列是一个内核数据结构,一个cpu对应一个运行队列!!我们通过画图进行说明:

在运行队列中排队的本质是对进程的PCB做排队,当排到某PCB时,再通过该PCB找到对应的代码和数据进行运行。

在队列里的这一个个进程,我们称作为运行状态。注:正在被cpu执行的进程一定是运行状态,没有被cpu执行但是该进程在运行队列里我们称该进程也是运行状态!!!

(2)阻塞状态:

cpu的速度很快,很快就能遍历一遍运行队列,但是硬件的速度很慢,当我们cpu调度进程时,进程或多或少需要访问硬件,这个时候进程就要等待获取硬件的资源,所以我们不要认为进程只会等待获取cpu资源,它也要等待获取硬件资源!!!所以每一个硬件也要维护自己的等待队列:

如果一个进程被cpu运行的时候发现该进程调用了fwrite函数要对磁盘进行访问,如果这时cpu停下来等待磁盘就绪就大大影响了整机效率。所以这时操作系统就会把该进程从运行队列中剥离下来,再将该进程放进磁盘的等待队列中,这样cpu就能继续运行其他进程了。该进程脱离了运行队列就不再是运行状态了,而此时它在磁盘的等待队列中,我们称此状态为阻塞状态。当它在磁盘完成等待后操作系统会将它的状态改成R(运行状态)然后将它返回运行队列再被执行。注:所以队列中排队的都是task_struct结构体对象(PCB),并不是对进程的代码和数据做排队。进程状态的本质是进程在不同的队列中等待某一个资源!

(3)挂起状态:

当我们内存中很多的进程都处于阻塞状态时,这些进程不能够被立即调度并且要等待很长时间,因此加载到内存的代码和数据短时间内不会被使用,占据了内存空间(占着茅坑不拉屎)那么内存空间不够了怎么办?这时操作系统为了节省内存空间会将进程的代码和数据暂时保存在磁盘上:

这时我们就称该进程为挂起状态。当该进程等待结束,磁盘上的代码和数据会被重新加载到内存上,然后进行后续的工作。我们将内存数据加载或保存在磁盘上称为内存数据的唤入唤出

Linux中具体的进程状态:

以上讲的是操作系统宏观的进程状态,接下来我们学习Linux中具体的进程状态。

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+

2. 当我们进程访问硬件时,运行状态:S+ 原因:CPU速度很快,硬件速度慢(相对于CPU),例如当我们往显示器上打印数据的时候,%99的时间都在等待IO就绪,%1的时间用来将数据打印到显示器上。需要等待就是阻塞状态,因此是S+状态。

3.T状态是暂停状态,当我们想要暂停一个进程可以用kill -19+pid。

pid为14775本来为S+状态现在变成了T状态

这样我们就暂停了一个程序,那么如何恢复程序继续运行呢?我们用kill -18+pid的方法。

这时我们发现进程由原来的S+变成了S,那么这两者有什么区别吗?

有+代表的前台进程,前台进程中命令行解释器是无效的,如果输入pwd就没有反应,但是可以用Ctrl c终止程序。

无+代表后台进程,后台进程中命令行解释器是有效的,如果输入pwd就会显示出当前路径,但是用Ctrl c无法终止程序,不过可以通过kill -9 命令来杀死程序。

4.D状态代表深度休眠状态,浅度睡眠状态能够被终止,但深度睡眠不可以。在该状态下的进程就像获得了一个免死金牌,无法被操作系统杀掉(用户是通过os杀掉进程的),只能通过断电或者进程自己醒来解决!!!高IO的情况下会看到此状态。一个企业中的集群出现了大量的D状态说明该集群正处于崩溃边缘,只能通过减少负载等待进程苏醒或者断电重启解决。

5.t状态表示这个进程正在被追踪停下,例如在vs下我们可以调试并用断点可将进程停下,这就是t状态。

6.当一个进程完成一个任务并且退出时,该进程的资源不会被立即回收,它的退出结果会被保留在该进程的PCB中,需要等待它的父进程或者操作系统来回收它的资源,我们称这个动作为进程等待。在进程退出以后和在资源被回收之前的期间,我们称它为Z状态,俗称僵尸状态。如果父进程和操作系统都没回收它的资源,那就会造成内存泄漏

7.当进程退出并且它被操作系统或者父进程回收资源以后,我们称它为X状态,俗称死亡状态

8.当进程退出时,它的父进程没有读取到子进程的返回代码就会产生僵尸进程。如果僵尸进程没有被回收维护该进程的PCB就永远在内存中,这样就会产生内存泄露。僵尸进程是不能够被杀死的。有三种状态的进程是不能够被杀死的:D、X、Z。因为它们已经死了。。。。那么该如何处理僵尸进程呢?等到后面让我为你讲解。

Linux操作系统和进程状态我们就讲到这,后面知识点我将持续更新。

猜你喜欢

转载自blog.csdn.net/m0_69005269/article/details/128560987