计算机是如何工作的——操作系统

操作系统(Operating System/OS)

1.多用户——多任务场景
2.OS是一个管理(硬件/软件)资源的软件,主要职责就是协调——专门的软件来做协调工作,协调不同任务队硬件资源的访问,管理着硬件资源,硬件资源都需要找OS申请后才被授权使用。

区分程序(Program)和进程(Process)/任务/作业

程序

是一套做数据处理的步骤,是静态。

**进程

从用户视角看,进程是程序的一次执行过程,是动态的。(如果把程序看成菜谱,进程就是一次做菜的过程)
从OS实现的角度看,进程是OS进行资源分配的最小单位,最小实体,换言之,同一个进程中的资源是共享的(如果存在比进程更低级的单位,是共享的),不同进程之间的资源是隔离的。
总结一下吧,1.动态过程 2.绑定一个程序 3.允许同时存在 4.OS内部进行资源的最小个体

关系

1.一个程序可以进行多次执行(表现为多个进程),甚至可以“同时”执行。
2.进程是程序在“运行阶段”的视角主体。

OS的职责

1.OS进行CPU协调的模块——进程管理模块
主要负责进程的调度工作,即主持不同进程在CPU运行的时间划分
实践中,OS需要负责把进程A从CPU上拿下来,把进程B放到CPU上通过修改PC寄存器的值。
2.OS进行内存协调的模块——内存管理模块
支持内存空间的分配工作
3.文件管理模块
硬盘最位主要的二级存储,是比较重要的一个IO设备——OS进行硬盘上的数据的协调的模块
OS一般不直接去管理硬盘上的数据,一般是委托文件系统(Filesystem)进行抽象管理,把硬盘上的数据抽象成文件(包含文件/目录)的概念。
4.网络管理模块
5.其他管理——以文件驱动形式进行管理——驱动管理

进程管理/进程调度

负责CPU的分配,天然是以时间线作为主轴

怎么做好进程调度?
1.

需要把进程(程序执行的过程)这个抽象的概念,用数据表示处理——面向对象的思想变成数据,才能被计算机进行处理

2.需要对进程做个区分

通过对进程做状态划分,来区分出处于不同情况下的进程——进程状态
分为五个状态:
新建(进程处于正在创建中),就绪(只等CPU),运行(进程的指令真正在CPU运行),阻塞(进程由于等待外部条件,所以暂时无法继续),结束(进程的所有指令执行结束,但PCB暂时保留,OS还需要一些其他工作的时候)
**进程状态转移图
在这里插入图片描述

注意:1.所有运行状态必须是从就绪状态变过去的;2.进程的CPU必须经过OS系统分配才能得到。
新建状态:随着程序的启动运行

新建->就绪:进程的初始化工作全完成
就绪->运行:进程被OS选中,并分配了CPU之后
运行->结束:进程的最后一条指令执行结束(就是main方法执行结束了)
运行->就绪:1.被高优先级的进程抢占了 2.时间片耗尽 3.进程可以执行一些OS提供的系统调用,主动放弃
运行->阻塞:等待一些外部条件(等待IO设备);进程休眠一段时间;
阻塞->就绪:外部条件满足(IO数据来了);休眠
结束->:进程PCB彻底被OS回收了。

就绪队列:装有所有处于就绪状态的进程,这里装的都是等待被分配CPU的进程(一般就一个)
阻塞队列:等待某些外部条件的进程组成的队列(一般每个条件都可以有一个)

3.现在手上有等待分配CPU的所有进程列表——就绪队列,那么选择哪个进程上CPU

通过——
先来先服务
优先级划分(进程PCB中需要一个优先级的属性)
短作业优先级
等等方式来选择

4.OS什么时候会介入进程调度

需要选择一个新的进程,进行CPU分配
①.一个新的进程刚处于就绪状态时,当该进程的优先级高时——具备这种能力的OS被称为抢占式(实时)
②运行状态的进程->结束。一个进程就结束了
③OS每隔一段时间,会调度一次:进程的时间片耗尽
④进程主动放弃CPU:运行->阻塞 ,运行->就绪

5.OS具体怎么进程切换

通过上下文切换:保护上一个进程的上下文+恢复下一个进程的上下文

上下文指的是:以PC寄存器所代表的一组寄存器中的值
保护上下文:把寄存器中的值,保护到内存的某个位置
恢复上下文:把内存中之间保护的值,写入寄存器中
在这里插入图片描述

并行(parallel)与并发(concurrent)

并行:进程真的同时在执行(微观角度的同一时刻,是有多个指令在执行,所以只会在多CPU多核场景下)
并发:进程假的同时在执行(微观上,表现为一次执行一个进程,但宏观上多个进程在“同时”执行)

用户态(user space)与 内核态(kernel space)

CPU正在执行的是OS的指令时,就进入到内核态。反之,正在执行的是普通进程的指令时,就在用户态。
在这里插入图片描述

管理的核心就是权限的划分。
内核态的指令权限高(所有的硬件都能访问)
用户态的指令权限低(只有访问OS规定的资源)

结论知识:
用户态的性能较好,内核态的性能较差

执行流(execution flow)

拥有独立PC的一套指令;不同的执行流从现象上看起来是完全独立的。

内存管理

是按照空间上划分的

线性地址(虚拟地址)与物理地址

物理地址:真实的内存中的地址
线性地址:物理地址被OS进行转换(映射)后的一个地址。

OS分配出来的空间只是线性地址空间,实际的物理地址,可以延迟到要访问这段内存时再分配,

进程间通信(Inter-Ptocess Communication/IPC)

理论上,进程之间是独立的,但实际上,往往是多个进程之间互相配合,来完成复杂的工作。所以进程之间的数据交换是很有必要的

进程间通信达常见方式:
1.管道(pipe)
2.消息队列(message queue)
3.信号量(semaphore)
4.信号(signal)
5.共享内存(shared memory)
6.网络(network)

内存管理中主要研究的问题

1.管理哪些内存已经被分配,哪些内存暂时未被分配
2.已经分配出去的内存,何时进行回收,如何经行回收
3.物理地址<->线性地址转换
4.内存碎片

研究OS时 ,会面临的问题

1.在分配资源时,如何避免出现死锁
(哲学家吃饭问题——银行家算法)

线程(thread)

(这里说的时OS系统层面的线程)
线程是OS进行调度的基本单位
只针对OS级别的线程:
OS针对同一个进程下的线程实现“连坐”机制:一旦一个线程异常退出,OS会关闭该线程所在的整个进程。

进程与线程的关系

进程:线程=1:m
一个线程一定属于一个进程;一个进程下可以有多个线程。
一个进程内至少有一个线程,通常被这个一开始就存在的线程,称为主线程(main thread)
区别:
1.概念区别
2.
3.

为啥OS要引入thread

由于进程这一概念天生就是资源隔离的,所以线程之间进行数据通信注定是一个高成本的工作。
每一个任务多个执行流来配合进行,需要一种方便数据通信的执行流,就是线程来承担

如何在代码中创建线程(最基本)
1.通过继承Thread类,并且重run方法。实例化该类的对象-thread对象
2.通过实现Runnable 接口,并且重写run方法。实例化Runnable对象。
利用Runnable对象去构建一Thread
(Thread——线程,Runnable——让这个线程去完成的工作(任务))
3.启动线程
在这里插入图片描述

public class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println("正在执行起来的");
    }
}
public class Main {
    public static void main(String[] args) {
        MyThread t= new MyThread();
        //通过调用Thread对象start方法,来开始线程的运行
        t.start();
    }
}

当手里有一个Thread对象调用其start()方法
注意:1.一个已经调用过start()不能再调用start()了,否则就会出现非法的线程状态异常,再调用就会有异常发生。
2.千万不要调用run(),否则就和线程没关系了,完全是在主线程下运行的;
例如:

public class MyThread extends  Thread{
    @Override
    public void run() {
        System.out.println("我是"+Thread.currentThread().getName());
    }
}
public class Main {
    public static void main(String[] args) {
        MyThread t=new MyThread();
        t.start();
        t.run();
    }
}

得到的结果是
在这里插入图片描述

public class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println("我是MyThread类下的run方法中的语句,会运行在子线程中");
    }
}

public class Main {
    public static void main(String[] args) {
        System.out.println("请猜猜是 主线程的打印先出现 还是 子线程的打印先出来 这个顺序是确定的吗");
        MyThread t=new MyThread();
        t.start();
        System.out.println("我是Mian类下的main静态方法中的语句,会运行在主线程中");
    }
}

哪个先打印是不确定的,但大概率情况下是main中的语句先打印。
怎么理解t.start()做了什么
t.start()就是把线程的状态从新建变成就绪。
线程把加入到线程调度器(不区分是OS还是JVM实现)的就绪队列中,等待被调度器选中分配CPU。
从子线程进入到就绪队列这一刻起,子线程和主线程在地位上是一样,所以
先后是不一定的。

因为t.start()是主线程上的语句,当他被执行时,说明主线程现在正在CPU上(主线程是运行状态),所以,主线刚刚执行完t.strat()就马上发生线程调度的概率不太,所以大概率情况下是main中的语句执行。

子线程先执行是咋来的?
1.很碰巧的在t.start()之后,发生了一次线程调度
2.主线程的状态从运行->就绪了,主线程不再持有CPU。意味着主线程的下一条语句不再执行
3.调取时,选中子线程调度,子线程的状态 从就绪->运行
子线程就持有CPU了,执行到子线程。

什么情况下会出现线程调度(开始选择一个新的线程分配CPU)
1.CPU空闲
①当前运行着的CPU执行结束了 运行->结束
②当前运行着的CPU等待外部条件 运行->阻塞
③当前运行着的CPU主动放弃 运行->就绪
2.被调度器主动调度
①高优先级线程抢占
②时间片耗尽

在多线程中,明明代码是固定的,但会出现现象是随机的可能性,主要原因就是调度的随机性体现在线程的运行过程中

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值