TCP输入 tcp_queue_rcv
static int __must_check tcp_queue_rcv(struct sock *sk, struct sk_buff *skb, int hdrlen, bool *fragstolen) { int eaten; /* 取队尾 */ struct sk_buff *tail = skb_peek_tail(&sk->sk_receive_queue); __skb_pull(skb, hdrlen); /* 尝试进行分段合并 */ eaten = (tail && tcp_try_coalesce(sk, tail, skb, fragstolen)) ? 1 : 0; /* 更新下一个期望接收的序号 */ tcp_rcv_nxt_update(tcp_sk(sk), TCP_SKB_CB(skb)->end_seq); /* 未合并 */ if (!eaten) { /* 添加到队列尾部 */ __skb_queue_tail(&sk->sk_receive_queue, skb); /* 关联控制块 */ skb_set_owner_r(skb, sk); } return eaten; }
tcp_try_coalesce函数进行合并数据段操作,若合并成功,则更新CB中的对应字段值;
static bool tcp_try_coalesce(struct sock *sk, struct sk_buff *to, struct sk_buff *from, bool *fragstolen) { int delta; *fragstolen = false; /* Its possible this segment overlaps with prior segment in queue */ /* 序号对不上 */ if (TCP_SKB_CB(from)->seq != TCP_SKB_CB(to)->end_seq) return false; /* 尝试合并到前一个数据段 */ if (!skb_try_coalesce(to, from, fragstolen, &delta)) return false; /* 调整内存使用 */ atomic_add(delta, &sk->sk_rmem_alloc); sk_mem_charge(sk, delta); NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPRCVCOALESCE); /* 更新cb相关字段 */ TCP_SKB_CB(to)->end_seq = TCP_SKB_CB(from)->end_seq; TCP_SKB_CB(to)->ack_seq = TCP_SKB_CB(from)->ack_seq; TCP_SKB_CB(to)->tcp_flags |= TCP_SKB_CB(from)->tcp_flags; return true; }
skb_try_coalesce函数为详细的合并过程,在进行了必要的合并检查之后进行合并;其中当skb线性区域有数据的时候,会将该线性区域处理成frag,并合并到模板skb中;对于非线性区域,则直接进行拷贝,如果是clone的,还需要增加frag的引用计数;合并完成之后,调整skb数据长度值;
bool skb_try_coalesce(struct sk_buff *to, struct sk_buff *from, bool *fragstolen, int *delta_truesize) { int i, delta, len = from->len; *fragstolen = false; /* 不能为克隆 */ if (skb_cloned(to)) return false; /* to尾部能够容纳得下新数据 */ if (len <= skb_tailroom(to)) { /* from拷贝到to尾部 */ if (len) BUG_ON(skb_copy_bits(from, 0, skb_put(to, len), len)); *delta_truesize = 0; return true; } /* to或者from有分片 */ if (skb_has_frag_list(to) || skb_has_frag_list(from)) return false; /* 线性缓冲区数据长度不为0 */ if (skb_headlen(from) != 0) { struct page *page; unsigned int offset; /* 达到最大frags限制 */ if (skb_shinfo(to)->nr_frags + skb_shinfo(from)->nr_frags >= MAX_SKB_FRAGS) return false; /* skb被锁定 */ if (skb_head_is_locked(from)) return false; /* 计算数据增量,去掉头部 */ delta = from->truesize - SKB_DATA_ALIGN(sizeof(struct sk_buff)); /* 找到对应的页和偏移 */ page = virt_to_head_page(from->head); offset = from->data - (unsigned char *)page_address(page); /* 根据from的页和偏移在to的frags上增加一个frag */ skb_fill_page_desc(to, skb_shinfo(to)->nr_frags, page, offset, skb_headlen(from)); *fragstolen = true; } else { /* 达到最大frags限制 */ if (skb_shinfo(to)->nr_frags + skb_shinfo(from)->nr_frags > MAX_SKB_FRAGS) return false; /* 计算增量,减掉所有头部和无数据线性区域 */ delta = from->truesize - SKB_TRUESIZE(skb_end_offset(from)); } WARN_ON_ONCE(delta < len); /* 拷贝frags */ memcpy(skb_shinfo(to)->frags + skb_shinfo(to)->nr_frags, skb_shinfo(from)->frags, skb_shinfo(from)->nr_frags * sizeof(skb_frag_t)); /* 增加frags数量 */ skb_shinfo(to)->nr_frags += skb_shinfo(from)->nr_frags; /* 不是克隆的,设置from的frags为0 */ if (!skb_cloned(from)) skb_shinfo(from)->nr_frags = 0; /* if the skb is not cloned this does nothing * since we set nr_frags to 0. */ /* 克隆的,则需要对frags增加引用 */ for (i = 0; i < skb_shinfo(from)->nr_frags; i++) skb_frag_ref(from, i); /* 总长度加上增量 */ to->truesize += delta; /* 总数据长度增加 */ to->len += len; /* 非线性数据长度增加 */ to->data_len += len; /* 记录增量 */ *delta_truesize = delta; return true; }
http代理服务器(3-4-7层代理)-网络事件库公共组件、内核kernel驱动 摄像头驱动 tcpip网络协议栈、netfilter、bridge 好像看过!!!!
但行好事 莫问前程
--身高体重180的胖子
分类:
linux tcp/ip
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!