计算机网络(操作系统)

计算机原理(操作系统角度)

一、操作系统(Operating System /OS)*

1、What/Why/History
多用户-多任务场景 OS的主要职责就是协调——专门的软件来协调工作
【(1)硬件(资源)会被多个任务“同时”访问。(2)OS协调不同任务对硬件资源的访问。OS管理着硬件资源,分配给不同的任务去使用。】

2、从职责视角看OS
(1)OS是一个管理资源的软件。(硬件/软件资源)
(2)OS是一个协调者(管理的含义就是做协调)
(3)OS是一个分配者,分配的主体就是任务。站在任务的角度,就是一切硬件资源都需要找OS申请后才能被授权使用。

3、从地位视角看OS
计算机网络(操作系统)_第1张图片

【总的来说,OS主要研究的就是“管理学”,如何高效、公平、低耗、稳定的进行资源的分配、协调工作以及在其中遇到的种种问题】
【程序最终是以进程(process)的角度去操作系统(协调者/管理者)那申请不同的硬件资源(CPU/IO/内存)进行使用】

4、程序(Program) VS 进程(Process)/ 作业(Job)
(1)程序:就是进行计算的一组流程,抽象为一套做数据处理的步骤(静态,一般是表现为一个或者一组文件)。程序=指令+数据
(2)进程:(本义:过程)进程就是程序的一次执行过程(动态)【*站在用户的视角看什么是进程】
【一个程序可以进行多次执行(表现为多个进程);甚至可以“同时”执行(多个进程同时存在),进程是程序在运行阶段的视角主体。程序是静态的,进程是动态的】

二、进程(Process)(重点)

1、进程基本知识
(1)从用户视角看:进程就是程序的一次执行过程(动态)
(2)站在OS实现的角度看什么是进程:
进程是OS进行资源分配的基本单位。
进程是OS进行资源分配的最小实体。

【1、OS的职责:协调者/管理者
2、资源:被OS管理的资源(硬件资源、软件资源)
3、分配的实体/单位:以此作为基本单位进行分配。分配的时候,只考虑到这一层级
4、基本/最小:允许有更高层级的单位,但是最低到进程】

【结论:
(1)同一进程之间的资源是共享的。(如果存在比进程更低级的单位(进程下的单位),资源是共享的)
(2)不同进程之间的资源,是隔离的。(A进程只能访问A进程自己的资源(这里主要讨论的是内存),A进程无法访问B进程的资源(内存),更无法访问被OS 直接管理的资源)】

理解进程是什么是非常非常重要的!!!
(1)动态过程
(2)绑定着一个程序
(3)允许同时存在
(4)OS内部进行资源申请的最小个体】

【OS要管理进程,进程是OS内部非常核心的一个概念。可以把OS的职责做个简单的划分:
(1)OS进行CPU协调的模块——进程管理模块:主要负责进程的调度工作(主持不同进程在CPU上运行时间的划分)。实践中,OS需要负责把进程A从CPU上拿下来,把进程B放到CPU上via修改PC寄存器的值,通过修改PC,让CPU去执行属于B的指令
(2)OS进行内存协调的模块——内存管理模块:主持内存空间的分配工作(进程通信的问题)
(3)OS委托文件系统进行抽象管理——文件管理模块。硬盘最为主要的二级存储,是比较重要的一个IO设备——OS进行硬盘上的数据的协调的模块:OS一般不直接去管理硬盘上的数据,一般是委托文件系统(File system)进行抽象管理,把硬盘上的数据抽象成文件(包含文件夹/目录)的概念
(4)网络管理的的模块
(5)其他硬件——以文件驱动的形式进行管理——驱动管理】

2、进程管理(进程调度)
【负责CPU的分配,天然是以时间线作为主轴去理解】
***要做好进程调度,我们面临哪些问题

***1、***需要把进程(程序执行的过程)这个抽象的概念,用数据表示处理——面向对象的思想(变成数据,才能被计算机进行处理(才能被OS这个软件进行处理))
Class Process / PCB (Process Control Block){ //对一个进程的对象化(进程中在管理过程中重要的数据的抽象)

【1、pid (全局唯一)
2、关联程序的信息(哪个进程下的哪个文件程序等)
3、关于运行的一些信息((1)哪个用户启动的(2)进程工作目录(Process work directory pwd)(3)什么时候开始,什么时候结束)
4、分配的资源。CPU:CPU的占用率,之前过去的一段时间里,分配给该进程的时间占比。 内存:分配出去的内存(内存不一定连续)…
5、调度时用到的信息
OS需要管理多个进程,通过数据结构组织起来管理,如(List、Map、Queue)】

***2、***需要对进程做个区分:哪些可以进行分配CPU了,哪些现在暂时还没有准备好。
通过对进程做状态划分,来区分出处于不同情况下的进程——进程状态(状态的转移【随着时间的变化,进程的情况也正在变化】)
(1)新建:进程处于正在创建中
(2)就绪:完事具备,只欠CPU
(3)运行:进程的指令真正在CPU运行着
(4)阻塞:进程由于等待外部条件,所以暂时无法继续
(5)结束:进程的所有指令执行结束,但PCB暂时保留,OS还需要做一些其他工作的时候

**进程的状态转移图(时间维度)(重要)
计算机网络(操作系统)_第2张图片

【所有运行状态必须是从就绪状态变过去的】
【进程的CPU必须经过OS系统分配才能得到】
【细致的划分
(1)->新建 :随着程序的启动运行
(2)新建->就绪:进程的初始化工作全部完成(这个工作时由OS的指令完成的)
(3)就绪->运行:进程被OS选中,并分配了CPU之后
(4)运行->结束:进程的最后一条指令执行结束(粗略地理解 ,就是main方法执行结束了)
(5)运行->就绪:1、被高优先级的进程抢占了。2、时间片耗尽。3、进程可以执行一些OS提供的系统调用,主动放弃。
(6)运行->阻塞:等待一些外部条件:等待IO设备,进程休眠一段时间…
(7)阻塞->就绪:外部条件满足,IO数据来了;休眠时间到了…
(8)结束->:进程PCB彻底被OS回收了】

【站在OS的角度,同一时刻,处于不同的状态下的进程不止一个。
(处于新建:n1个;处于就绪:n2个;处于运行:只有1个(单CPU单核情况下);处于结束:n3个;处于阻塞:n4个)
就绪队列:装有所有处于所有就绪状态的进程,这里装的都是等待被分配CPU的进程。(就绪队列一般就一个)
阻塞队列:等待某些外部条件的进程组成的队列(阻塞队列有很多个,一般每个条件都可以有一个) 】

***3、***现在手上有等待分配CPU的所有进程列表——就绪队列,下一个问题,选择哪个进程上的CPU?要求:要有消息,要保证公平性,要让更紧急的任务被更紧急的处理、低成本解决…
EG:先来先服务、优先级划分(进程PCB中需要管理一个优先级的属性)、短作业优先级…

***4、***OS 什么时候会介入进程调度:需要选择一个新的进程,进行CPU的分配
(1)一个新的进程刚处于就绪状态时,当该进程的优先级较高可以优先处理——具备这种能力的OS被称为抢占式(实时)
(2)运行状态的进程->结束。一个进程就结束了。
(3)OS 每隔一段时间,会调度一次:进程的时间片耗尽
(4)进程主动放弃CPU(1、运行->阻塞 2、运行->就绪)【2、3、4、是CPU空闲下来的时候】

***5、***OS具体怎么进程切换:通过上下文切换(CS Context Switch)——保护上一个进程的上下文+恢复下一个进程的上下文(上下文Context 不是 内容Content)可以用环境这个词去理解。
在进程调度中,上下文指的是:以PC寄存器所代表的一组寄存器中的值。
保护上下文:把寄存器中的值,保存到内存的某个位置。
恢复上下文:把内存中之前保护的值,写入寄存器中。

public static void main(String[] args) {
    
    Scanner scanner =new Scanner(System.in);
    
    scanner.nextLine();
    //这段代码的背后,最终会执行到一个由OS实现的方法
    //进程状态:运行->阻塞(等用户的输入)
    //OS角度:由于要执行OS实现的方法,OS可以干任何事
    //1、修改当前的状态 2、把当前进程放到阻塞队列 3、调度一个新的进程运行
}

【由进程管理,我们引出的一些概念,没有精确的定义,要因地制宜的理解】
1、并行(parallel)与并发(concurrent)
(1)并行:进程真的同时在执行(微观角度的同一时刻,是有多个指令在执行的,所以只会在多CPU多核场景下)
(2)并发:进程假的同时在执行(微观上,表现为一次只执行一个进程,但是宏观上,多个进程在“同时”执行)
2、用户态(user space) VS 内核态(kernel space)
(1)用户态:CPU正在执行的是普通进程的指令时,就在用户态。用户态的性能较好。【用户态的指令权限低(只能访问OS规定的资源)(用户态指令只能访问进程自己的内存)】
(2)内核态:CPU正在执行的是OS的指令时,就进入到内核态。内核态的性能较差。【内核态的指令权限高(所有硬件都能被访问)(内核态指令可以访问所有内存)】
【CPU本身就有权限开关,所有可以做到!!! OS在做管理,管理的核心就是权限的划分】

3、执行流(execution flow)
执行流拥有独立PC的一套指令:不同的执行流从现象上看起来是完全独立的。
内存管理:空间上划分
【内核使用的内存】【分配给进程使用的内存】【空闲空间】空间划分不保证是连续的
【Java应用程序员眼中的内存
JVM的内存空间分为栈区、堆区、方法区…【属于Java程序员和JVM之间的概念】(在一块内存中,JVM作为OS眼中的普通进程,OS分配给JVM进程的内存,JVM又把分到的空间进行隔断(区域的划分:栈区、堆区、方法区),这些是Java程序员眼中的内存。)】

4、、线性地址(虚拟地址) VS 物理地址
(1)线性地址:物理地址被OS进行转换后的一个地址。
(2)物理地址:真实的内存中的地址。
【由OS的内存管理单元(Memory Manage Unit MMU)进行映射】
计算机网络(操作系统)_第3张图片
计算机网络(操作系统)_第4张图片

OS分配出来的空间只是线性地址空间,实际的物理内存,可以延迟到要访问这点内存时再分配。(A找B租房子,需要100套房子。B承诺101到200号房间给A用(只是分配了线性地址出去)。如果A租下房子后没有使用,则B并不需要真正给 A准备100套房子。什么时候A真正需要某个房子,B再去给A找到房间对应的房号即可)

5、进程间通信(Inter-Process Communication /IPC)
(1)进程A和进程B有各自的数据,理论上,进程之间是独立的。但实际中,往往是多个进程之间相互配合来完成复杂的工作的。例如:我们之前使用的MySQL的场景。通过 workbench 进程和 mysql服务器进程进行通信,来实现数据的增删查改。所以,有了进程之间交换数据的必要性。(通信是必要的)

(2)当下的问题:OS进行资源分配是以进程为基本单位进行分配的,包括内存。意味着分配给A进程的内存不会分配给B进程。所以,进程A、B之间直接通过内存来进行数据交换的可能性完全不存在了。【A被隔离】【B被隔离】所以OS需要通过一套机制,用于让A、B进程之间进行必要的数据交换——进程间通信。(内存是隔离的,所以必须需要机制来辅助)

(3)进程间通信的常见方式***(记忆)
A、管道(pipe)
B、消息队列(message queue)
C、信号量(semaphore)
D、信号(signal)
E、共享内存(shared memory)
F、网络(network)——workbench 和 mysql 的通信的方式

6、、在研究OS 是实现时,可能面临的一些其他问题:
(1)、在分配资源时,如何避免出现死锁(dead lock)的问题

计算机网络(操作系统)_第5张图片

三、进程线程(thread)的领域

现在暂时介绍的都是OS系统层面的线程。
1、进程(Process) 和 线程(thread)关系
进程:OS进行资源分配的基本单位。(不包含CPU资源)
线程:OS进行调度的单位。(CPU资源)
进程 <—> 线性是 1 : m的关系
【 一个线程一定属于一个进程;一个进程下可以允许有多个线程
一个进程内至少有一个线程,通常把这个一开始就存在的线程,称为主线程(main thread)
主线程和其他线程之间的地位是完全相等的,没有任何特殊性。】
2、为什么OS要引入thread这一概念
由于进程这个概念天生就是资源隔离的,所以进程之间进行数据通信注定是一个高成本的工作。
现实中,一个任务需要多个执行流一起配合完成,是非常常见的。
所以,需要一种方便数据通信的执行流概念出来,线程就承担了这一职责。
3、什么是线程:
线程是OS进行调度(分配CPU)的基本单位
线程变成了独立执行流的承载概念,进程退化成只是资源(不含CPU)的承载概念。
计算机网络(操作系统)_第6张图片

程序的一次执行过程表现为一个进程,main所在的线程就是主线程。主线程中可以运行对应的操作来创建运行其他线程。

4、只针对OS级别的线程:OS针对同一个进程下的线程实现“连坐”机制(视为一体的);一旦一个线程异常退出,OS会关闭该线程所在的整个进程。

线程VS进程
1、概念区别
2、由于进程把调度单位这一个职责让渡给线程了。所以,使得单纯进程的创建销毁适当简单。
3、由于线程的创建和销毁不涉及资源分配、回收的问题。所以,通常理解线程的创建/销毁成本要低于进程的成本。

接下来的学习针对的都是JVM中规定的线程
1、“Java线程” VS “OS线程(原生线程)”
不同JVM有不同的实现,他们的外在表现基本一致,除了极个别的几个现象。Java线程,一个线程异常关闭,不会连坐。我们使用的HotSpot实现(JVM)采用,使用一个OS线程来实现一个Java线程。
2、Java中有JVM的存在,所以使得Java中做多进程级别的开发基本很少。Java中还克服了很多OS线程的缺点。所以,在Java开发中,我们使用多线程模型来进行开发,很少使用多进程模型。

Java线程在代码中是如何体现的
1、Java.lang.Thread类(包括其子类)的一个对象 Thread —— 线程
2、如何在代码中创建线程(最基本)
(1)通过继承Thread类,并且重写run方法
实例化该类的对象 ->Thread对象。
(2)通过实现Runnable 接口,并且重写run方法。实例化Runnable对象。利用Runnable对象去创建一个Thread对象。(Runnable —— 让这个线程去完成的工作(任务))
3、启动线程
当手中有一个Thread对象时调用其start()方法
注意:(1)一个已经调用过start(),不能再调用start()了,再次调用就会有异常发生。
在这里插入图片描述

【非法的线程状态异常,t.start()只允许工作在“新建状态下”】
(2)千万不要调成run(),调用run方法,就和线程没关系了,完全是在主线程下在运行代码。


 public static void main(String[] args) {
        MyThread t = new MyThread();

//        t.start();    // 让 t 线程,从新建 -> 就绪状态,等待被调度
                        // t 被调度器选中之后,才会执行
        t.run();        // 在主线程中,和之前学过的 java 一样,就是一个简单的方法调用
    }
}

【我们写的无论是Thread的子类还是Runnable的实现类,只是给线程启动的“程序”。所以,同一个程序,可以启动多个线程】

什么情况下会出现线程调度(开始选择一个新的线程分配CPU)
1、CPU空闲
(1)当前运行的CPU执行结束了 ,运行->结束
(2)等待外部条件(I/O) ,运行->阻塞
(3)主动放弃 ,运行->就绪
2、被调度器主动调度
(1)高优先级线程抢占
(2)时间片耗尽(这个情况最常见)
在多线程中,明明代码是固定的,但会出现现象是随机的可能性,主要原因就是调度的随机性体现在线程的运行过程中!!!

你可能感兴趣的:(JavaEE,java)