linux内核接收网络数据流程(四)

已经进行到这里,接下来需要讲的就是tcp的recv如何读取到内核的数据的了。

一 先看看struct sock的缓存队列sk_receive_queue,

这个队列就是内核的软中断上下文和进程空间的tcp_recvmsg(应用层调用tcp_recv)之间通信的队列。

因此,我们只需要找到哪里写队列,哪里读队列的就好了。


二 继续上一节的软中断上下文处理

tcp_v4_rcv()//主要流程

//如果失败,直接调用tcp_v4_do_rcv()存储到struct sock的sk_receive_queue中

if (!tcp_prequeue(sk, skb))//数据放到prequeue中,如果失败了,直接放到receive_queue中

ret = tcp_v4_do_rcv(sk, skb);

bool tcp_prequeue(struct sock *sk, struct sk_buff *skb)

{//最终也是从prequeue取出数据,转移到sk_receive_queue中

while ((skb1 = __skb_dequeue(&tp->ucopy.prequeue)) != NULL)

sk_backlog_rcv(sk, skb1);

{

sk_backlog_rcv(sk,skb);//函数指针tcp_v4_do_rcv()

}

//上面应该是做一个缓冲,避免应用层读取太慢了,sk_receive_queue存不下了

int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)

{

tcp_rcv_established();//这里我们仅仅看ESTABLISHED状态,

{

eaten = tcp_queue_rcv(sk, skb, tcp_header_len,

      &fragstolen);

{

__skb_queue_tail(&sk->sk_receive_queue, skb);

}

}

}

 

三 再看tcp_recvmsg函数调用

int tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock,

int flags, int *addr_len)

{

timeo = sock_rcvtimeo(sk, nonblock);//获取超时时间,如果是非阻塞为0

seq = &tp->copied_seq;//读取数据起始seq

target = sock_rcvlowat(sk, flags & MSG_WAITALL, len);//socket设置了,MSG_WAITALL选项,要等到数据满足接收长度的条件的时候,才返回,下面会挂起cpu

skb_queue_walk(&sk->sk_receive_queue, skb) //这是一个宏,循环读取sk_receive_queue中的skb

{

///由于tcp是字节流,因此我们拷贝给用户空间,需要正序的拷贝给用户,这里的第一个seq前面已经描述了,表示当前的总的sock连接中的未读数据的起始序列号,而后一个seq表示当前skb的起始序列号。因此这个差值如果小于skb->len,就表示,当前的skb就是我们需要读取的那个skb(因为它的序列号最小).  

     offset = *seq - TCP_SKB_CB(skb)->seq;  

}

//接下来是清理读取一部分sk_receive_queue后的清理工作。可以自行阅读源码

}

四 下一节我们讲解,软中断如何激活socket描述符的,以及epoll和select实现机制。


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值