冯诺依曼体系
上图中的存储器指的是内存,内存的速度是较快的
内存是掉电易失的并不能永久存储数据。磁盘属于外存拥有永久性存储能力,是外设的一种,磁盘即是输出设备也是输入设备(外设是相对于内存和CPU而定义的,可以分为输入/输出设备,外设的速度较慢)
运算器 + 控制器 + 其他 = CPU,CPU的速度是极快的
CPU运算时是需要数据的,它只能被动的接受运算指令和数据。编译的本质就是把程序翻译成CPU认识的指令让CPU能够去运行。
CPU会在控制信号层面和外设会有交互
因为外设比内存的速度慢很多,所以CPU再读取和写入的时候,在数据层面只和内存交互。因为数据是存储在磁盘上的,所以内存在和CPU交互前,磁盘会预先的把数据写入到内存里等待CPU需要,因此程序要运行必须先加载到内存,所以内存也可以看成是一个大的缓存。而这种内存和外设的数据交互就是I/O操作
操作系统(OS)
操作系统是一个进行软硬件资源管理的软件。操作系统要通过合理的管理软硬件资源,为用户提供良好的、稳定的、高效的、安全的执行环境。操作系统不需要直接和硬件交互,只需要通过驱动拿到硬件的数据进行管理
操作系统会对数据先描述(定义一个结构,每个数据都是一个结构体对象),再组织(将所有的结构体对象用链表结构链起来)
操作系统为了确保自身安全,并不能让用户直接访问。用户可以通过发送指令,用户操作接口(shell,lib)接受指令再去调用操作系统接口,操作系统接收后再执行对应内容
进程/PCB概念
程序的本质就是文件,在磁盘上存储
由于内存会从硬盘上加载很多的程序进来,所以操作系统要对这些程序管理起来,首先会把每个进程描述成一个结构体 (struct task_struct),里面保存了该进程的所有属性和该进程对应的代码和属性地址以及下一个结构体的地址。每一个结构体就可以称为每个程序的PCB(进程控制块)
进程 = 加载进内存的程序 + 该程序对应的PCB
CPU执行就会去找优先级高的PCB,通过PCB找到对应的代码并执行。如果某个进程结束或者死亡了,CPU就会释放掉该进程对应的PCB和可执行程序。进程在调度运行的时候,是具有动态属性的
进程常用的调用操作
PPID为父进程ID,PID为当前进程ID(子进程)
ps ajx – 显示当前所有进程
ps ajx | grep “进程名” – 查看指定进程
ls /proc/ – 查看所有进程(以文件形式,每个进程都是一个文件)
进入进程文件后可以查看该进程的所有属性。当进程运行时删掉硬盘中的可执行文件时,进程还是会运行
kill -9 PID – 杀掉指定进程
getpid() – 返回当前进程的ID – <sys/types.h>
getppid() – 返回当前进程的父进程ID – <sys/types.h>
fork() – 创建子进程 – <unistd.h>
fork()执行前只有一个父进程,执行后:父进程+子进程。创建成功后子进程ID会返回给父进程,0返回给子进程
一个程序可以有多个进程同时运行相同的代码,也就是说父进程和子进程可以共享执行fork()之后的代码,通过返回值不同可以控制多个进程执行不同的代码–多进程–并发
进程状态
不同的操作系统可能状态的名称不一样或者也会有不一样的状态,常见的状态有运行,阻塞,挂起,新建,就绪,等待,挂机,死亡……
一个进程的状态也是进程内部的属性,在PCB里放着
进程的这么多状态都是为了满足不同的场景
进程不只是会等待(占用)CPU资源,也会占用外设资源。每一个外设资源都会有自己的等待队列
进程的状态本质上是进程在不同的队列里,等待某种资源
一个CPU有一个运行队列,让进程入队列本质上是将该进程的PCB结构体放入运行队列中。
运行状态:
凡是PCB在运行队列里的进程都是运行状态,并不是正在运行时才是运行状态
阻塞状态:
进程不能直接被CPU调用,并不在运行队列中,而是将进程的PCB对象放到硬件的等待队列里
挂起状态:
因为内存的空间有限,如果有大量的进程处于阻塞状态的话就会大量的占用内存的空间,所以操作系统为了避免这种情况就会将阻塞的进程对应的代码和数据备份一份到硬盘并删除掉在内存中的那一份,但是此时进程并没有被释放掉,它的PCB还是留在内存中,这时候的进程就处于挂起状态。(阻塞不一定挂起但是挂起一定是阻塞)
对于Linux来讲:运行状态–R、睡眠状态–S,暂停状态–T,深度睡眠状态–D,追踪暂停状态–t,僵尸状态–Z
运行状态–R
当进程的PCB处于运行队列中时就是运行状态
睡眠状态–S
因为CPU的运行速度非常快,所以一般来说只要访问到外设进程大部分时间都是在等待的也就是阻塞状态,Linux上的睡眠状态就是阻塞状态的一种
暂停状态–T
kill -19可以让进程变为暂停状态,也是阻塞的一种。kill -18可以恢复状态。恢复后的进程会变为后台运行
深度睡眠–D
如果当前的进程太多了,挂起也解决不了内存空间饱满的时候,操作系统就会自动的杀掉某一些进程,但是这里就会出现一些问题。因为操作系统杀死进程是不会根据用户意愿的,所以难免就会杀死一些比较重要的进程,当进程被杀死后就不可以恢复了。
为了应对这种情况,Linux就设定了深度睡眠状态,处于深度睡眠状态的进程,是不可以被用户和操作系统杀掉的,只能通过自己醒来或者断电的操作才可以杀掉
深度睡眠一般只会出现在高IO的情况下
追踪暂停状态–t
追踪暂停状态是一种特殊的暂停状态,进程处于此状态表示该进程正在被追踪,比如调试进程时
僵尸状态–Z
一般来说进程退出的时候,不能立即释放其对应的资源,需要保存一段时间让父进程或者操作系统读取到它的资源。当进程退出时到父进程读取前的这段时间里,进程就属于是僵尸状态。如果不回收僵尸状态的子进程会导致内存泄漏
当状态后面有个+号时代表前台运行,此时命令行不能写入数据。没有+号时是后台运行,此时可以在命令行写入指令并响应,但是此时不能用ctrl+c终止进程,只能用kill -9直接杀死
孤儿进程是指父进程提前退出后,为了防止子进程无法管理回收资源,这时子进程会被操作系统(1号进程)领养,被操作系统领养的进程就被称为孤儿进程。
进程优先级
CPU资源分配的先后顺序就是指进程的优先级。在Linux中优先级的本质就是PCB里面的一个属性(可能是一个整数,可能是几个整数)
Linux的最终优先级 = 老的优先级(PRI默认为80)+ NI值。修改优先级就是修改NI值
最终优先级的范围是[60,99],所以NI值的修改范围在[-20,19]
修改优先级的步骤:
top指令(可能需要sudo提高权限)------输入r-------输入需要修改的进程id--------输入想要修改的NI值
进程的其他概念
竞争性:
系统进程数目众多,而CPU资源只有少量甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务更合理竞争相关资源,便具有了优先级
独立性:
多进程运行需要独享各种资源,多进程运行期间互不干扰
并行:
多个进程在多个CPU下分别同时进行运行,这称之为并行
并发:
多个进程在一个CPU下采用进程切换的方式,在一段时间之内让多个进程都得以推进,称之为并发
进程的切换:
CPU不会等待一个进程执行完再执行下一个进程,而是每个进程都会有自己的时间片,当进程执行的时间到了后,CPU就会切换进程执行。
CPU会有一套寄存器,寄存器是被所有进程共享的,但是寄存器里的数据只属于当前进程。进程切换的时候,需要先进行上下文保护,这里的上下文指的是CPU里的寄存器的数据,而不是寄存器。当进程恢复运行时,上下文需要恢复