计算机和OS启动过程:https://www.cnblogs.com/z-sm/p/5657914.html
CPU中断:https://www.cnblogs.com/z-sm/p/15162076.html
OS进程调度:https://www.cnblogs.com/z-sm/p/15200785.html
OS多路复用IO:https://www.cnblogs.com/z-sm/p/6680141.html
关于Linux的历史和内部原理的入门书籍,推荐阅读 Linux内核v0.1.2完全注释 一书,前面几章对Linux的发展过程、版本变化等做了详细的介绍。
20170104
冯诺依曼计算机(遵循冯诺依曼结构设计的计算机:存储器、运算器、控制器、输入设备、输出设备)之前也有计算机,不过在那之前的计算机是专用的,不可编程,只能干特定的事情没法干其他事。与之前计算机的一个不同在于冯诺依曼计算机是通用计算机,可以干很多事情,并且程序是可存储的(“存储程序”),多个程序可以存储在计算机中。
20170328
1、典型的系统硬件组成结构
现代计算机组成中,IO bridge分为北、南桥两部分,分别称为MCH(Memory Controller Hub)、ICH(IO Controller Hub),分别用来管理高速(如内存等)、中低速组件(如磁盘、USB等)。
2、存储设备层次结构
典型 PC 机上通常含有三种类型的存储器,一种是用来运行程序和临时保存数据的内存存储器,一种是存放着系统开机诊断和初始化硬件程序(即BIOS)的 ROM,另一种是用来存放存计算机实时时钟信息和系统硬件 配置信息的少量 CMOS 存储器。
为什么叫主存(main memory)?因为CPU地址总线控制的不止是通常所认为的内存,还有显存等,这里面最常用的是通常认为的内存,故叫主存?
速度级别:
硬盘:毫秒级
内存:纳秒级
3、OS提供的抽象表示
1、进程、线程。并行化:线程级并行(多核或超线程)、指令级并行、单指令多数据(SIMD)
2、虚拟存储器
————
(从上可见,栈从上向下增长,故亦称下推表)
20170427
精简指令集/复杂指令集的CPU架构:
RISC: MIPS架构、PowerPC架构(IBM)、ARM架构(ARM)、SPARC架构(Sun,后被Oracle收购) 等的CPU用RISC
CISC:X86架构(Intel、AMD、VIA等) 等的CPU用CISC
题外话:Intel 1代为8位机(8080、8085)、2代16位机(8086)、3代为32位机(80386)、4代及目前最新为64位机。64位已经远远够用了,所以位长应该不会再增了。
20170428
计算机内部负整数为什么用补码?
原因是:只有这种形式,计算机才能实现正确的加减法。
计算机其实只能做加法,1-1其实是1+(-1)。如果用原码表示,计算结果是不对的。比如说:
1 -> 00000001
-1 -> 10000001
+ ------------------
-2 -> 10000010
用符合直觉的原码表示,1-1的结果是-2。
如果是补码表示:
1 -> 00000001
-1 -> 11111111
+ ------------------
0 -> 00000000
结果是正确的。
计算机浮点数运算为什么不准确?
如C下 printf("%.10f\n",0.1f*0.1f); 为0.0100000007,Java下 System.out.println(0.1f* 0.1f); 为0.010000001,虽然保留位数少的话可得期望值,但其实真正的计算结果有很多位,所以计算结果不是我们期望的。
原因:运算本身没错,而是计算机不能精确表示很多数,比如0.1,数都不能精确表示当然运算结果也不准确了。为什么不能精确表示呢——与十进制只能表示10的若干次方和的数一样,采用二进制的计算机只能表示2的若干次方和的数,前者不能准确表示无限小数如1/3,后者除不能表示无限小数外还有一些数也不能准确表示如0.1。
处理不精确:如果要求的精度不高,可以四舍五入;否则可以将小数转化为整数进行运算,算完再转化为小数。
20170719
1、CPU视角看计算机启动过程(见 CPU阿甘——码农翻身)
2、CPU视角看程序装载运行过程(见 CPU阿甘之烦恼——码农翻身):地址重定位,分页、工作集、页表、缺页中断,分段、段表、段错误
3、进程、线程实现(见 我是一个进程——码农翻身)
4、死锁相关(见 我是一个线程——码农翻身)
5、同步和锁
同步和互斥问题(见 那些烦人的同步和互斥问题——码农翻身):脱机打印、信号量、生产者消费者问题(通过信号量解决)
1、产生死锁的原因主要是:
(1)因为系统资源不足。
(2)进程运行推进的顺序不合适。
(3)资源分配不当等。
如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则就会因争夺有限的资源而陷入死锁。其次,进程运行推进顺序与速度不同,也可能产生死锁。
2、产生死锁的四个必要条件:
(1)互斥条件:一个资源每次只能被一个进程使用。
(2)请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3)不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4)循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。
3、死锁的预防:
(1)资源一次性分配:(破坏请求和保持条件)
(2)可剥夺资源:即当某进程新的资源未满足时,释放已占有的资源(破坏不可剥夺条件)
(3)资源有序分配法:系统给每类资源赋予一个编号,每一个进程按编号递增的顺序请求资源,释放则相反(破坏循环等待条件)
4、死锁的避免:(银行家算法)
预防死锁的几种策略,会严重地损害系统性能。因此在避免死锁时,要施加较弱的限制,从而获得较满意的系统性能。由于在避免死锁的策略中,允许进程动态地申请资源。因而,系统在进行资源分配之前预先计算资源分配的安全性。若此次分配不会导致系统进入不安全状态,则将资源分配给进程;否则,进程等待。其中最具有代表性的避免死锁算法是银行家算法。
5、死锁的检测:进程等待图(wait graph)看是否有环
6、死锁的解除:
(1)剥夺资源:从其它进程剥夺足够数量的资源给死锁进程,以解除死锁状态;
(2)撤消进程:可以直接撤消死锁进程或撤消代价最小的进程,直至有足够的资源可用,死锁状态.消除为止;所谓代价是指优先级、运行代价、进程的重要性和价值等
页面置换算法:(详见:页面置换算法])
总结:
最佳置换算法
基于队列:FIFO。实现简单,时间效率高;效果差(缺页多),Belady异常
基于栈:LRU。与FIFO比:实现较复杂,时间效率低;效果好,没有Belady异常
LFU,与LRU类似
ClOCK:时间开销比LRU小但效果接近LRU
1、最佳置换算法(OPT)
置换算法所选择的被淘汰页面将是以后永不使用的,或者是在最长时间内不再被访问的页面,这样可以保证获得最低的缺页率。由于人们目前无法预知进程在内存下的若干页面中哪个是未来最长时间内不再被访问的,因而该算法无法实现。
示例:(9次缺页中断、6次页面置换)
2、先进先出置换算法(FIFO)
基于队列的算法。优先淘汰最早进入内存的页面,亦即在内存中驻留时间最久的页面。该算法实现简单,只需把调入内存的页面根据先后次序链接成队列,设置一个指针总指向最早的页面。但该算法与进程实际运行时的规律不适应,因为在进程中,有的页面经常被访问。FIFO算法还会产生当所分配的物理块数增大而页故障数不减反增的异常现象(由 Belady于1969年发现,故称为Belady异常)。
示例:(15次缺页中断、12次页面置换)
3、最近最久未使用置换算法(LRU)
基于栈的算法。选择最近最长时间未访问过的页面予以淘汰,它认为过去一段时间内未访问过的页面,在最近的将来可能也不会被访问。LRU性能较好,但需要寄存器和栈的硬件支持。LRU是堆栈类的算法。理论上可以证明,堆栈类算法不可能出现Belady异常。FIFO算法基于队列实现,不是堆栈类算法。
示例:(12次缺页中断、9次页面置换)
基于优先队列实现LRU:假设优先级越高越不应被删除,则对于要访问的目标数据(访问的页)
若缓存(三个物理块)中存在,则从缓存中访问该页,并把该页置为最高优先级;若不存在,则从缓存移除优先级最低的(最长时间未使用的物理块中到)页、把新页加入缓存,并把新页面置为最高优先级。
基于LinkedHashMap实现LRU:实际上就是对上述基于优先队列方案的实现,LinkedHashMap的entry的entry按加入的顺序保存,故“晚加入”相当于上面的“高优先级”。对于某个数据:
若LinkedHashMap中存在该元素,则访问删除该元素然后重新添加到map;若不存在,则直接加入(未满时)或 移除第一个entry并将元素加入(满时)到map。代码:
abstract class DbUtilWithLruCache<T> { private final int cacheCapacity = 10; private LinkedHashMap<String, T> lruCache = new LinkedHashMap<>();// LinkedHashMap具有FIFO的特点,借助之来维护优先级,越晚添加者(越靠后)优先级越高 // 添加缓存数据,添加的数据优先级最高 private void setToLruCache(String key, T val) { if (lruCache.size() == cacheCapacity) { Iterator<?> it = lruCache.entrySet().iterator(); lruCache.remove(it.next()); } lruCache.put(key, val); } // 提高指定缓存数据的优先级 private void increasePriority(String key, T val) { // 缓存中移除 lruCache.remove(key); // 新加到缓存 lruCache.put(key, val); } protected abstract T getValFromDB(String key); protected abstract void setValToDB(String key, T val); /** 获取数据 */ public synchronized T get(String key) { T val = lruCache.get(key); if (null == val) {// 不存在:从db取、更新缓存 // 从真实位置取得数据 val = getValFromDB(key); // 加到缓存 setToLruCache(key, val); } else {// 存在:提高优先级 increasePriority(key, val); } return val; } /** 存数据 */ public synchronized void set(String key, T val) { T oldVal = lruCache.get(key); if (oldVal != val) {//数据变化:存到db、更新缓存 setValToDB(key, val); setToLruCache(key, val); } else {//数据没变化:提高优先级 increasePriority(key, val); } }