概述
-
内核态与用户态
-
内核态: 可以访问系统资源, 比如CPU, 内存, 网络, 外设
-
用户态: 只能访问进程自己的资源, 无法访问系统资源
-
用户态访问系统资源时, 需通过系统调用, 即CPU切到内核态, 读取资源后再切回用户态. 中间涉及堆栈上下文的切换, 为避免频繁切换, 有了"用户缓冲区"和"系统缓冲区"
-
当用户进程需要从"磁盘/网络"中读取数据时, 系统会将"系统缓冲区"的数据复制到"用户缓冲区". 若"系统缓冲区"中没有对应数据, 系统会将当前进程挂起, 处理其他进程. 等数据到达"系统缓冲区"后, 系统将数据拷贝至"用户缓冲区", 然后才会通知进程, 不同IO模型通知时机不同
-
调用
-
系统调用
-
系统调用指运行在用户态的程序向操作系统内核请求系统资源
-
系统调用是用户程序与操作系统之间的接口
-
系统调用由操作系统内核提供, 运行在内核态
-
-
函数调用
-
函数调用由库函数或用户提供, 运行在用户态
-
主要通过压栈操作来进行函数调用
-
-
-
-
套接字 (socket)
-
定义: 进程间通信流的端点, 是一种进程间通信机制, 由五元组(本地IP+本地端口+远程IP+远程端口+协议)唯一标识套接字, 一个套接字对应着一条连接
-
位于应用层和传输层之间的一个抽象层, 便于应用程序更方便的将数据经由传输层传输
-
每个套接字在"系统缓冲区"中都有"发送缓冲区"和"接收缓冲区", 并提供有send和receive方法
-
数据发送: 由"用户缓冲区"拷贝到"发送缓冲区", 拷贝的过程通过客户端套接字的send方法完成, 需要CPU参与. 最后数据将从"发送缓冲区"拷贝到网卡中, 从内存到网卡通过DMA(类似协处理器)拷贝, 不需要CPU参与.
-
数据接收: 通过"网卡"接收, 数据通过DMA拷贝至"接收缓冲区". 再由"接收缓冲区"通过CPU拷贝至"用户缓冲区", 该过程通过服务端套接字的receive方法完成.
-
当服务器网卡收到的数据校验成功后, 会向客户端发送ACK, 客户端再清除客户端套接字"发送缓冲区"里的数据
-
-
五种IO模型, IO即磁盘/网络读写
-
c10k 一台计算机实现10000台客户端的连接, 并处理对应的请求. 一个进程处理一个连接, 会消耗过多资源. 所以要一个进程处理多个连接, 即IO多路复用, 目前通过IO多路复用已实现c10k.
-
五种IO模型 (参考自https://zhuanlan.zhihu.com/p/54580385)
-
所有的IO模型都分为两阶段, 前四个模型, 第二阶段复制数据阶段都是阻塞的.
-
等待系统将数据准备好, 即等待数据从网卡/磁盘复制到"系统缓冲区", 需要DMA参与
-
将数据从"系统缓冲区"复制到"用户缓冲区", 需要CPU参与
-
-
阻塞式IO, 第一阶段"请求进程"调用receive, 一直阻塞至第二阶段完成
-
非阻塞IO, 第一阶段"请求进程"不停调用receive, receive立即返回异常. 直至"数据已备好"后, "请求进程"再调用receive会阻塞至第二阶段完成
-
IO多路复用(即事件驱动IO), 第一阶段"请求进程"调用select, 并阻塞至"数据准备好". 第二阶段"请求进程"发起receive, 阻塞至拷贝完成. 虽然看起来"请求进程"都是阻塞, 但是服务器内核可以同时监听select负责的多个套接字, 服务器效率非常高
-
信号驱动IO, 第一阶段"请求进程"向内核注册信号后, 不阻塞. 当"数据准备好"后, 内核通过信号通知"请求进程", "请求进程"调用receive, 阻塞取得数据
-
异步IO, 第一阶段"请求进程"发起receive, 不阻塞. 当第二阶段数据拷贝完后, 内核会通过回调通知"请求进程".
-
-
nginx是事件驱动服务器的代表, 适合IO密集型服务, 使用的是epoll; Apache是线程服务器的代表, 适合计算密集型服务(apache存疑)
-
I/O多路复用 (select, poll, epoll)
-
select
-
select函数
-
定义
select用来监视多个文件描述符, 当文件描述符所对应事件状态不改变时, select会阻塞. 当某个文件描述符所对应事件状态改变后, select会返回
-
Python案例
readable_list, writeable_list, execable_list = select.select(read_list, write_list, exce_list, timeout) 当read_list的fd满足可读条件时, 则将发生变化的fd添加到readable_list中 当write_list的fd满足可写条件时, 则将发生变化的fd添加到writeable_list中 当exce_list的fd发生错误时, 则将该发生错误的fd添加到execable_list中
-
-
原理
-
select通过内核来监视一个由多个文件描述符(fd)组成的数组. 当select返回后, 数组中就绪的文件描述符会被内核修改标记位, 进程便可以通过遍历数组, 来获得这些文件描述符并从而进行后续的读写操作
-
进程会指定内核监听哪些文件描述符的哪些事件, 当没文件描述符所监听的事件发生时, 进程被阻塞, 当一个或者多个文件描述符事件发生时, 进程则被唤醒
-
文件描述符数组: 位图(bitmap), 最大一般为1024位, 即最大可监听1024个文件描述符. 当有事件发生时, 对应的位会被标记为1. 当下一次监听时, 位图需全部置为0
-
-
select被调用时的过程
-
上下文切换, 将用户态切换为内核态, 并将fd数组从用户空间复制到内核空间
-
内核遍历fd数组, 查看这些文件描述符对应事件是否发生. 如果这些文件描述符无对应事件发生, 进程将阻塞. 当设备驱动产生中断或者timeout时间后, 进程唤醒并再次进行遍历. 持续该过程直至有事件发生
-
内核标记发生事件的fd, 并将fd从内核空间复制到用户空间, 并从内核态切换为用户态
-
-
select缺点
-
单个进程能够监视的fd数量存在限制, 在linux上为1024
-
当fd数组很大时, 因每次调用select都需要把fd数组从用户空间拷贝到内核空间, 所以开销很大
-
当fd数组很大时, 内核对fd数组的遍历浪费时间
-
下一次监听时, 位图需全部置为0
-
-
-
poll
-
原理
-
结构 struct pollfd { int fd; # 文件描述符 short events; # 该文件描述符注册的事件集合 short revents; # 该文件描述符事件状态发生变化的事件集合 }
-
poll通过内核监视多个pollfd组成的数组, 当任一监听的pollfd变化时, revents会被标记并返回. 且下一次监听时, events不用重新置位. poll本质上和select无区别, 只是没有最大连接数的限制, 原因是它基于链表存储
-
-
poll缺点
-
当fd数组很大时, 因为每次调用poll都需要把pollfd数组从用户空间拷贝到内核空间, 所以开销很大
-
当fd数组很大时, 内核对pollfd数组的遍历浪费时间
-
-
-
epoll
-
在freeBSD系统上没有epoll, 请使用kqueue. kqueue与epoll的不同之处: kqueue的一个fd的read/write事件需要分开注册, 而epoll则是一个fd一次注册read/write事件
-
结构
struct epollfd {
int fd; # 文件描述符
short events; # 该文件描述符注册的事件集合 }
-
函数
-
epoll_create: 内核空间中建立红黑树, 双向就绪链表
-
epoll_ctl
-
向内核空间的红黑树中塞入epollfd
-
为该事件文件描述符注册一个回调函数. 当该事件文件描述符监听的事件就绪后, 该回调函数会把就绪的epollfd加入到双向就绪链表中
-
-
epoll_wait: 检查双向就绪链表中是否有元素, 没有就睡眠, 有就将epollfd从内核空间拷贝至用户空间, 并返回
-
-
高效理由
-
对于select缺点一, epoll所支持的最大fd数量是系统中可打开文件的最大数目, 一般该数目和内存有较大关系
-
对于select缺点二, epoll_ctl会将epollfd从用户空间拷贝到内核空间, 之后每次调用epoll_wait只是检查就绪链表, 返回已就绪的, 不像select, poll返回所有的
-
对于select缺点三, 因为注册了回调函数, 故不需要内核遍历
-
-
通知模型 (参考自https://blog.csdn.net/fengxinlinux/article/details/75331567)
-
水平触发(LT)
-
文件描述符上有数据变化时, 触发一个事件. 可以只读该事件有关的一部分数据, 未读完则下次检查时会再触发事件来通知
-
优点是稳定可靠, 缺点是当就绪的文件描述符过多时效率低
-
-
边缘触发(ET)
-
文件描述符上有数据变化时, 触发一个事件. 需要一次读完该事件有关的数据, 否则之后不会再触发事件来通知
-
优点是高效, 缺点是不可靠, 实现复杂
-
-
select, poll仅支持水平触发; epoll支持水平触发和边缘触发, 默认采用水平触发; nginx采用的是边缘触发
-
-
-
-
最后
以上就是雪白小土豆为你收集整理的「网络」套接字 (Socket)的全部内容,希望文章能够帮你解决「网络」套接字 (Socket)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复