操作系统常见问题
1.操作系统的内存管理机制
- 操作系统使用虚拟地址空间(Virtual address space)来进行物理内存的隔离,每个进程都有自己的虚拟地址空间,每个进程只能访问自己的虚拟地址空间。这样就有效的实现了隔离。
- 地址空间,物理地址空间是真实存在的,每个计算机都有唯一的物理地址空间地址。程序在运行的时候,会进行虚拟地址空间到物理地址空间的转换。这个行为操作系统通过文件系统这个中间层进行转换。
文件系统
- 分段
基本思路是把一段程序所需要的内存空间大小的虚拟地址空间映射到某个物理地址空间。
分段解决了地址隔离和重定向的问题,但是内存使用效率低下的问题依然没有解决。如果物理空间使用不足,那么整个程序依然会被换入换出。 - 分页
把物理地址空间人为的分为大小固定的页,每一页的大小由硬件决定,不同的硬件支持不同大小的页,同时操作系统可以选择使用多大的页(在硬件支持的情况下),但是每一时刻只能选择一种大小。·
虚拟空间中的页面称为虚拟页,物理地址空间的页面称为地址页。这样,不同的虚拟页就可以映射到相同的物理页,实现物理内存的共享,提高内存使用效率。
2.进程和线程
2.1 进程和线程的区别
- 进程(process):
一个程序在一个数据集上的一次运行过程。系统资源分配的单位。进程是独立的,有自己的内存空间和上下文环境,无法获取其他进程的存储空间。同一进程的两段代码不能同时执行,除非引入线程。 - 线程(thread):
线程是一个实体,是被系统调度和执行的基本单位,CPU调度的基本单位。
线程是进程的一部分,一个线程只能属于一个进程,一个进程可以有多个线程
2.2 线程的状态和切换
2.3 线程的同步
- 互斥量: 采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问。
- 信号量: 允许多个线程访问同一资源,但需要控制同一时刻访问此资源的最大线程数量。
- 事件(信号):通过通知操作的方式来保持多线程同步,还能方便实现多线程优先级的比较操作
- 自旋锁多处理器:获取/释放自旋锁;单处理器:禁止/使用内核抢占
2.4 线程的调度
线程调度指的是系统为线程分配CPU使用权的方式。主要有协同式线程调度和抢占式线程调度。
- 协同式线程调度
线程的执行时间由线程自身控制,执行结束后要主动通知系统切换到另一线程。
不会涉及到线程同步问题 - 抢占式线程调度
线程由系统来分配执行时间,线程的切换不由线程本身控制。(java中的yield()方法可以主动让出执行时间,但是无法主动获取执行时间),涉及到上下文的保存,线程同步等问题。
java使用的线程调度是抢占式调度。
java中线程会按优先级分配CPU时间片运行。
3.死锁
指两个或两个以上的线程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。
3.1产生的原因
- 竞争资源引起进程死锁
- 竞争不可剥夺资源(例如:系统中只有一台打印机,可供进程P1使用,假定P1已占用了打印机,若P2继续要求打印机打印将阻塞)
- 指的是竞争临时资源(临时资源包括硬件中断、信号、消息、缓冲区内的消息等),通常消息通信顺序进行不当,则会产生死锁
- 进程间推进顺序非法
进程A持有资源1,进程B持有资源2。接下来A想要拥有2,B想要拥有1。产生死锁
3.2产生的必要条件
- 互斥条件
- 请求和保持条件
- 不可剥夺条件
- 环路等待条件
3.3预防死锁
- 资源一次性分配:一次性分配所有的资源
- 只要有一个资源得不到,就不给这个进程分配其他资源
- 当某进程拥有部分资源,但是无法得到另外的资源。则释放已经拥有的资源
- 资源有序分配: 系统给每个资源编号,每一个进程按边好递增的顺序请求资源,释放则相反。
java中预防死锁
- 以确定的顺序获得锁
例如将锁排序(利用HashCode)、利用银行家算法使用特定的顺序获得锁 - 超时放弃
使用synchronized关键词提供的内置锁时,只要线程没有获得锁,那么就会永远等待下去
使用Lock接口中的tryLock()方法,可以按照固定的时长等待锁,超时后放弃已经拥有的锁
Socket的三种通信模型-BIO、NIO和AIO
BIO 同步阻塞IO
服务器为每一个连接启动一个线程处理,高并发情况下线程数量急剧增加,性能下降甚至崩溃
可通过FixedThreadPool线程池(固定数量可重用线程池)的方式实现多个线程处理N个客户端。底层还是BIO。
适合用于连接数目小且固定
NIO 同步非阻塞IO
基于事件驱动思想。服务器为每一个请求启动一个线程处理。客户端发送的连接请求都会注册到多路复用器上,客户端会不断询问是否就绪,造成了cpu的浪费。当多路复用器轮询到连接有IO请求时才启动线程处理。
适合用于连接数目多,连接比较短的项目(聊天服务器)。
- 缓冲区 Buffer
Buffer是一个对象,本质是一个数组,提供对数据结构化访问以及维护读写位置等信息
具体有 ByteBuffer、CharBuffer、IntBuffer、LongBuffer\FloatBuffer、DoubleBuffer。 - 通道 Channel
Channel与流不同,是双向的,可以实现读写和同时读写。是全双工的。注册在Selector上
Channel主要分为两大类:SelectableChannel(用户网络读写)、FileChannel(文件独读) - 多路复用器 Selector
提供选择以及就绪任务的能力,不断轮询Channel,如果某个Channel发生读写,则其就绪,被轮询处理,进行后续 的IO操作。
AIO 异步非阻塞IO
一个有效请求一个线程,当进行读写操作时,调用API的read或者writer方法,读写异步,读和写的流被分别写入缓冲区,完成后主动调用回调函数。
适合连接数目多且连接较长的项目(相册服务器)
同步、阻塞理解:
- 同步 : 自己亲自出马持银行卡到银行取钱(使用同步IO时,Java自己处理IO读写);
- 异步 : 委托一小弟拿银行卡到银行取钱,然后给你(使用异步IO时,Java将IO读写委托给OS处理,需要将数据缓冲区地址和大小传给OS(银行卡和密码),OS需要支持异步IO操作API);
- 阻塞 : ATM排队取款,你只能等待(使用阻塞IO时,Java调用会一直阻塞到读写完成才返回);
- 非阻塞 : 柜台取款,取个号,然后坐在椅子上做其它事,等号广播会通知你办理,没到号你就不能去,你可以不断问大堂经理排到了没有,大堂经理如果说还没到你就不能去(使用非阻塞IO时,如果不能读写Java调用会马上返回,当IO事件分发器会通知可读写时再继续进行读写,不断循环直到读写完成)
同步阻塞IO(VIO) | 非阻塞IO(NIO) | 异步IO(AIO) | |
---|---|---|---|
客户端个数:IO线程 | 1:1 | M:1(1个IO线程处理多个客户端连接) | M:0(不需要启动额外的IO线程,被动回调) |
IO类型(阻塞) | 阻塞 | 非阻塞 | 非阻塞 |
IO类型(同步) | 同步 | 同步 | 异步 |
API使用难度 | 简单 | 简单 | 复杂 |
调试难度 | 简单 | 简单 | 复杂 |
可靠性 | 非常差 | 差 | 高 |
吞吐量 | 低 | 中 | 高 |