64位和32位的区别
-
运行能力不同:64位CPU一次可以处理8个字节的数据;32位CPU一次可以处理4个字节的数据
-
内存寻址能力不同:64CPU的最大寻址空间为2^64,理论可达16TB,64位CPU会设有不同的地址总线的数量,对应不同大小的寻址空间;32位CPU的最大寻址空间为4GB
-
运行软件不同:32位和64位CPU的指令集不同,64位操作系统可以兼容运行32位的软件,32位的操作系统不可以运行64位的软件
Linux下的线程
-
Linux实现的是基于核心轻量级进程的“一对一”线程模型,一个线程实体对应一个核心轻量级进程,线程之间的管理在核外函数库中实现
进程和线程的区别
-
进程是资源调度和分配的基本单位,线程是CPU调度和分派的基本单位
-
一个进程可以拥有多个线程
-
进程是程序的一次执行,线程是进程中的代码段,进程通过线程同时运行不同段的代码
-
进程切换需要保存CPU现场信息并恢复要切换来的进程的CPU现场信息,开销比较大;线程切换只需要切换寄存器信息,开销比较小
-
进程拥有独立的内存空间,线程拥有独立的栈,共享内存空间
系统线程数量上限
-
/usr/include/bits/local_lim.h中查看
-
对Linux threads来说,这个值一般是1024;对NPTL来说没有硬性限制,受限于线程栈占用的内存,一般默认线程栈是8M
杀死一个进程
-
kill pid:系统向程序发送信号,程序接收到信号后先释放资源,再关闭程序
-
kill -9 pid:-9表示强制执行
Linux下的IO模型
-
阻塞IO,当前IO操作未完成时不能执行其它的操作
-
非阻塞IO,轮询机制,当前数据报没有准备好的时候,也不会阻塞程序
-
IO多路复用:一个进程监控多个文件描述符的机制,本质上也是阻塞IO,效率比阻塞IO高
-
信号驱动IO模型:内核准备好数据之后通知进程,进程调用信号处理函数来获取数据报
-
异步IO模型:内核一方面去取数据报内容,一方面将程序控制权还给应用进程,应用进程继续处理其他事务,非阻塞状态
同步&异步,阻塞&非阻塞
-
同步和异步:消息通知机制
-
同步:处理者自己等待消息是否被触发
-
异步:触发机制通知处理者
-
-
阻塞和非阻塞:程序等待消息通知时的状态(能不能干其它任务)
-
阻塞:等待时挂起
-
非阻塞:等待时继续执行其它任务
-
-
同步非阻塞:需要在两个任务之间来回切换,检查等待的任务是否完成,效率比较低下
-
举例
-
同步阻塞:小明一直盯着下载进度条,到 100% 的时候就完成。
同步体现在:等待下载完成通知; 阻塞体现在:等待下载完成通知过程中,不能做其他任务处理;
-
同步非阻塞:小明提交下载任务后就去干别的,每过一段时间就去瞄一眼进度条,看到 100% 就完成。
同步体现在:等待下载完成通知; 非阻塞体现在:等待下载完成通知过程中,去干别的任务了,只是时不时会瞄一眼进度条;【小明必须要在两个任务间切换,关注下载进度】
-
异步阻塞:小明换了个有下载完成通知功能的软件,下载完成就“叮”一声。不过小明仍然一直等待“叮”的声音(看起来很傻,不是吗)。
异步体现在:下载完成“叮”一声通知; 阻塞体现在:等待下载完成“叮”一声通知过程中,不能做其他任务处理;
-
异步非阻塞:仍然是那个会“叮”一声的下载软件,小明提交下载任务后就去干别的,听到“叮”的一声就知道完成了。
异步体现在:下载完成“叮”一声通知; 非阻塞体现在:等待下载完成“叮”一声通知过程中,去干别的任务了,只需要接收“叮”声通知即可;【软件处理下载任务,小明处理其他任务,不需关注进度,只需接收软件“叮”声通知,即可】
-
I/O多路复用
-
概念:一种机制,通过一个进程监视多个描述符,当某个描述符准备就绪时通知程序进行相应的读写操作;与多线程+阻塞I/O相比,可以监听多个文件描述符,不用创建和维护线程或进程,大大减少了系统的开销。
-
支持I/O多路复用的系统调用:select、poll、epoll
-
select:最初解决IO阻塞问题的方法。调用后select函数会阻塞,直到有描述符就绪或超时,当select函数返回后,可以通过遍历fdset找到就绪的描述符。
-
优点:良好的跨平台支持
-
缺点
-
单个进程打开的文件描述符(FD)有一定的限制,32位系统的默认值是1024,64位系统的默认值是2048
-
轮询方式扫描socket,FD数量较多时效率低下
-
需要维护一个用来存放大量FD的数据结构,在用户态和内核态传递该结构时的复制开销比较大
-
-
-
poll:本质上和select没有区别,通过基于链表的存储方式解决了最大连接数量受限的问题
-
优点:基于链表存储,没有最大连接数量的限制
-
缺点
-
大量的FD数组被整体复制于用户态和内核态之间
-
轮询排查方式
-
水平触发
-
-
-
epoll:使用“事件”的就绪通知方式,只返回状态发生变化的文件描述符,解决了轮询的瓶颈
-
优点
-
没有最大并发连接的限制,1G能监听10万个端口
-
事件就绪通知的方式(回调函数),效率不会随FD的增加而下降,效率得到提升
-
内存拷贝,利用mmap减少复制开销
-
-
对文件描述符操作的两种方式:LT模式和ET模式
-
LT模式:默认模式,同时支持block和no-block socket。内核通知一个文件描述符是否就绪了,可以选择不对这个就绪的FD进行IO操作,内核会继续通知
-
ET模式:高速工作方式,只支持no-block socket。内核通知一个文件描述符是否就绪了,如果不对这个就绪的FD进行IO操作,内核不会再次通知,除非某个操作导致那个FD不再是就绪状态了
-
-
-
-
select/poll/epoll对比
-
一个进程能打开的最大连接数
-
select:32位1024个,64位2048个,可以修改并重新编译内核,性能可能会受到影响
-
poll:基于链表存储,没有最大连接数的限制
-
epoll:有很大的上限,1G内存约10万个连接,2G内存约20W连接
-
-
FD剧增后带来的IO效率问题
-
select、poll:每次调用时都采用轮询方式遍历,随着FD的增加,遍历速度会线性下降
-
epoll:根据每个FD上的回调函数实现,活跃的socket才会主动调用callback,活跃的socket较少的情况下没有线性性能下降的问题,但是socket都很活跃的情况下,可能存在性能问题
-
-
消息传递方式
-
select、poll:内核空间到用户空间的拷贝
-
epoll:mmap映射,内核空间和用户空间共享内存
-
-
-
选择:当连接数少且连接都十分活跃的情况下,select和poll和性能可能会比epoll好,因为epoll机制需要很多的函数回调
socket编程的线程通信模型,BIO/NIO/AIO
同步阻塞的BIO、同步非阻塞的NIO、异步非阻塞的AIO。其中BIO是一个连接一个线程。NIO是一个请求一个线程。AIO是一个有效请求一个线程。
同步:select轮询查看channel是否处于就绪状态
异步:channel处于就绪状态之后通过回调函数通知线程epoll
阻塞:应用程序在获取网络数据的时候,如果网络传输数据很慢,那么程序就一直等待,直到传输完毕为止。
非阻塞:应用程序直接可以获取已经准备就绪的数据,无须等待
-
BIO:同步阻塞IO,服务器实现模式为一个连接一个线程,可能会造成不必要的线程开销,可以通过线程池机制改善
-
适用于连接数目比较小且固定的架构,对服务器资源要求比较高,并发局限于应用中,程序简单直观容易理解
-
-
NIO:同步非阻塞IO,服务器实现模式为一个请求一个线程,客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有IO请求时才启动一个线程进行处理。用户进程需要时不时询问IO操作是否就绪
-
适用于连接数目多且连接比较短的架构,并发局限于应用中,编程比较复杂
-
数据准备好之后,再交由应用进行处理,数据的读取/写入在应用线程中完成,节省了数据准备时间
-
缓冲区 buffer:NIO基于块进行数据处理,NIO中所有数据的读取都通过缓冲buffer进行处理
-
通道 channel:对数据的读取和写入要通过channel,通道是全双工的,可以更好的映射底层操作系统的API
-
选择器 selector:提供选择已经就绪的任务的能力,同步&非阻塞(可以同时轮训多个channel)
-
-
AIO:异步非阻塞IO,具有异步能力,对socket和IO都起作用,是一种在读写操作结束之前允许进行其他操作的IO处理
-
适用于连接数目比较多且连接比较长的架构,充分调用OS参与并发操作,编程比较复杂
-
与NIO相比,使用回调函数,读完之后再通知应用,可以胜任重量级,读写过程长的任务
-
Reactor模型
-
同步I/O的事件分发器
-
注册读就绪事件和相应的事件处理器;事件分发器等待事件;事件到来,激活事件分发器,分发器调用对应的事件处理器;事件处理器完成实际的读操作
-
Proactor是异步事件分发器,完成读或者写操作后通知事件处理器,事件处理器直接处理缓冲区的数据即可,不需要再自己读取
内存碎片
-
内部碎片:已经分配给进程但是进程不使用的存储块;直到进程释放它或者进程结束后,系统才有可能利用这个存储块
-
外部碎片:还没有分配出去的存储块,但是由于太小了无法分配给新的进程;伙伴系统算法
提高系统并发性
-
提高CPU并发计算能力
-
多进程&多线程
-
使用线程,减少进程切换
-
减少不必要的锁,考虑无锁编程
-
考虑进程优先级
-
考虑系统负载
-
-
改进IO模型
-
使用DMA技术
-
异步I/O
-
改进多路I/O就绪通知策略
-
内存映射
-
直接I/O
-
Sendfile
-
死锁的条件
-
互斥条件:一个资源一次只能被一个进程访问
-
请求与保持条件:进程因请求资源而阻塞时,对已获得的资源保持不放
-
不剥夺条件:进程已经获得的资源,在未使用完之前不能强行剥夺,只能进程自行释放
-
循环等待条件:若干资源形成一种头尾相接的循环等待资源的关系
解决方法
- 预防死锁:破坏死锁产生的必要条件:1.互斥条件(不可破坏);2.请求与保持条件(一次申请全部资源);3.不可剥夺条件(不能满足要求时先释放资源再申请);4.循环等待条件(线性排队)
- 避免死锁:银行家算法,系统在为进程分配资源之前,首先计算此次分配的安全性,如果是安全的再进行分配。安全是指,存在一个进程序列,按照这个顺序为各个进程分配资源,所有进程都能顺利完成运行
- 解除死锁:剥夺资源、撤销进程
线程切换过程
-
控制权的转换
-
根据优先级切换上下文
-
用户级上下文:正文、数据、用户堆栈和共享存储区
-
寄存器上下文:通用寄存器、程序寄存器、处理器状态寄存器、栈指针
-
系统上下文:进程控制块、内存管理信息、内核栈
-
进程通信
管道,速度比较慢
- 无名管道:半双工通信方式,用于有亲缘关系的进程之间的通信,单独构成了一种文件系统,只存在内存当中
- 有名管道:可用于所有进程之间的通信,以文件的形式存放在文件系统当中
消息队列
- 基于链表的一个队列,存放在内核缓冲区中,容量受系统的限制
共享内存
- 存放在内存当中,速度更快,但是要注意同步的问题
进程状态转换
线程阻塞状态包括三种
- 等待阻塞:运行中的线程执行wait()方法会进入该状态
- 同步阻塞:没有获取到锁的线程进入该状态
- 超时阻塞:执行sleep(time)或者wait(time)方法进入该状态
进程调度方式
- 先来先服务调度算法
- 短进程优先调度算法
- 优先级调度算法
- 高响应比优先调度算法
- 时间片轮转算法
- 多级反馈队列调度算法