现代的计算机, 大多遵守 冯诺依曼体系结构
【补充】:
由于CPU、存储器、输入设备、输出设备,都属于是硬件设备。普通用户和硬件设备进行交互是很困难的,操作系统就是用来解决这个问题的。
操作系统是一组做计算机资源管理的软件的统称。目前常见的操作系统有:Windows系列、Unix系列、
Linux系列、OSX系列、Android系列、iOS系列、鸿蒙等
系统调用:操作系统给应用程序提供的API接口
例如:我们想在控制台打印一个“hello world”,首先调用printf这个库函数,这个库函数的内部就会调用操作系统提供的系统调用write,然后进入到内核来执行,内核进一步的控制硬件,完成输出.
驱动程序:属于软件,由硬件厂商提供的,直接和硬件设备打交道,相当于在硬件和内核之间进行了一层转换.
【补充】:
当我们使用系统调用进入到操作系统内核的时候,会涉及到用户态到内核态之间的切换。由于内核只有一个,应用程序有很多,这些程序都需要靠这个内核来提供工作支持,此时很可能需要排队等待
操作系统的核心功能:
操作系统是一个用来管理的软件,包括内存管理、文件管理、设备管理、进程管理等.
每个应用程序运行于现代操作系统之上时,操作系统会提供一种抽象,好像系统上只有这个程序在运行,所有的硬件资源都被这个程序在使用。这种假象是通过抽象了一个进程的概念来完成的,进程可以说是计算机科学中最重要和最成功的概念之一。
进程是操作系统对一个正在运行的程序的一种抽象,换言之,可以把进程看做程序的一次运行过程;同时,在操作系统内部,进程又是操作系统进行资源分配的基本单位。
进程和程序的区别:
例如:
QQ进程,动态的,被加载到内存中的
QQ程序,属于可执行文件,静态的,存储在硬盘上的
一个计算机的硬盘上可能存储着很多的可执行文件,但同一时刻可能只有一小部分在运行
二者主要区别:
(1)程序是永存的;进程是暂时的,是程序在数据集上的一次执行,有创建有撤销,存在是暂时的;
(2)程序是静态的观念,进程是动态的观念;
(3)进程具有并发性,而程序没有;
(4)进程是竞争计算机资源的基本单位,程序不是。
(5)进程和程序不是一一对应的: 一个程序可对应多个进程即多个进程可执行同一程序; 一个进程可以执行一个或几个程序
(6)进程是动态的,而程序是静态的。
(7) 进程有一定的生命期,而程序是指令的集合,本身无“运动”的含义。没有建立进程的程序不能作为1个独立单位得到操作系统的认可。
(8)1个程序可以对应多个进程,但1个进程只能对应1个程序。进程和程序的关系犹如演出和剧本的关系。
进程管理是操作系统内核的功能
一个操作系统上同时运行着很多的进程,操作系统是如何将他们管理起来的呢?
主要分为两个步骤:
class PCB {
// 进程的唯一标识 —— pid;
// 进程关联的程序信息,例如哪个程序,加载到内存中的区域等
//内存指针
//文件描述符表
// 分配给该资源使用的各个资源
// 进度调度信息
}
形如上面的PCB,就代表着一个实实在在运行着的程序,也就是进程。
【说明】:
pid :进程的唯一标识
内存指针:操作系统需要程序中的一些必要的数据(需要运行的指令以及运行时依赖的数据)加载到内存中,内存指针用来描述该进程的内存中哪些部分是指令,哪些部分是数据
文件描述符表: 用来表示当前的进程都打开了哪些文件
内存指针和文件描述符表描述了当前进程持有的系统资源,认为进程是操作系统中“资源分配”的基本单位.
进程里还有一些关键的属性,用来实现进程的调度,其中包括进程的优先级、进程的状态、进程的记账信息、进程的上下文
由于计算机的CPU是有限的,但进程的数量是比较多的,操作系统要做到尽可能地公平,就需要进行调度。
由于CPU的轮转速度是非常快的,CPU的主频达到1.9GHz,也就是1s内有19亿个时钟周期,人们从宏观上看,就好像是这些进程都在同时执行一样,但在微观上并不是同时执行的,而是轮流的占用CPU,我们也把这种执行方式叫做并发式执行。
CPU上有多个核心,每个核心上都可以运行一个进程,同一时刻两个进程在两个CPU核心同时执行,这种执行方式叫做并行式执行。
【二者的区别】:
并发式执行:宏观上同时执行,微观上不是同时执行
并行式执行:宏观上同时执行,微观上也是同时执行
进程里有一些关键的属性,用来实现进程的调度,其中包括进程的优先级、进程的状态、进程的记账信息、进程的上下文
对于这些属性的含义呢,我们先看下下面的这个例子,通过这个例子来理解一下。
假如小绿是个漂亮的妹子,她的追求者有很多,她选男朋友的标准是:有钱,长的帅,会舔,但是没有同时具备这三个条件的人,小绿决定同时和A.B.C三个人交往,A很有钱,B长的帅,C很会舔.
此时小绿需要化身成时间管理大师,周一和A逛街,周二和B吃饭,周三和C一起学习…
如果此时将小绿看成CPU,把ABC看作是三个进程的话,在时间表的安排下,就好像是ABC三个进程并发的在CPU上执行
进程优先级:每个进程都有相应的优先级,优先级决定它何时运行和接收多少 CPU 时间。
在排时间表的时候,优先级就用来决定先给谁排,后给谁排。
进程的状态: 进程状态反映进程执行过程的变化。这些状态随着进程的执行和外界条件的变化而转换。在三态模型中,进程状态分为三个基本状态,即运行态,就绪态,阻塞态。在五态模型中,进程分为新建态、终止态,运行态,就绪态,阻塞态。
比如A说他要出差一周,那么就下来一周的时间,小绿给B和C安排就行了。“出差” 对于A来说就是一个特殊的状态,正常情况下,ABC的状态都是就绪状态,如果A出差了,A就进入了阻塞状态
进程的记账信息: 操作系统在安排进程的时候,也会记录每个进程以往在CPU上执行的时间,如果发现某个进程被安排的太少,就会进行适当的调整。
小绿在安排时间表的时候,会考虑到以往的时间安排,如果小绿发现前几周给C安排的时间都太少了,下一周就会给C安排更多的时间
进程的上下文: 对于进程来说,具体指得是CPU里的一堆寄存器里面的值,上下文就会在进程被切出CPU的时候把寄存器的状态保存到PCB(内存),下次进程回到CPU上就把PCB里的上下文读取出来,恢复到CPU寄存器种
也可以理解成进程在执行某个操作的时候,执行了一半就被调度走了,此时需要记录当前执行的操作执行到了哪里,方便调度回来的时候继续从当前位置继续向下执行
在上面我们已经介绍了在进程的PCB中的内存指针,是用来描述当前进程持有哪部分内存空间的。
同时由于虚拟地址空间的存在是进程具备隔离性。
进程隔离性: 一个进程的运行不会影响到另一个进程,尤其是一个进程崩溃也不会影响到另一个进程
【补充】:
进程的隔离性,确实让系统更稳定了,但是也有别的问题但多个进程之间想要配合工作,就变得很麻烦 于是操作系统又引入了 “进程间通信”
这里可能有些同学会有这样的疑问:如果每个进程都有一个"虚拟地址空间",一个系统里有很多的进程,那这些虚拟地址空间的总和超过了物理内存大了,该怎么办?
主要有两个原因限制了这种情况的发生:
但也不妨存在极端情况,导致物理内存不足,出现这种bug的话,就需要程序员去进行优化或换一个内存更大的机器
如上所述,进程是操作系统进行资源分配的最小单位,这意味着各个进程互相之间是无法感受到对方存在的,这就是操作系统抽象出进程这一概念的初衷,这样便带来了进程之间互相具备”隔离性(Isolation)“。
但现代的应用,要完成一个复杂的业务需求,往往无法通过一个进程独立完成,总是需要进程和进程进行配合地达到应用的目的,如此,进程之间就需要有进行“信息交换“的需求。进程间通信的需求就应运而生。
目前,主流操作系统提供的进程通信机制:
管道、共享内存、文件、网络、信号量、信号
其中,网络是一种相对特殊的 IPC(Inter-Process Communication) 机制,它除了支持同主机两个进程间通信,还支持同一网络内部非同一主机上的进程间进行通信。
在多进程里,经常会谈到"父进程"与"子进程",例如,进程A中创建了进程B,那么A是B的父进程,B是A的子进程
但是在多线程里,没有"父进程"与"子进程"的概念
一个线程就是一个 “执行流”. 每个线程之间都可以按照顺讯执行自己的代码. 多个线程之间 “同时” 执行着多份代码.
并发编程
虽然多线程也能实现并发编程,但是线程比进程更轻量
【补充】:
创建进程的步骤:
同一个进程里的若干线程之间的关联关系:
由于创建、销毁进程很消耗时间,为了提高效率,便引入了"线程",线程也可叫做"轻量级进程"
线程是包含在进程中的,(一个进程里可以有多个线程),每个线程也都有自己的PCB,同一个进程里的多个线程之间,共用同一份系统资源,这意味着新创建的线程无需重新分配系统资源,只需和它出于同一进程里其他线程共用一份系统资源,这就是创建线程比创建进程更省时的原因。
下面这个例子进一步帮助大家理解多进程和多线程
但并不是线程数越多越好,因为随着线程数的增加,CPU的核心逐渐被吃满后,再继续增加线程数,已经没有意义了,也就是说,使用多线程确实能够提高效率,但前提是多喝资源是充分的,随着线程的增加,CPU的核心数都被吃满了,此时在继续增加线程,就已经没有意义了,这是速度不会提升,反而额外增加调度的开销
但是这里还会涉及到一个问题就是,多个线程之间不会相互影响吗,也就是说多个滑稽老铁之间不会相互打架吗?会,如果两个滑稽老铁相中了同一个鸡腿,那么此时就可能发生冲突。
在多线程的角度上,可以理解成多个线程访问同一个变量的时候,也有可能发生冲突,这属于我们后面要学的线程安全问题。
还有一个更严重的问题就是,如果某个滑稽因为没有抢过别人,直接把桌子掀了,那么其他人也都吃不到了
这就相当于,在一个进程中的某个线程抛出异常(如果catch了这个异常就安全了),如果没有得到妥善的处理,就可能导致整个进程都崩溃。
线程是操作系统中的概念,操作系统内核实现了线程这样的机制,并且对用户层提供了一些API供用户使用。(Linux中的pthread库)
Java标准库的中的Thread类可以视为是对操作系统提供的API进行了进一步的抽象和封装