用户态协议栈之Epoll的实现

0、思考问题:

如何判断send数据,对方有没有接受成功?
send返回正数是没有用的。
send函数做的事情是,将应用需要发送的数据拷贝到协议栈中,用户空间拷贝到内核空间,何时发送的数据包是由协议栈自己决定的。

1、TCP的粘包和分包
应用层多次send,第一次send,32字节 第二次send,64字节,协议栈发送粘包96字节
解决方案:
方案1、在应用层协议里,协议头里加长度的域
方案2、每个包加分隔符
方案3、发送定长的包,比较低级
协议栈里tcp包先发的先到 序号+延迟确认 保证。
协议栈设置可以取消延迟确认机制,仅限于在网络情况较好的情况下取消该机制。

拷贝方式:
1、直接将内存拷贝到协议栈
2、mov,整块内存映射到协议栈内存中
多个线程并发给一个fd调用send,会使send buff里数据混乱。
解决方案:通过io多路复用(epoll、poll、select),epoll判断多线程
把fd从epoll里取到,然后epollout取消,不再监听,另一个线程使用该fd send,send完再加入到epoll里,继续监听。保证只有一个线程占用了可写的fd。

1、调用send时候,返回-1,eagein,唯一原因,send buff 是满的。
解决方案:调用send函数以前(特别是发送数据多时)加上poll(fd)或select(fd)或epoll(fd)判断fd是否准备就绪,是否可写。不就绪就等待下一次再发。

2、recv时候,fd非阻塞,set,返回-1,正常现象,recv buff没有数据而已。

2、epoll具体实现

客户端数量越多epoll性能越好,1024,超过500以上用epoll。

1)、如何管理好这么多fd
fd状态:
空闲状态:不可操作状态,即空闲,没有数据。
就绪状态:可操作,可读可写。
就绪fd是很少的,大部分处于空闲状态。

提供两个集合,就绪集合,空闲集合。
空闲集合:重查找,做索引,可以从以下选择:最合适的是红黑树
a、最快的是hash,查找速度快,缺点不利于扩展,不利于后端fd管理。
b、然后是红黑树(avl),查找性能logN,可以扩展,最小节点,没有太多空间浪费。
c、b/b+树,查找性能 (logN)/M+logM,节点比较大,不适合内存索引。
d、跳表,概率学,节点有很多浪费,一半的节点都没使用。
fd从3开始依次增加,maxfd设置最大值。

就绪集合:用队列存储
为什么使用队列?
没有查找、修改操作,所有都需要遍历,再加上数量相对不多。数组长度固定,不太好。
队列:先进先出,可能一次拿不完数据。栈是不行的。

红黑树如何做到线程安全?
1、锁整棵树
2、锁子树
两者性能差别不大,锁整棵树更容易实现,推荐锁整棵树,用互斥锁。
用什么锁,互斥锁,业务粒度大用互斥锁,业务粒度小用自旋锁。

队列安全:用自旋锁,用cas也可以spinlock。

协议栈哪些地方需要epoll?
三次握手,数据传输,四次挥手过程中哪些地方需要epoll
a、三次握手完成时候,accept将全链接队列中的tcb块和fd绑定一起,可读
b、recvbuff中有数据,延迟ack超时发送时,将数据搬运到recvbuff,可读
c、sendbuff有空间的时候,可写
d、接受到fin的时候,可读
以上四种情况需要通知到epoll,对应的fd是否就绪

协议栈回调epoll模块,有几个动作需要做:
a、查找,从空闲队列中,找到对应的fd,找不到出错;
b、加入就绪队列,fd置为可读;
c、发条件通知,信号,给epoll_wait,让epoll_wait知道就绪队列不为空了。 

ET、LE:
ET,recvbuffer中从没有数据到有数据,触发一次。
LE,recvbuffer中一直有数据则一直触发。
recvbuffer:延迟ack确认哪些数据已经收到了,则将这些数据拷贝到recvbuffer中。


 


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值