Java开发者需要了解的硬件知识 (二)、操作系统篇

前言:

上一篇讲了CPU,作为整个计算机的核心计算硬件,讲解了它于JAVA语言间的瓜葛。这一篇讲讲计算机软件里的老大哥OS ----操作系统。

因为学习内容里有些不那么重要的知识点,往往就是截图或者少量文字带过,个人笔记不会记录那么多细节,详细资料请读者自己查询,见谅。

 

正文:

计算机的启动过程

通电 -> bios uefi 工作 -> 自检 -> 到硬盘固定位置加载bootloader -> 读取可配置信息 -> CMOS

img

 

OS 操作系统

内核分类

微内核 - 弹性部署 5G IoT

宏内核 - PC phone

外核 - 科研 实验中 为应用定制操作系统 (多租户 request-based GC JVM)

 

用户态和内核态

cpu分不同的指令级别

linux内核跑在ring 0级, 用户程序跑在ring 3,对于系统的关键访问,需要经过kernel的同意,保证系统健壮性

内核执行的操作 - > 200多个内核方法,通过系统调用 sendfile read write pthread fork

JVM -> 站在OS老大的角度,就是个普通程序

 

进程 线程 纤程(协程) 中断

面试高频:进程和线程有什么区别?

专业答案:进程是OS分配资源的基本单位线程是执行调度的基本单位

进程最重要的分配资源是:独立的内存空间线程调度执行(线程共享进程的内存空间,没有自己独立的内存空间

 

 

 

进程

Linux中也称为task,是系统分配资源的基本单位

资源包括:独立的地址空间,内核数据结构(进程描述符),全局变量, 数据段。。

Linux进程描述符:PCB (Process Control Block),用于Linux的进程管理(线程有他的PCB)

进程在Linux中的创建与启动

调用系统函数 fork()创建进程 ,exec()启动进程

从 A 中fork B 的话,A称之为B的父进程,B称之为A的子进程

僵尸进程

ps -ef |grep defunct (defunct表示无用的僵尸进程)

父进程产生子进程后,会维护子进程的PCB结构,子进程退出后,由父进程释放,如果父进程没有释放,那么子进程会成为一个僵尸进程(defunct)

孤儿进程

子进程结束之前,父进程已经退出,就会产生孤儿进程,孤儿进程会成为Init进程的子进程,由1号进程维护 (在图形化Linux中是1457号线程)

进程调度

内核进程调度器决定该哪个进程运行,何时开始,运行多长时间。

每个进程都可以自定义不同的调度方案

多任务执行分为

  • 抢占式(大多数现代系统采用):由进程调度器强制开始或暂停(抢占)某一进程的执行

  • 非抢占式:除非进程主动让出(yielding)cpu,否则将一直运行

调度策略

Linux2.6采用CFS调度策略:Completely Fair Scheduler

按优先级分配时间片的比例,记录每个进程的执行时间,如果有一个进程执行时间不到他应该分配的比例,优先执行

默认调度策略:

  • 实时进程 : 优先级分高低 - FIFO (First In First Out),优先级一样 - RR(Round Robin)

  • 普通:CFS

线程在Linux中的实现

线程在Linux就是一个普通的进程,只不过和其他进程共享资源(内存空间,全局数据等。。。)

其他系统都有各自所谓的LWP的实现 Light Weight Process

内核线程

内核启动后需要做一些后台操作,这些操作由Kernel Thread 来完成,它只在内核空间中运行

在JVM中的线程

创建一个JVM中线程,即申请一个操作系统的线程(重量级线程),1:1关系

 

纤程 Fiber

纤程可以理解为线程里面的线程,用户态的线程而非内核态的线程。

它处于线程内部,非常轻量级,可以在线程中快速切换。JVM自己管理,自己切换,与操作系统无关。

优势:

  1. 占有资源很少 ,OS 线程要1M ,而Fiber只需要4K

  2. 切换比较简单

  3. 可以启动很多个纤程,可以达到10W+

目前2020-03-22支持内置纤程的语言:Kotlin Scala Go Python(lib)...... Java14并没有(open jdk : loom库)

Java中对于纤程的支持:没有内置,盼望内置

纤程的应用场景

纤程 vs 线程池: 纤程适合很短的计算任务, 不需要和内核打交道,并发量高 的场景

 

中断

硬中断

硬件跟操作系统内核打交道的一种机制

软中断(80中断)

系统调用:int 0x80 或者 sysenter原语

通过ax寄存器填入调用号(调用号指定了对应的内核方法),参数通过bx cx dx si di 寄存器传入内核,最后返回值通过ax返回

java的一个中断例子

java读网络 –> jvm read() –> c库read() -> 内核空间 -> system_call() (系统调用处理程序)-> sys_read()

 

 

 

 

 

内存管理

DOS时代 - 同一时间只能有一个进程在运行(也有一些特殊算法可以支持多进程)

windows9x - 多个进程装入内存,如此就会出现两个问题

  • 内存不够用

  • 互相打扰

为了解决这两个问题,诞生了现在的内存管理系统:虚拟地址 分页装入 软硬件结合寻址

  • 分页(内存不够用),内存中分成固定大小的页框(4K),把程序(硬盘上)分成4K大小的块,用到哪一块,加载那一块,加载的过程中,如果内存已经满了,会把最不常用的一块放到swap分区(即交换区,系统运行内存不够时,与Swap进行交换), 把最新的一块加载进来,这个就是著名的LRU算法

    1. LRU算法 LeetCode146题Least Recently Used 算法(翻译为最不常用)

    2. 哈希表(保证 查找操作复杂度为O(1)) + 链表 (保证 排序操作和新增操作复杂度为 O(1)))

    3. 双向链表 (保证查找效率)

 

 

 

  • 虚拟内存(解决相互打扰问题)

    1. DOS Win31 ... 这些旧的系统,都会直接操作内存,可能导致进程间互相干掉对方

    2. 为了保证互不影响 - 让进程工作在虚拟空间,程序中用到的空间地址不再是直接的物理地址,而是虚拟的地址,这样,A进程永远不可能访问到B进程的空间

    3. 虚拟空间多大呢?寻址空间 - 64位系统 2 ^ 64,比物理空间大很多 ,单位是byte

    4. 站在虚拟的角度,进程是独享整个系统 + CPU

    5. 内存映射:偏移量 + 段的基地址 = 线性地址 (虚拟空间)

    6. 线性地址通过 OS + MMU(硬件 Memory Management Unit),得到对应的物理内存地址

 

 

  • 缺页中断(不是很重要):

    1. 需要用到页面内存中没有,产生缺页异常(中断),由内核处理并加载

 

posted @ 2020-08-19 18:41  Rooooy7  阅读(328)  评论(0编辑  收藏  举报