小编个人主页详情<—请点击
小编个人gitee代码仓库<—请点击
linux系列专栏<—请点击
倘若命中无此运,孤身亦可登昆仑,送给屏幕面前的读者朋友们和小编自己!
前言
【linux】linux调试器-gdb的使用—书接上文详情请点击<—
本文由小编为大家讲解—【linux】冯诺依曼、操作系统、进程、PCB的概念讲解
一、冯诺依曼体系
冯诺依曼体系是由冯·诺依曼提出的计算机设计的基本框架,包括五大部件:输入设备,存储器,运算器,控制器,输出设备。我们的计算机,笔记本的设计都遵守冯诺依曼体系
这五大部件都是独立的个体,各个硬件单元必须使用“线”连接起来,总线:系统总线,IO总线
其中运算器和控制器即为我们常说的中央处理器(CPU)
其中输入设备和输出设备是外设(外部设备)
- 存储器:指的是内存,在冯诺依曼体系结构中处于核心地位,是硬件级别的缓存空间
- 输入设备:鼠标,键盘,摄像头,话筒,磁盘,网卡……
- 输出设备:显示器,播放器硬件,磁盘,网卡……
- 运算器:对我们的数据进行计算任务(算数运算,逻辑运算)
- 控制器:对我们计算机硬件流程进行一定的控制
- 一个程序要运行起来,必须先加载到内存中?因为冯诺依曼体系结构规定必须这样执行
- 因为存储是分级的,中央处理器(CPU)的运行速度非常快是纳秒级别的,磁盘的运行速度是毫秒级别的,计算机整体是要注重运行效率的,根据木桶原理,如果使CPU去读取磁盘的数据,那么计算机的效率会下降至和磁盘的运行效率一样,这必然不是我们想要的
- 所以就加入了一个中间枢纽—内存,内存的运行速度是微秒级别,这个速度处于CUP和磁盘运行速度的中间,那么就由内存去和CUP和磁盘去打交道,这样有了这个中枢,计算机的整机运行效率就和内存的运行速度差不多,这样的整机效率也不低,达到我们的要求
- 那么不考虑缓存的情况,CPU只能对内存进行读写,不能访问外设(输入或输出设备)
- 外设(输入或输出设备)要输入或输出数据,只能写入内存或从内存中读取数据
- 程序其实就是二进制文件(代码和数据),它被存储到磁盘中,程序要运行就要被要被中央处理器以进程的方式运行,那么磁盘不能直接访问中央处理器,所以就必须先将二进制文件加载到内存中,再由内存和CPU进行交互,进而达到运行程序的目的
二、操作系统(OS)
操作系统是一款进行管理的软件,操作系统包括内核(进程管理,内存管理,文件管理,驱动管理)和其它程序(函数库,shell程序)
操作系统通过对下管理好底层的软硬件资源(手段),为用户提供良好(稳定,高效,安全)的执行环境(目的)
通常来讲,这个用户我们将其理解为程序员,而不是普通用户,普通用户使用的客户端和手机app软件是由程序员开发出来的软件,并不是使用的操作系统,而程序员要开发软件,就注定要使用操作系统,所以这个用户特指程序员
- 操作系统里会有各种数据,操作系统并不相信用户,这些用户可能会正常使用,也可能会非正常使用,如果是非正常使用,那么就会对操作系统造成伤害,造成数据丢失等,那么操作系统造出来就是为了服务用户,如果无法服务用户,那么也没有存在的必要了
- 那操作系统既要为用户提供服务又要保证数据的安全,那么操作系统就以接口的方式给用户提供调用的入口,让用户通过接口来获取操作系统内部的数据
- 这个调用方式称为系统调用,用于调用的接口是由操作系统提供的,并且使用c语言实现的接口,是操作系统自己内部的函数调用
- 因此所有访问操作系统的行为,都只能通过系统调用来完成
用户使用操作系统都要自上而下贯穿计算机的软硬件结构
lib库函数和系统调用接口的关系是lib库函数调用系统调用接口,因为lib库函数要访问操作系统,而要访问操作系统又只能通过系统调用,所以lib库函数只能通过调用系统调用接口才能对操作系统访问,即lib库函数和系统调用接口的关系是上下层的调用和被调用的关系
- 当我们知道了这个原理之后,那么我们就可以知道,任何语言包括c语言,c++,java,php,go语言等等,本质就是对系统调用接口进行一定的封装才形成的语言
- 操作系统如何进行的管理?先描述,再组织
- 在管理的结构中分为管理者(操作系统),执行者(驱动程序),被管理者(软硬件资源)
- 管理的本质其实就是管理数据,通过对数据的管理,达到对被管理者的管理,这些数据是通过执行者拿到的
- 例如你要描述一个人,那么就要描述这个人的一些属性(数据),比如身高,性别,体重,职业,成就。那么小编来描述一个人,身高:181cm,性别:男,体重:74KG,成就:445天成就最快大满贯,职业:乒乓球职业运动员。只要描述一个人的属性(数据)足够多,那么这个人就越容易被确定是哪一个人,小编进行描述的是张继科
- 在操作系统进行管理,首先要先描述,即确定要对谁进行管理,那么如何确定,就是通过属性(数据),当这些属性足够多的时候,这个被管理的事物就可以被确定,在操作系统中,由于linux是使用c语言写的,用于描述事物的属性(数据)就被封装在一个struct结构体中,构成一个结构体类型,当要管理事物的时候,先使用这个结构体类型去实例化出一个对象,这个对象中包含着这个事物的属性以及指向这个事物的地址,这样对事物的管理就转化成了对这个结构体对象中数据的管理
- 当有多个事物需要进行管理的时候,为了管理的有序,其中要被管理的事物已经被一个struct对象描述,那么我们接下来就是进行组织,即使用数据结构管理这些struct对象,例如可以使用单链表,在这些struct对象作为一个节点,这个节点中放入结构体指针,将其指向下一个结构体,那么就使用了单链表将被管理的对象组织起来了,这个组织的过程叫做建模
- 当组织起来之后,那么操作系统对多个事物的管理就转化为了对数据结构单链表的增删查改,例如删除一个事物,那么组织起来的单链表中有用于描述这个事物的结构体对象,这个结构体对象中有指向这个事物的指针可以找到这个事物进行删除,删除完之后在这个单链表中删除描述这个事物的结构体对象就达到了对这个事物的删除
- 再例如要增加一个事物,那么先使用结构体对象对这个事物进行描述,之后使用单链表对这个结构体对象进行连接即可,对数据的管理就达到了对事物的管理
- 所以在操作系统中,对管理任何事物,先描述再组织之后,最终都可以转化为对某种数据结构的增删查改
- 所以在我们的操作系统中,注定了一定存在大量的数据结构
三、进程、PCB
通俗来讲:一个已经加载到内存中的程序叫做进程(任务),正在运行的程序叫做进程
细致来讲:进程是由内核PCB(process control block)数据结构对象加上你自己的代码和数据构成
- 内核PCB数据结构对象(进程控制块):描述这个进程信息(数据)所有的属性值的集合,这个集合被放在了进程控制块中
- 程序其实就是二进制文件,这个二进制文件由你自己的代码和数据构成
- 通过操作系统如何进行管理的我们知道,任何一个事物要进行管理,都需要先描述再组织,多个进程也需要被操作系统管理,那么也不例外,对多个进程的管理,也要先创建用于描述进程属性的结构体对象再进行组织,最终会转化为对某种数据结构的增删查改,这里的某种组织,对于tack_struct最为适合的是双向链表
- 操作系统已经对进程的全部属性进行了描述并且封装成了一个结构体对象,在进程中这个结构体对象称为内核PCB数据结构对象,这个内核PCB数据结构对象其实是一个统称,在任何操作系统Windows,Linux,MacOS都适用,在Linux中被另外起了一个名字叫做task _struct,
- tack_struck的本质是描述进程的结构体,tack_struct是linux内核中的一种数据结构,对于tack_struct最为适合的是双向链表,其被装载到内存(RAM)里面并且其包含着进程的信息
PCB这个结构体大体包括
- 进程的编号(标识符),每一个进程都有与之对应的唯一标识符
- 进程的状态,进程是启动,是休眠,还是关闭
- 进程的优先级,同时存在多种进程优先执行哪个进程
- 相关的指针信息,这个指针指向进程的地址
- 数据结构类型的指针例如:strcut PCB* next,strcut PCB* queue……,因为这个进程可能会被组织放置在不同的数据结构下,这些组织后的数据结构的状态作用目的不同,例如僵尸进程,等待进程等
- 其它信息
- ps ajx可以获取liunx操作系统上的所有正在运行的进程信息
- 现在小编有一个死循环程序打印的程序test.c,每次打印休眠1s,运行后如果想要退出的时候ctrl+c即可退出,小编将其编译链接称为test二进制文件,并且运行起来,那么在我们的linux中就成为了一个进程
#include <stdio.h>
#include <unistd.h>
int main()
{
while(1)
{
printf("我是一个进程,正在运行啦!\n");
sleep(1);
}
return 0;
}
- test二进制文件,既然被小编运行起来了那么就是进程,那么就可以被包含在了当前linux正在运行的进程,我们可以使用 ps ajx|head -1&&ps ajx|grep 文件名,获取它的进程信息,其中对应的PID就为这个正在运行的进程的进程编号(标识符)
- /proc系统目录中,存储着所有正在运行的进程的目录文件及其相关的进程信息,并且进程的目录文件名是以进程对应的进程编号(标识符 PID)为名称,我们使用ls查看一下这个系统目录,可以看到小编运行的程序的对应进程的标识符7517存在于其中
- ll /proc/标识符,小编打开一下小编运行的程序的对应进程的标识符7517的目录文件
exe是链接文件,通过这个链接文件可以找到进程的位置,进程是由二进制文件运行后进行的,exe对应即二进制文件的绝对位置
cwd是当前进程的工作目录,进程的所有工作都将默认在这个工作目录中执行,进程在运行时产生临时文件的路径即默认为cwd对应的工作目录和临时文件名进行拼接
- 如何证明cwd的拼接的呢?下面小编在我们的程序内fopen使用"w",打开一个临时文件code,关于fopen我们知道当我们使用"w"打开一个文件的时候如果当前目录下不存在这个临时文件code,那么就会进行自动创建,运行程序之后查看code的路径位置即可
#include <stdio.h>
#include <unistd.h>
int main()
{
fopen("code","w");
while(1)
{
printf("我是一个进程,正在运行啦!\n");
sleep(1);
}
return 0;
}
- 重复上述操作,并且观察,即使是相同的二进制文件,每次运行后产生的进程的进程编号(标识符 PID)都不相同
- 那么接下来我们查找一下进程的目录文件,并且验证code文件的路径
很明显的可以观察出,code的路径就为cwd对应的当前进程的工作目录和code文件名拼接而成,并且当前进程所有的工作都在这个路径下进行,并且产生的临时文件也在cwd对应的当前进程的工作目录下
总结
以上就是今天的博客内容啦,希望对读者朋友们有帮助
水滴石穿,坚持就是胜利,读者朋友们可以点个关注
点赞收藏加关注,找到小编不迷路!