多路复用
socket 套接字
使用 socket 套接字进行网络通信的时候需要进行三次握手和四次挥
阻塞 IO
原理
服务端和客户端在进行连接或者 read/write 操作时,都会先阻塞住,等待操作完成后再进行下一步。
类比
老师收作业,有十个同学举手,老师只能收一个,回来,再收一个再回来,如此把作业收完。如果没有作业,一直处于等待状态。
非阻塞IO
原理
服务端和客户端在进行连接或者read/write操作时,不会阻塞住,会继续进行轮询查询,直到有响应到来。
类比
老师,收作业,有十个同学举手,老师不知道哪个同学举手,每次从头开始收作业,作业收完后,老师依旧要视察有没有举手。
select/poll
用户态和内核态
操作系统中将空间分为用户空间和内核空间,规定内核空间中只能操作一些相对安全的指令,防止用户空间的进行不安全的操作将操作系统搞崩掉。 用户空间向内核空间发送指令,cpu 状态会从用户态变为内核态,反之从内核态变为用户态,两态之间的转变非常消耗 cpu 资源。
原理
开始时会将文件描述符 fd 从用户空间复制到内核空间中。
缺点是如果 fd 数量过大,复制行为比较耗时。
非阻塞 I0 在进行读写操作时并不会等待服务端将信息发送过来之后再开始,他的内部实际一个事件循环,也就是死循环,会不断检查内核空间中的所有文件描述符 fd 的状态。当检查到已就绪的fd时,会将此fd告诉用户空间已准备就绪可以进行处理。此时用户空间拿到此fd之后就去处理。
缺点是要不断在内核空间和用户空间轮询fd,以查找到准备就绪的fd去处理。
类比
老师收作业,有10个学生在举手,老师不知道收谁的作业,只能每次从头开始找举手的同学,然后把作业收上来。
优点
优点不用频繁在用户态和内核态之间转换,节省cpu开销
poll 与 select 的不同点
- poll 中内核空间中保存 fd 的结构由数组变为链表,不再受 select 单线程中最大保存 fd 的数组大小为 1M 的限制。
- poll 中引入了就绪事件参数,用于保存就绪事件,使得返回就绪参数给用户空间之后,不用再因为内核空间改变 select 中保存 fd 数组的值而重新重置 fd 数组。
epoll
原理
引入 epoll_create 创建一个 epollEvent。
- epollEvent里面定义 rbn 红黑树用于保存用户空间传入的 fd,大大提高 fd 的删改查的速度。
- epollEvent 中具有就绪列表,用于保存就绪的 fd。
- epollEvent 中具有一个等待队列,用于保存闲置的线程。
引入epoll_ctl 用于操作epollEvent。
当用 epoll_create 创建了一个 epollEvent 以后,可以使用 epoll_ctl 将用户空间的fd存入到 epolEvent 中的 rbn 数据结构中。
引入 epoll item 用于主动将就绪列表中的 fd 回传到用户空间。
epoll item 中定义了一个重要的结构 pwglist 关联了一些回调函数用于调用等待队列中的线程,用于判断就绪列表中有没有就绪事件,如果有,传回用户空间进行处理。
流程
- 客户端发送了一条数据并通过网卡传到内存。
- cpu 调用中断方法将 socket 放入就绪列表并生成 epoll_item。
- epoll_item 中的回调函数启动唤醒等待队列中的线程判断就绪空间中的 fd 并返回。
- 调用 epoll_wait 将就绪列表中的所有 fd 返回进行处理
优点
解决以上方法的所有缺点
类比
老师收作业,有 10 个同学举手,然后把这 10 个同学的作业一起收类比上来。