1. 用户态和内核态
1.1 用户态和内核态的概念
内核态:cpu可以访问内存的所有数据,包括外围设备,例如硬盘,网卡,cpu也可以将自己从一个程序切换到另一个程序。
用户态:只能受限的访问内存,且不允许访问外围设备,占用cpu的能力被剥夺,cpu资源可以被其他程序获取。
1.2 用户态切换到内核态的三种方式
- 系统调用
- 异常
- 外围设备的中断
2. 为什么不用多线程?
当客户端连接非常多的时候 多线程进行处理的话 需要CPU上下文切换,处理一些操作句柄。代价很高。
3. 如何用单线程的方式来处理客户端的大量连接呢?
3.1. DMA控制器
当线程处理A消息时,B上有消息过来,不会造成丢失。因为有DMA控制器。
DMA(Direct Memory Access,直接存储器访问) 是所有现代电脑的重要特色,它允许不同速度的硬件装置来沟通,而不需要依赖于 CPU 的大量中断负载。否则,CPU 需要从来源把每一片段的资料复制到暂存器,然后把它们再次写回到新的地方。在这个时间中,CPU 对于其他的工作来说就无法使用
3.2. 文件描述符
每一个网络连接在内核中都是以文件描述符(FD)存在
3.3 单线程遍历文件描述符
在用户态中进行的,效率低。
3.4 select/poll
系统调用,select函数就是将文件描述符从用户态拷贝到内核态,交由内核来判断哪个FD有数据(把轮询的操作放在内核 中去进行)
select将rset(读文件描述符集合,读取网络上的数据)拷贝到内核中进行判断,fd中是否有数据。没有数据就会一直判断,处于阻塞状态。有数据会将FD对应的节点置位,select返回,不再阻塞。 然后再进行遍历,查看哪个FD被置位了。 然后就会读取被置位的fd的数据,并且进行相应的处理,也就是poll操作。
缺点:
- FDset默认大小1024,bitmap,记录的socket的状态,是有限的。
- FDset不可重用,每次都会进行遍历,重新创建bitmap。因为select把rset修改过了,我们在重新进行监听的时候,需要将rset进行复位。
- 拷贝过程有较大的开销,用户态和内核态的切换。
- rest被select置位后,需要再次遍历判断去接受数据,需要O(N)的复杂度。
rest: bitmap类型,默认1024bit,表述对哪个文件描述符进行启用/监听
3.5 poll
监听原理和select差不多,都要对rset进行拷贝。 解决了select ①②的缺点 区别 pollfds结构体 可以进行重用
pollfd结构体
fd: 读文件描述符 events:需要在意的事件是什么,pollin,pollout revents:对event的回馈,默认是0
过程
poll函数进行阻塞。 若有数据,①pollfd.revents会进行置位1。②poll返回
3.6 epoll
多路复用函数 epfd : 可以理解为白板,存储的是读文件描述符和其对应的事件 epoll_ctl :在白板上进行写字,文件描述符fd,event事件,
有数据的话 ①置位,也就是进行重排,有数据的fd会放在最前面的位置上。②然后进行返回,返回的是触发事件的数量。 然后进程处理。
场景
epoll: redis,nginx,nio
参考
用户态和内核态的区别
【并发】IO多路复用select/poll/epoll介绍
DMA(直接存储器访问) - 百度百科
epoll原理详解及epoll反应堆模型
|