目录
一、冯诺伊曼体系结构
二、操作系统(Operator System)
2.1 操作系统如何管理硬件?
2.2 操作系统如何管理软件?
2.3 一张图带你直观了解OS管理过程
三、进程(启示录)
3.1 进程的基本概念
3.1.1 进程PCB
3.1.2 task_struct内容分类
3.2 查看进程
3.3 结束进程方式
3.4 通过系统调用创建进程-fork初识
3.4.1 fork()函数
3.4.2 getppid()获取父进程id
3.4.3 创建并查看父子进程
一、冯诺伊曼体系结构
冯诺伊曼体系架构了大部分计算机的程序执行的流程!接下来我们将要主要围绕存储器(这里指的是内存)与程序之间的交集!
在此之前需要你有一定的计算机设备了解,了解内设,外设主要硬件设备!
根据冯诺伊曼体系要求,计算机CPU处理数据的来源是内存,编译器会将程序代码编译成CPU认识的二进制指令集,然后程序运行后加载到内存中,CPU读取内存指令执行代码!整个过程CPU不与外设直接打交道,而是与内存直接“沟通”!也就是说数据载入一定载入到内存中,内存写出也一定写到外设中!
现在打个比方,如果你想在QQ上将某个文件发给自己的好友,从发送到接收的过程,大致可以简化成 你的磁盘-->你的内存-->对方内存-->对方磁盘 的过程!
接下来我们将要简单理解管理软硬件,控制程序的执行的系统软件--操作系统(OS) !
二、操作系统(Operator System)
简单概括:操作系统是一个软硬件资源管理的软件
操作系统的核心任务:1.内存管理 2.进程管理 3.文件管理 4.驱动管理
2.1 操作系统如何管理硬件?
在我们的程序中,我们会无时不刻地利用函数调用我们的外设,比如C的printf,C++的cout,那么谁帮助我们管理安排这些调用呢?答案是我们的操作系统,操作系统会通过底层硬件设备的驱动管理去管理我们的底层硬件!
管理的核心思路是先描述,后组织!在计算机中我们对于数据总会通过某种属性将数据汇聚,比如一名学生,我们通过它的姓名,年龄,班级等特有属性来描述一个学生,然后通过属性数据区分不同的学生,便依此来实现我们特有的操作!在代码实现,我们可以利用一个结构体来实现我们之前所述的描述!操作系统对于不同硬件同样如此,然后操作系统利用数据结构容器(比如链表)组织硬件设备数据信息!并依此操作(增删查改)它们,这样我们可以实现具体环境调用具体的设备!
2.2 操作系统如何管理软件?
软件能管理硬件,同样软件可以管理软件!方法同样是先描述,后组织!
通过上文的所述,我们可以看到OS帮助我们有效运行计算机的运行!用户通过代码指令来操作我们的计算机!但不是操作我们的操作系统!操作系统不相信任何人,用户不能直接对操作系统进行直接操作!因为OS不知道用户的属性是“好人”还是“坏人”!但OS又要给用户提供服务,所以操作系统封装保护了自己,然后它会提供客户系统调用接口!这个过程我们可以理解为我们到银行取钱,但不是直接到金库取钱!工作人员给予我们服务!但我们与工作人员有一窗之隔,我们在窗口两端进行我们的操作!此时我们可以将工作人员看作OS系统,某一个窗口是一个系统调用接口,金库是我们底层的硬件!
那些窗口我们称之为 系统调用!我们将钱或者银行卡从窗口交付的过程叫做传参,钱或者银行卡返回给我的过程称为返回值! Linux操作系统用的是C语言写的,它的系统调用接口本质是C式接口。其实我们之前学过的很多语言函数接口本质上与这些系统调用接口有很大的关系!它们的函数内部封装了这些系统调用接口!它们的函数调用绕不开这些系统调用!
到这还没讲完!几年前,银行的服务比较落后,很多服务在不了解银行操作流程不能完成!很多老人群体不清楚办事流程,所以不会进行银行操作!但随着现代化发展,银行的人性化服务不断发展,银行内有了大厅接待人员,一位什么都不懂的老人只需要将自己的基本信息填好交给这个大厅接待,然后大厅接待去拿着表格到相应的窗口替你办事!整个过程中相当于是别人帮老人完成了服务!这里的老人当作是用户群体,这个大厅经理相当于shell(指令)、C/C++(lib库)、图形化界面!
到这里我们的整个OS操作系统基本过程就讲完了!后面的一张图很完美诠释了整个阶梯流程!
2.3 一张图带你直观了解OS管理过程
三、进程(启示录)
上文提到OS的核心任务有进程管理,那么它如何管理进程呢?
同样是先描述,后组织!下面我们来学习Linux第一座大山:进程!
3.1 进程的基本概念
进程:一个运行起来(加载到内存)的程序称为进程(粗略)
3.1.1 进程PCB
我们知道我们写的代码本质是一个文件,最终放到我们的磁盘上!当我们运行exe文件时,exe文件会被加载到内存中!我们的计算机可能有许多程序被加载到内存中,那么CPU如何判断先执行哪一个程序呢?
同样还是先描述,后组织!描述一个正在运行的程序(进程)的工具是--进程PCB(进程控制块)!在Linux中进程PCB是结构体task_struct!PCB中数据是一个进程的所有属性!每一个加载到内存的进程,OS通过PCB描述它们,然后按照先后通过链表串联这个PCB(组织)!
3.1.2 task_struct内容分类
标示符: 描述本进程的唯一标示符,用来区别其他进程。
状态: 任务状态,退出代码,退出信号等。
优先级: 相对于其他进程的优先级。
程序计数器: 程序中即将被执行的下一条指令的地址。
内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。
I/ O状态信息: 包括显示的I/O请求,分配给进程的I/ O设备和被进程使用的文件列表。
记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
其他信息
💡💡这里PCB有一个数据是内存指针存储的是指向程序代码文件的指针,也就是说PCB中有了这个文件指针,我们就能找到对应的磁盘代码!也就是说一个进程=内核数据结构(PCB) + 进程对应的磁盘代码
所以CPU读取代码指令,首先OS系统帮助我们在进程链表中遍历每一个进程的PCB,查看它们的进程状态以及进程优先级,综合考量后将一个进程交付给我们的CPU读取进程相应的文件代码!所以操作系统管理进程的本质其实是管理进程的PCB!
3.2 查看进程
在Windows操作系统中,我们可以通过任务管理器查看我们的进程!
那么在Linux中我们如何查看一个进程呢?
Linux中进程的标识符为pid_t类型的数字,系统调用函数getpid()获取进程的标识符!
现在我们来看getpid函数头文件!
现在我们上代码~
Linux中查看我们进程属性的方式: ps ajx | head -1 && ps axj | grep waitpro |grep -v grep
这里的grep后面跟exe文件名,grep -v grep是因为grep本身也是一个进程,所以-v取消查看grep进程!
现在我们来查看一个进程!
3.3 结束进程方式
结束进程方式:kill -9 +进程的pid /在运行的代码窗口ctrl+c !!
3.4 通过系统调用创建进程-fork初识
3.4.1 fork()函数
fork函数是用来创建子进程的!fork之后会有两个进程
子进程,顾名思义,首先它是一个进程,子进程依赖于父进程,父进程通过fork来创建子进程!创建完子进程后,OS会描述子进程,提供子进程的PCB!子进程会共享自fork函数以后的代码!
fork函数对于不同进程返回不同的值,fork创建失败返回-1,fork创建子进程成功以后,父进程的fork函数会返回子进程的id,子进程的fork函数会返回0!通过id的不同,我们可以让父子进程执行不同的代码!
到后面学到进程地址空间,我们会更加深入地讲解fork函数如何返回两个值!
3.4.2 getppid()获取父进程id
现在我们再看看这个进程的父进程!
我们可以看到我们的进程两次运行进程id发生了变化,但是我们的父进程id却没有发生变化!我们的父进程是bash!它是我们xhell的代理人!如果杀掉这个父进程,我们的xhell也就会什么命令都执行不了!这个bash每次我们登录xhell就会启动!也就是说我们的程序每次加载到内存,OS就会描述一遍这个进程!
3.4.3 创建并查看父子进程
现在我们直接上代码看看如何使用fork()创建子进程
查看父子进程
根据结果,很明显我们可以看到,子进程的fork返回值是0,所以执行了if语句的代码!父进程fork获得了子进程的id,执行了else语句!
再进一步我们来直接打印证明我们fork返回值