tcp数据包接收流程整理
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
4.接下来就是数据包在传输层的传递了。我们以tcp为例,与UDP一样,都是以不可靠的IP协议为网络层协
议,所以,TCP数据传输的可靠性是靠TCP协议本身来保证的,所以数据不可能过早ACK。废话不多说,先
看。
到了这里,就是上下交接的关键点所在了,首先是通过__inet_lookup_skb这个函数找到对应的socket
先一步一步来看,目前进度是tcp_v4_rcv(),在这个函数当中,会做这样一个判断 if(!sock_owned_by_user(sk)),它是判断当前的sock有没有被用户进程锁定,如果被锁定,那就代表用户进程 进入到了内核层,而且正在处理数据。也就是说!sock_owned_by_user不成立(被锁住)所以就会将数据放 到sk_backlog当中去。如果if条件成立,sock没有被锁住,就代表当前并没有进程在处理数据,继续往下,接 着又是一个if判断 (!tcp_prequeue(sk, skb)),我们接着进入tcp_prequeue当中去看一看,这个函数一进去就做 了一个判断 if(sysctl_tcp_low_latency || !tp->ucopy.task) return 0;
path。
由于处理乱序包或缓冲区用尽这些情况很麻烦,因此处理这些情况的path就是slow-path,具体是什么path是根据
tcp语义和数据包特性来确定的,和具体协议栈的实现没有关系。
(队列1) tcp_prequeue_process里面有这么一段,处理backlog
if (copied >= target) {
Initialise the virtual path cache for the packet. It describes how the packet travels inside Linux networking. ip_rcv_finish()中首先判断skb包中是否设置路由缓存,如果没有设置,调用ip_route_input()来查找路由 项,然后调用dst_input()来处理skb包。dst_input()里面只有一句话return skb_dst(skb)->input(skb); 其中skb_dst(skb)是获取skb的路由缓存项,如果数据包是发送到本地,input接口会设置为ip_local_deliver(); 如果需要转发,则设置的是ip_forward()。 下面继续看ip_local_deliver(),
我们接着看tcp_v4_do_rcv(sk, skb),它是应用层系统调用 跟 内核数据包传递的交互之处,下面有一张图,
很好的解释了整个流程。
我们先继续看tcp_v4_do_rcv,其中会调用tcp_rcv_established, tcp_recvmsg当中调用release_sock,其中又_release_sock,又调用函数指针,处理......backlog里的东西 这里所说的处理prequeue应该是以防万一,真正的prequeue处理还是在上面的tcp_prequeue
fast path需要一些附加条件,
其含义是复制到用户空间或者按序进入receive_queue的处理路径,slow path指的是乱序报文的处理以及其他不符合
fast path的数据包的处理方式。这些path与数据报文进入那个队列没有关系,一般是将fast path分为两类:
直接复制进用户空间的路径是most-fast-path,没有进程上下文导致按序的skb进入receive_queue的是more-fast-
估计这里释放sock,下面的才可以将数据添加到prequeue 里面有一个sk_wait_event,里面会调用release_sock
(队列2)还有这一段,处理 prequeue ,do prequeue
remote CPU queue).),这个函数会先从当前CPU获取一个per CPU 机制的softnet_data (每个CPU都有一个 softnet_data类型变量,用来管理进出分组的等待队列),通过它获取等待队列的指针之类的,然后将skb挂载到队列 当中,然后skb的传递结束,后续的一些处理就不赘述了。将当前设备添加到NAPI轮询队列。
似,都是 (compute_score)看匹配的分(还是根据协议族,地址,端口来),谁更像是我要的,那就找谁。 在数据传输层,数据包的接收是靠三个接收队列完成的,这三个队列分别是
receive_queue,prequeue,backlog这三个队列,其中,receive_queue算是真正的接收处理队列,而其余两个
/* Do not sleep, just process backlog. */
release_sock(sk);
lock_sock(sk);
} else sk_wait_data(sk, &timeo); (sk_wait_data - wait for data to arrive at sk_receive_queue)
只能算是预备处理队列。其中的区别听我慢慢道来。(linux tcp中的几个队列 )
三个队列,如果有用户进程锁住sock,不管三七二十一,把把丢到backlog,如果没有锁住,先看能不能 放到pre_queue,不能(代表没任务,不是正在处理receive_queue),那就可以挂载到receive_queue,不然就 挂载到pre_queue。
2.当CPU等待队列中存在数据包的时候,相应的软中断就会被唤醒,多种方式,最终都会调用
do_softirq,它会调用net_rx_action,这个函数会调用poll接口,在默认情况下,这个接口 是process_backlog(),它会调用函数_skb_dequeue()直接从当前CPU的接收队列取下头一个包, 然后调用__netif_receive_skb(skb)(
ip_local_deliver()首先检查是否需要组装分片,如果需要组装分片,则调用ip_defrag()来重新组合各个分 片,最后经过钩子处理后,调用ip_local_deliver_finish()来将skb包传递到传输层
ip_local_deliver_finish()首先根据IP包承载的报文协议类型找到对应的net_protocol实例,然后调用其handler 接口。ip_local_deliver_finish()中调用ipprot->handler(skb),如果使用TCP协议,则handler为tcp_v4_rcv
据包。在这个中断处理当中,会给新到的数据包分配空间,skb。 这个中断函数会调用netif_rx(netif_rx - post buffer to the network code),这个函数做了什么事呢,这里只
说关于数据包的传递,它会调用enqueue_to_backlog(skb, cpu,&rflow->last_qtail),接下来enqueue_to_backlog. enqueue_to_backlog(enqueue_to_backlog is called to queue an skb to a per CPU backlog queue (may be a
(/uid-23629988-id-309295.html TCP接收数据包如何选择socket )
对于有连接的tcp而言,先在连接成功的socket里面找(__inet_lookup_established),匹配地址和端口,不 然就在处于listen状态的socket(__inet_lookup_listener)里面找,这个时候,谁都不知道谁,方法跟udp类
netif_receive_skb:process receive buffer from network。netif_receive_skb() is the main receive data processing fu nction.)
处理获得的数据包,这个函数会调用deliver_skb,里面return pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
具体详见 the net pdf
当目的主机收到一个以太网数据帧时,数据就开始从协议栈中由底向上升,同时去掉各 层协议加上的报文首部。每层协议盒都要去检查报文首部中的协议标识,以确定接收数据的 上层协议。这个过程称作分用( D e m u l t i p l e x i n g),图1 - 8显示了该过程是如何发生的。
如果skb的三层协议类型是IP协议,则pt_prev->func()调用的就是ip_rcv()。
3.接下来,就是正式进入到 网络层的数据包传输了,ip_rcv(skb)会先查看该数据包是不是发给本机的,
如果不是就丢弃,然后查看是不是正常的数据包,如果是,就交给ip_rcv_finish()继续处理
(return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, skb, dev, NULL, ip_rcv_finish);)
tcp数据包接收流程整理
在此关于网络传输所用的数据包skb的收发流程(囊括所有分层详细,),因为所参考的版本各异,所以一 些细节可能会有所不同,但是大体都是一样的。 值得注意的是几处 中断锁 还有rcu_read_lock() 的使用
Van's trick is to deposit buffers into socket queue on a device interrupt, to call tcp_recv function on the receive process context and checksum and copy the buffer to user space. smart...
我们来看这里面两个判断项,前面的说明过了,后面一个条件的成立是在什么情况下呢?通过查阅资
料,其实进程不锁定sock,除了①还没开始处理数据,还有另外一种情况,②就是获取的数据不够多,进程 休眠等待数据。想要不返回0,也就说需要tp->ucopy.task!=null,表示当前正在等待数据,可是进入到这里 的先决条件就是sock没有被锁住,所以只有可能是第二种情况,当出现这种情况,不返回0,我们继续往下
( tcp_prequeue函数负责向pre_queue中添加skb,无法添加的原因只有两个: 一是用户为了降低延迟,全局关闭了pre_queue的功能(通过sysctl_tcp_low_latency开关)
பைடு நூலகம்
二是当前没有进程/用户等待处理该sk上的报文,即tp->ucopy.task为0时,也不能使用pre_queue。)
看。
然后就是直接调用__skb_queue_tail(&tp->ucopy.prequeue, skb);将数据包加入到prequeue的等待队列当中
去。
不过,当skb刚刚挂载到prequeue当中,tcp_prequeue紧接着就会将该数据包取下,然后唤醒等待进程,处 理数据包去了,这里调用的使用一个sk->sk_backlog_rcv(sk, skb)函数指针,sk是在tcp_v4_rcv()当中通过 __inet_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest);获得的,依据当前进程上下文(ftrace),可以判 断,该函数指针指向的是tcp_v4_do_rcv(sk, skb),
数据包的接收
1.不同于数据包发送,从上到下的过程,数据包的接收是从下到上的过程(这句废话,仅为装B...读起来更
好一点), 数据包接收的第一步就是网卡驱动从网络中获取数据包。
/lambda107/archive/2010/08/02/1790184.html 当一个数据包从网络中到达时,网卡会向CPU发送一个中断,然后会调用特定于网络的驱动程序来接收数
议,所以,TCP数据传输的可靠性是靠TCP协议本身来保证的,所以数据不可能过早ACK。废话不多说,先
看。
到了这里,就是上下交接的关键点所在了,首先是通过__inet_lookup_skb这个函数找到对应的socket
先一步一步来看,目前进度是tcp_v4_rcv(),在这个函数当中,会做这样一个判断 if(!sock_owned_by_user(sk)),它是判断当前的sock有没有被用户进程锁定,如果被锁定,那就代表用户进程 进入到了内核层,而且正在处理数据。也就是说!sock_owned_by_user不成立(被锁住)所以就会将数据放 到sk_backlog当中去。如果if条件成立,sock没有被锁住,就代表当前并没有进程在处理数据,继续往下,接 着又是一个if判断 (!tcp_prequeue(sk, skb)),我们接着进入tcp_prequeue当中去看一看,这个函数一进去就做 了一个判断 if(sysctl_tcp_low_latency || !tp->ucopy.task) return 0;
path。
由于处理乱序包或缓冲区用尽这些情况很麻烦,因此处理这些情况的path就是slow-path,具体是什么path是根据
tcp语义和数据包特性来确定的,和具体协议栈的实现没有关系。
(队列1) tcp_prequeue_process里面有这么一段,处理backlog
if (copied >= target) {
Initialise the virtual path cache for the packet. It describes how the packet travels inside Linux networking. ip_rcv_finish()中首先判断skb包中是否设置路由缓存,如果没有设置,调用ip_route_input()来查找路由 项,然后调用dst_input()来处理skb包。dst_input()里面只有一句话return skb_dst(skb)->input(skb); 其中skb_dst(skb)是获取skb的路由缓存项,如果数据包是发送到本地,input接口会设置为ip_local_deliver(); 如果需要转发,则设置的是ip_forward()。 下面继续看ip_local_deliver(),
我们接着看tcp_v4_do_rcv(sk, skb),它是应用层系统调用 跟 内核数据包传递的交互之处,下面有一张图,
很好的解释了整个流程。
我们先继续看tcp_v4_do_rcv,其中会调用tcp_rcv_established, tcp_recvmsg当中调用release_sock,其中又_release_sock,又调用函数指针,处理......backlog里的东西 这里所说的处理prequeue应该是以防万一,真正的prequeue处理还是在上面的tcp_prequeue
fast path需要一些附加条件,
其含义是复制到用户空间或者按序进入receive_queue的处理路径,slow path指的是乱序报文的处理以及其他不符合
fast path的数据包的处理方式。这些path与数据报文进入那个队列没有关系,一般是将fast path分为两类:
直接复制进用户空间的路径是most-fast-path,没有进程上下文导致按序的skb进入receive_queue的是more-fast-
估计这里释放sock,下面的才可以将数据添加到prequeue 里面有一个sk_wait_event,里面会调用release_sock
(队列2)还有这一段,处理 prequeue ,do prequeue
remote CPU queue).),这个函数会先从当前CPU获取一个per CPU 机制的softnet_data (每个CPU都有一个 softnet_data类型变量,用来管理进出分组的等待队列),通过它获取等待队列的指针之类的,然后将skb挂载到队列 当中,然后skb的传递结束,后续的一些处理就不赘述了。将当前设备添加到NAPI轮询队列。
似,都是 (compute_score)看匹配的分(还是根据协议族,地址,端口来),谁更像是我要的,那就找谁。 在数据传输层,数据包的接收是靠三个接收队列完成的,这三个队列分别是
receive_queue,prequeue,backlog这三个队列,其中,receive_queue算是真正的接收处理队列,而其余两个
/* Do not sleep, just process backlog. */
release_sock(sk);
lock_sock(sk);
} else sk_wait_data(sk, &timeo); (sk_wait_data - wait for data to arrive at sk_receive_queue)
只能算是预备处理队列。其中的区别听我慢慢道来。(linux tcp中的几个队列 )
三个队列,如果有用户进程锁住sock,不管三七二十一,把把丢到backlog,如果没有锁住,先看能不能 放到pre_queue,不能(代表没任务,不是正在处理receive_queue),那就可以挂载到receive_queue,不然就 挂载到pre_queue。
2.当CPU等待队列中存在数据包的时候,相应的软中断就会被唤醒,多种方式,最终都会调用
do_softirq,它会调用net_rx_action,这个函数会调用poll接口,在默认情况下,这个接口 是process_backlog(),它会调用函数_skb_dequeue()直接从当前CPU的接收队列取下头一个包, 然后调用__netif_receive_skb(skb)(
ip_local_deliver()首先检查是否需要组装分片,如果需要组装分片,则调用ip_defrag()来重新组合各个分 片,最后经过钩子处理后,调用ip_local_deliver_finish()来将skb包传递到传输层
ip_local_deliver_finish()首先根据IP包承载的报文协议类型找到对应的net_protocol实例,然后调用其handler 接口。ip_local_deliver_finish()中调用ipprot->handler(skb),如果使用TCP协议,则handler为tcp_v4_rcv
据包。在这个中断处理当中,会给新到的数据包分配空间,skb。 这个中断函数会调用netif_rx(netif_rx - post buffer to the network code),这个函数做了什么事呢,这里只
说关于数据包的传递,它会调用enqueue_to_backlog(skb, cpu,&rflow->last_qtail),接下来enqueue_to_backlog. enqueue_to_backlog(enqueue_to_backlog is called to queue an skb to a per CPU backlog queue (may be a
(/uid-23629988-id-309295.html TCP接收数据包如何选择socket )
对于有连接的tcp而言,先在连接成功的socket里面找(__inet_lookup_established),匹配地址和端口,不 然就在处于listen状态的socket(__inet_lookup_listener)里面找,这个时候,谁都不知道谁,方法跟udp类
netif_receive_skb:process receive buffer from network。netif_receive_skb() is the main receive data processing fu nction.)
处理获得的数据包,这个函数会调用deliver_skb,里面return pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
具体详见 the net pdf
当目的主机收到一个以太网数据帧时,数据就开始从协议栈中由底向上升,同时去掉各 层协议加上的报文首部。每层协议盒都要去检查报文首部中的协议标识,以确定接收数据的 上层协议。这个过程称作分用( D e m u l t i p l e x i n g),图1 - 8显示了该过程是如何发生的。
如果skb的三层协议类型是IP协议,则pt_prev->func()调用的就是ip_rcv()。
3.接下来,就是正式进入到 网络层的数据包传输了,ip_rcv(skb)会先查看该数据包是不是发给本机的,
如果不是就丢弃,然后查看是不是正常的数据包,如果是,就交给ip_rcv_finish()继续处理
(return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, skb, dev, NULL, ip_rcv_finish);)
tcp数据包接收流程整理
在此关于网络传输所用的数据包skb的收发流程(囊括所有分层详细,),因为所参考的版本各异,所以一 些细节可能会有所不同,但是大体都是一样的。 值得注意的是几处 中断锁 还有rcu_read_lock() 的使用
Van's trick is to deposit buffers into socket queue on a device interrupt, to call tcp_recv function on the receive process context and checksum and copy the buffer to user space. smart...
我们来看这里面两个判断项,前面的说明过了,后面一个条件的成立是在什么情况下呢?通过查阅资
料,其实进程不锁定sock,除了①还没开始处理数据,还有另外一种情况,②就是获取的数据不够多,进程 休眠等待数据。想要不返回0,也就说需要tp->ucopy.task!=null,表示当前正在等待数据,可是进入到这里 的先决条件就是sock没有被锁住,所以只有可能是第二种情况,当出现这种情况,不返回0,我们继续往下
( tcp_prequeue函数负责向pre_queue中添加skb,无法添加的原因只有两个: 一是用户为了降低延迟,全局关闭了pre_queue的功能(通过sysctl_tcp_low_latency开关)
பைடு நூலகம்
二是当前没有进程/用户等待处理该sk上的报文,即tp->ucopy.task为0时,也不能使用pre_queue。)
看。
然后就是直接调用__skb_queue_tail(&tp->ucopy.prequeue, skb);将数据包加入到prequeue的等待队列当中
去。
不过,当skb刚刚挂载到prequeue当中,tcp_prequeue紧接着就会将该数据包取下,然后唤醒等待进程,处 理数据包去了,这里调用的使用一个sk->sk_backlog_rcv(sk, skb)函数指针,sk是在tcp_v4_rcv()当中通过 __inet_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest);获得的,依据当前进程上下文(ftrace),可以判 断,该函数指针指向的是tcp_v4_do_rcv(sk, skb),
数据包的接收
1.不同于数据包发送,从上到下的过程,数据包的接收是从下到上的过程(这句废话,仅为装B...读起来更
好一点), 数据包接收的第一步就是网卡驱动从网络中获取数据包。
/lambda107/archive/2010/08/02/1790184.html 当一个数据包从网络中到达时,网卡会向CPU发送一个中断,然后会调用特定于网络的驱动程序来接收数