Linux内核网络源码分析——接收数据
目录:Linux内核网络源码分析
概述
接收数据分为从上往下和从下往上的两个过程
从上往下:
详细流程分析
数据包到达网卡,产生硬件中断
———————————
硬件中断处理:
rtl8139_interrupt() <8139too.c>
static inline void netif_rx_schedule(struct net_device *dev,
struct napi_struct *napi)
{
__napi_schedule() <net/core/dev.c>
—————————————–
软中断处理:
net_rx_action() <net/core/dev.c>
netif_recive_skb() <net/core/dev.c>
// ptype_all 链用于监听从网络设备上接收的所有包,通常不使用它。
// ptype_base hash表是被协议标识符弄乱的,用于决定哪个协议将接收传入的网络包。
deliver_skb() <net/core/dev.c>
ip_rcv() <net/ipv4/ip_input.c>
// 将IP数据包发往上层协议,数据包穿过Netfilter传到达ip_rcv_finish
ip_local_deliver_finish() <net/ipv4/ip_input.c>
概述
接收数据分为从上往下和从下往上的两个过程
从上往下:
1、用户空间中调用read接收数据,此系统调用在内核中的函数是sys_read(fs/read_write.c)。
2、sys_read最终会调用skb_recv_datagram:
2、sys_read最终会调用skb_recv_datagram:
§ 如果数据还没有到达,且设置socket设为阻塞模式时,进程挂起signal_pending(current)。
§ 如果数据已到达,或data_ready通知进程资源得到满足后继续处理 (wake_up_interruptible(sk->sleep);)进入第3步
§ 如果数据已到达,或data_ready通知进程资源得到满足后继续处理 (wake_up_interruptible(sk->sleep);)进入第3步
3、INET Socket接收数据
从下往上:1、数据到达网卡,产生一个硬件中断,由网卡驱动程序处理中断。
2、网卡驱动程序将收到的数据封装成struct sk_buff,然后把它放入接收队列,并置NET_TX_SOFTIRQ软中断标志位等待CPU调度。
3、CPU进程调度schedule()检测到软中断NET_RX_SOFTIRQ,调用相应处理函数net_tx_action(该函数在中net_dev_init注册)
4、net_tx_action开始处理sk_buff,经过网络层(IP)和传输层(TCP/UDP)处理,针对发往本机的数据包找到相应的INET Socket并放入其接收队列,设置data_ready唤醒上层接收进程。
2、网卡驱动程序将收到的数据封装成struct sk_buff,然后把它放入接收队列,并置NET_TX_SOFTIRQ软中断标志位等待CPU调度。
3、CPU进程调度schedule()检测到软中断NET_RX_SOFTIRQ,调用相应处理函数net_tx_action(该函数在中net_dev_init注册)
4、net_tx_action开始处理sk_buff,经过网络层(IP)和传输层(TCP/UDP)处理,针对发往本机的数据包找到相应的INET Socket并放入其接收队列,设置data_ready唤醒上层接收进程。
接收数据处理流程
—————————————–硬件中断处理
rtl8139_interrup
…NAPI的Poll机制…
—————————————–软中断处理
net_rx_action
rtl8139_poll
rt18139_rx
netif_recive_skb
—————————————–网络层处理
ip_rcv
<NF_INET_PRE_ROUTING>
ip_rcv_finish
ip_local_deliver
<NF_INET_LOCAL_IN>
ip_local_deliver_finish
—————————————–传输层处理
UDP
udp_rcv
__udp4_lib_rcv
udp_queue_rcv_skb
__udp_queue_rcv_skb
sock_queue_rcv_skb
skb_queue_tail
__skb_queue_tail
sk->sk_data_ready
TCP
tcp_v4_rcv
tcp_v4_do_rcv
tcp_rcv_established
__skb_queue_tail
sk->sk_data_ready
详细流程分析
数据包到达网卡,产生硬件中断
———————————
硬件中断处理:
rtl8139_interrupt() <8139too.c>
…
/* Receive packets are processed by poll routine.
If not running start it now. */
if (status & RxAckBits){
/* Receive packets are processed by poll routine.
If not running start it now. */
if (status & RxAckBits){
if (netif_rx_schedule_prep(dev, &tp->napi)) {
RTL_W16_F (IntrMask, rtl8139_norx_intr_mask);
__netif_rx_schedule(dev, &tp->napi);
__netif_rx_schedule(dev, &tp->napi);
}
}
…
netif_rx_schedule() <include/linux/netdevice.h>…
// 这 个函数被中断服务程序调用,将设备的POLL方法添加到网络层次的POLL处理队列中去,
// 排队并且准备接收数据包,在使用之前需要调用 netif_rx_reschedule_prep,并且返回的数为 1,
// 并且触发一个NET_RX_SOFTIRQ 的软中断通知网络层接收数据包。
/* Add interface to tail of rx poll list. This assumes that _prep has
* already been called and returned 1.
*/
static inline void __netif_rx_schedule(struct net_device *dev,
struct napi_struct *napi)
{
// 排队并且准备接收数据包,在使用之前需要调用 netif_rx_reschedule_prep,并且返回的数为 1,
// 并且触发一个NET_RX_SOFTIRQ 的软中断通知网络层接收数据包。
/* Add interface to tail of rx poll list. This assumes that _prep has
* already been called and returned 1.
*/
static inline void __netif_rx_schedule(struct net_device *dev,
struct napi_struct *napi)
{
__napi_schedule(napi);
}
/* Try to reschedule poll. Called by irq handler. */static inline void netif_rx_schedule(struct net_device *dev,
struct napi_struct *napi)
{
if (netif_rx_schedule_prep(dev, napi))
__netif_rx_schedule(dev, napi);
__netif_rx_schedule(dev, napi);
}
netif_rx_schedule_prep() <include/linux/netdevice.h>// 确定设备处于运行,而且设备还没有被添加到网络层的POLL处理队列中。
// 在调用 netif_rx_schedule之前会调用这个函数。
/* Test if receive needs to be scheduled but only if up */
static inline int netif_rx_schedule_prep(struct net_device *dev,
struct napi_struct *napi)
{
// 在调用 netif_rx_schedule之前会调用这个函数。
/* Test if receive needs to be scheduled but only if up */
static inline int netif_rx_schedule_prep(struct net_device *dev,
struct napi_struct *napi)
{
return napi_schedule_prep(napi);
}
netif_rx_ni() <net/core/dev.c>int netif_rx_ni(struct sk_buff *skb)
…
preempt_disable();// 禁止当前进程被抢占
err = netif_rx(skb);
if (local_softirq_pending()) // 检测本地处理器是否有需要处理的softirq
do_softirq();// 处理软中断
preempt_enable();
…
netif_rx() <net/core/dev.c>…
preempt_disable();// 禁止当前进程被抢占
err = netif_rx(skb);
if (local_softirq_pending()) // 检测本地处理器是否有需要处理的softirq
do_softirq();// 处理软中断
preempt_enable();
…
netif_rx() <net/core/dev.c>
- int netif_rx(struct sk_buff *skb)
- //该函数从设备驱动程序中接收数据包,并添加到上层协议的处理队列
- //所有接收到的数据包都会在这里被处理发往上层
- //在拥塞控制或协议层再中来决定是接收数据或将数据包丢弃
- …
- local_irq_save(flags);//在保存当前中断状态到 flags 之后禁止当前处理器的中断
- queue = &__get_cpu_var(softnet_data);
- __get_cpu_var(netdev_rx_stat).total++;
- if (queue->input_pkt_queue.qlen <= netdev_max_backlog) {//判断接收队列是否已满
- if (queue->input_pkt_queue.qlen) {//接收队列未满,继续判断接收队列中是否为空
- enqueue:
- //接收队列非空,即NET_RX_SOFTIRQ已设置
- //不必设置软中断,直接将数据包入队
- dev_hold(skb->dev);//Hold reference to device to keep it from being freed.
- __skb_queue_tail(&queue->input_pkt_queue, skb);
- local_irq_restore(flags);
- return NET_RX_SUCCESS;
- }
- //若接收队列为空,则使用NAPI设置NET_RX_SOFTIRQ软中断
- napi_schedule(&queue->backlog);
- goto enqueue;
- }
- //接收队列已满,数据包将被遗弃
- __get_cpu_var(netdev_rx_stat).dropped++;//统计丢弃数据包数量
- local_irq_restore(flags);//恢复中断
- …
__napi_schedule() <net/core/dev.c>
- /**
- * __napi_schedule - schedule for receive
- * @n: entry to schedule
- *
- * The entry’s receive function will be scheduled to run
- */
- void __napi_schedule(struct napi_struct *n)
- {
- unsigned long flags;
- local_irq_save(flags);
- list_add_tail(&n->poll_list, &__get_cpu_var(softnet_data).poll_list);
- __raise_softirq_irqoff(NET_RX_SOFTIRQ);
- local_irq_restore(flags);
- }
软中断处理:
net_rx_action() <net/core/dev.c>
static void net_rx_action(struct softirq_action *h)
…
if (test_bit(NAPI_STATE_SCHED, &n->state))
work = n->poll(n, weight);
// 调用NIC设备注册的poll函数获取数据,
// 对于RTL8139网卡即rtl8139_poll(该函数在中rtl8139_init_onece注册)
…
rtl8139_poll() <drivers/net/8139too.c>…
if (test_bit(NAPI_STATE_SCHED, &n->state))
work = n->poll(n, weight);
// 调用NIC设备注册的poll函数获取数据,
// 对于RTL8139网卡即rtl8139_poll(该函数在中rtl8139_init_onece注册)
…
static int rtl8139_poll(struct napi_struct *napi, int budget)
…
if (likely(RTL_R16(IntrStatus) & RxAckBits))
work_done += rtl8139_rx(dev, tp, budget);
…
rt18139_rx() <drivers/net/8139too.c>…
if (likely(RTL_R16(IntrStatus) & RxAckBits))
work_done += rtl8139_rx(dev, tp, budget);
…
rt1839_rx() <drivers/net/8139too.c>
- static int rtl8139_rx(struct net_device *dev,
- struct rtl8139_private *tp,int budget)
- //从rx_ring中接收数据,构造sk_buff并发往上层
- …
- //为分配sk_buff空间,准备保存接收的数据包
- skb = dev_alloc_skb (pkt_size + 2);
- if (likely(skb)) {
- skb_reserve (skb, 2); /* 16 byte align the IP fields. */
- #if RX_BUF_IDX == 3
- wrap_copy(skb, rx_ring, ring_offset+4, pkt_size);
- #else
//从rx_ring中复制数据 - skb_copy_to_linear_data (skb, &rx_ring[ring_offset + 4], pkt_size);
- #endif
- skb_put (skb, pkt_size);//调整skb->tial的位置
- skb->protocol = eth_type_trans (skb, dev);
- dev->last_rx = jiffies;
- tp->stats.rx_bytes += pkt_size;
- tp->stats.rx_packets++;
- netif_receive_skb (skb);//将收到的数据包发往上层
- } else {
- if (net_ratelimit())
- printk (KERN_WARNING
- "%s: Memory squeeze, dropping packet.\n",
- dev->name);
- tp->stats.rx_dropped++;
- }
- …
// ptype_all 链用于监听从网络设备上接收的所有包,通常不使用它。
// ptype_base hash表是被协议标识符弄乱的,用于决定哪个协议将接收传入的网络包。
netif_recive_skb() <net/core/dev.c>
- int netif_receive_skb(struct sk_buff *skb)
- …
- skb_reset_network_header(skb);
- skb_reset_transport_header(skb);
- skb->mac_len = skb->network_header - skb->mac_header;
- …
- //遍历ptype_all中绑定的协议处理数据包
- list_for_each_entry_rcu(ptype, &ptype_all, list) {
- if (!ptype->dev || ptype->dev == skb->dev) {
- if (pt_prev)
- ret = deliver_skb(skb, pt_prev, orig_dev);//处理数据包
- pt_prev = ptype;
- }
- }
- …
- //遍历ptype_base[ntohs(type) & PTYPE_HASH_MASK]中绑定的协议处理数据包
- type = skb->protocol;
- list_for_each_entry_rcu(ptype,
- &ptype_base[ntohs(type) & PTYPE_HASH_MASK], list) {
- if (ptype->type == type &&
- (!ptype->dev || ptype->dev == skb->dev)) {
- if (pt_prev)
- ret = deliver_skb(skb, pt_prev, orig_dev);//处理数据包
- pt_prev = ptype;
- }
- }
- if (pt_prev) {
- ret = pt_prev->func(skb, skb->dev, pt_prev, orig_dev);//处理数据包
- } else {
- //没有找到任何上层协议处理数据包,则将数据包丢弃
- kfree_skb(skb);
- /* Jamal, now you will not able to escape explaining
- * me how you were going to use this. :-)
- */
- ret = NET_RX_Drop;
- }
deliver_skb() <net/core/dev.c>
- static inline int deliver_skb(struct sk_buff *skb,
- struct packet_type *pt_prev,
- struct net_device *orig_dev)
- {
- atomic_inc(&skb->users);//skb->user中登记一个用户
- return pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
- //调用上层协议的处理函数,对于IP协议数据包,则pt_prev->func注册为ip_rcv()
- }
int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
// 对包进行基本校验:IP目标、完整性、CSUM等
// * 1. Length at least the size of an ip header
// * 2. Version of 4
// * 3. Checksums correctly. [Speed optimisation for later, skip loopback checksums]
// * 4. Doesn’t have a bogus length
// 然后数据包穿过Netfilter传到达ip_rcv_finish
…
return NF_HOOK(PF_INET, NF_INET_PRE_ROUTING, skb, dev, NULL, ip_rcv_finish);
…
ip_rcv_finish() <net/ipv4/ip_input.c>// 对包进行基本校验:IP目标、完整性、CSUM等
// * 1. Length at least the size of an ip header
// * 2. Version of 4
// * 3. Checksums correctly. [Speed optimisation for later, skip loopback checksums]
// * 4. Doesn’t have a bogus length
// 然后数据包穿过Netfilter传到达ip_rcv_finish
…
return NF_HOOK(PF_INET, NF_INET_PRE_ROUTING, skb, dev, NULL, ip_rcv_finish);
…
…
if (skb->dst == NULL) {
int err = ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, skb->dev);
// 查找路由,将路由信息写到skb->dst中,
// 如果是发往本地的数据包则skb->dst->input函数注册为ip_local_deliver处理。
…
return dst_input(skb);// 根据路由信息处理数据包
…
dst_input() <include/net/dst.h>if (skb->dst == NULL) {
int err = ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, skb->dev);
// 查找路由,将路由信息写到skb->dst中,
// 如果是发往本地的数据包则skb->dst->input函数注册为ip_local_deliver处理。
…
return dst_input(skb);// 根据路由信息处理数据包
…
static inline int dst_input(struct sk_buff *skb)
/* Input packet from network to transport. */
…
err = skb->dst->input(skb);
…
ip_local_deliver() <net/ipv4/ip_input.c>/* Input packet from network to transport. */
…
err = skb->dst->input(skb);
…
// 将IP数据包发往上层协议,数据包穿过Netfilter传到达ip_rcv_finish
ip_local_deliver() <net/ipv4/ip_input.c>
- /*
- * Deliver IP Packets to the higher protocol layers.
- */
- int ip_local_deliver(struct sk_buff *skb)
- {
- /*
- * Reassemble IP fragments.
- */
- if (ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) {
- if (ip_defrag(skb, IP_DEFRAG_LOCAL_DELIVER))
- return 0;
- }
- return NF_HOOK(PF_INET, NF_INET_LOCAL_IN, skb, skb->dev, NULL,
- ip_local_deliver_finish);
- }
ip_local_deliver_finish() <net/ipv4/ip_input.c>
- static int ip_local_deliver_finish(struct sk_buff *skb)
- {
- struct net *net = dev_net(skb->dev);
- __skb_pull(skb, ip_hdrlen(skb));
- /* Point into the IP datagram, just past the header. */
- skb_reset_transport_header(skb);
- rcu_read_lock();
- {
- int protocol = ip_hdr(skb)->protocol;
- int hash, raw;
- struct net_protocol *ipprot;
- resubmit:
- raw = raw_local_deliver(skb, protocol);//原始套接字接收数据
- hash = protocol & (MAX_INET_PROTOS - 1);
- ipprot = rcu_dereference(inet_protos[hash]);//获取协议处理结构
- if (ipprot != NULL && (net == &init_net || ipprot->netns_ok)) {
- int ret;
- if (!ipprot->no_policy) {
- // 对转发的数据包进行安全策略检查, 检查失败的话丢包
- if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
- kfree_skb(skb);
- goto out;
- }
- nf_reset(skb);
- }
- ret = ipprot->handler(skb);//调用相应的处理函数
- if (ret < 0) {
- protocol = -ret;//?
- goto resubmit;
- }
- IP_INC_STATS_BH(IPSTATS_MIB_INDELIVERS);
- } else {
- if (!raw) {
- if (xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
- IP_INC_STATS_BH(IPSTATS_MIB_INUNKNOWNPROTOS);
- icmp_send(skb, ICMP_DEST_UNREACH,
- ICMP_PROT_UNREACH, 0);
- }
- } else
- IP_INC_STATS_BH(IPSTATS_MIB_INDELIVERS);
- kfree_skb(skb);
- }
- }
- out:
- rcu_read_unlock();
- return 0;
- }
Nice Eessay!
ReplyDeleteThanks So Much!