文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>网络处理的软中断机制分析

网络处理的软中断机制分析

时间:2010-03-11  来源:cao5170

首先需要介绍的就是netif_rx(在net/core/dev.c中定义)函数,这个函数在网卡驱动程序与linux内核之间建立了一道桥梁,将网卡接收上来的数据包(sk_buff形式)插入内核维护的接收缓冲区队列当中: int netif_rx(struct sk_buff *skb) {        int this_cpu = smp_processor_id();        struct softnet_data *queue;        unsigned long flags;          if (skb->stamp.tv_sec == 0)               do_gettimeofday(&skb->stamp);        /*        获取当前处理CPU的接收数据包缓冲区队列指针。系统为每一个CPU都维护一个独立的列表,这样可以避免共享访问互斥问题。        */        queue = &softnet_data[this_cpu];          local_irq_save(flags);          netdev_rx_stat[this_cpu].total++;        /*        这里判断当前输入队列的长度是否超过预定义的一个值,如果没有超过,则向下执行。        如果当前队列的长度大于0,则将sk_buff插入队列,并且返回,注意这里并没有调用__cpu_raise_softirq产生一个软中断,而较老的内核版本当中,插入队列以后立刻调用这个函数产生软中断。        另外,如果队列长度为0,则需要调用netif_rx_schedule及后续的__netif_rx_schedule函数将当前网络设备加入softnet_data的轮询列表(poll_list)当中,这个列表维护所有网络设备的列表,当系统软中断处理函数运行时,逐个检索处于poll_list中的设备,然后调用设备的dev->poll方法处理输入数据包队列中的数据。        将设备加入poll_list列表当中后,返回enqueue标记处继续将sk_buff加入输入数据包队列中,然后返回。        */        if (queue->input_pkt_queue.qlen <= netdev_max_backlog) {               if (queue->input_pkt_queue.qlen) {                      if (queue->throttle)                             goto drop;   enqueue:                      dev_hold(skb->dev);                      /*                      将当前sk_buff插入input_pkt_queue队列的尾部,即刻返回。                      */                      __skb_queue_tail(&queue->input_pkt_queue,skb);                      local_irq_restore(flags);                      return queue->cng_level;               }                 if (queue->throttle) {                      queue->throttle = 0;               }                 netif_rx_schedule(&queue->blog_dev);               goto enqueue;        }          if (queue->throttle == 0) {               queue->throttle = 1;               netdev_rx_stat[this_cpu].throttled++;        }   drop:        netdev_rx_stat[this_cpu].dropped++;        local_irq_restore(flags);          kfree_skb(skb);        return NET_RX_DROP; }   从上面的分析可以知道,netif_rx函数主要负责将数据包插入内核队列中,并触发软中断,这一点与较早的版本是不同的,那么软中断是在什么地方触发的呢? 以前的章节介绍过,硬件中断是在irq.c的do_IRQ函数中调用handle_IRQ_event函数,进而调用相应硬件驱动程序的中断处理函数实现的。在do_IRQ函数执行完硬件处理函数以后,接着就会调用do_softirq函数执行软中断,并且根据软中断号在softirq_vec数组中查找相应中断的action方法,对于NET_RX_SOFTIRQ类型的软中断来说,系统将其action注册为net_rx_action,这样我们就进入了net_rx_action函数当中: static void net_rx_action(struct softirq_action *h) {        int this_cpu = smp_processor_id();        struct softnet_data *queue = &softnet_data[this_cpu];        unsigned long start_time = jiffies;        int budget = netdev_max_backlog;(系统每次从队列中取300个sk_buff处理)          br_read_lock(BR_NETPROTO_LOCK);        local_irq_disable();               /*        在这里循环判断当前轮询列表是否为空。如果不为空,则进入软中断处理过程。        */        while (!list_empty(&queue->poll_list)) {               struct net_device *dev;                 if (budget <= 0 || jiffies - start_time > 1)                      goto softnet_break;                 local_irq_enable();               /*               从轮询列表中取出当前的设备dev指针,接着为dev调用poll方法,这是设备初始化过程中已经定义好的方法,如果设备未能自己实现该函数,则系统默认注册为process_backlog。poll函数执行成功返回0,失败返回-1。这里逻辑判断是,dev->quota必须大于0,而poll函数执行成功,则可以继续,直到所有的设备都查询一遍为止。               */               dev = list_entry(queue->poll_list.next, struct net_device, poll_list);                 if (dev->quota <= 0 || dev->poll(dev, &budget)) {                      /* 这里的poll函数是netdevice结构的一个函数指针,对于不同的网卡驱动程序,我们可以根据自己的情况定义poll方法的实现(e1000网卡驱动程序就自己实现了一个poll方法,详情后面来分析),而系统默认提供一个方法。                      */                      local_irq_disable();                      list_del(&dev->poll_list);                      list_add_tail(&dev->poll_list, &queue->poll_list);                      if (dev->quota < 0)                             dev->quota += dev->weight;                      else                             dev->quota = dev->weight;               } else {                      dev_put(dev);                      local_irq_disable();               }        }          local_irq_enable();        br_read_unlock(BR_NETPROTO_LOCK);        return;   softnet_break:        netdev_rx_stat[this_cpu].time_squeeze++;        /*        触发软中断处理,等待下一次调用本函数。        */        __cpu_raise_softirq(this_cpu, NET_RX_SOFTIRQ);          local_irq_enable();        br_read_unlock(BR_NETPROTO_LOCK); }   软中断处理函数net_rx_action实际上就是调用各个网络设备的poll方法处理数据包的,一般的讲,poll默认为process_backlog(在net/core/dev..c中定义): static int process_backlog(struct net_device *backlog_dev, int *budget) {        int work = 0;        int quota = min(backlog_dev->quota, *budget);        int this_cpu = smp_processor_id();        struct softnet_data *queue = &softnet_data[this_cpu];        unsigned long start_time = jiffies;          for (;;) {               struct sk_buff *skb;               struct net_device *dev;                 local_irq_disable();               /*               从输入缓冲区队列中取出sk_buff,调用netif_receive_skb函数将sk_buff交给上层协议进行处理。这里就是循环调用__skb_dequeue取出skb,直到所有的skb处理完毕为止。               */               skb = __skb_dequeue(&queue->input_pkt_queue);               if (skb == NULL)                      goto job_done;               local_irq_enable();                 dev = skb->dev;                 netif_receive_skb(skb);                 dev_put(dev);                 work++;                 if (work >= quota || jiffies - start_time > 1)                      break;        } }   接下来看一下sk_buff是如何被递交到上层协议进行处理的,只是通过调用netif_receive_skb(在net/core/dev.c中定义)函数实现的: int netif_receive_skb(struct sk_buff *skb) {        struct packet_type *ptype, *pt_prev;        int ret = NET_RX_DROP;        unsigned short type = skb->protocol;               /*        给每个网络数据包打上时间戳。        */        if (skb->stamp.tv_sec == 0)               do_gettimeofday(&skb->stamp);          skb_bond(skb);          pt_prev = NULL;        /*        上层的每个协议在其初始化的过程中会调用dev_add_pack函数将自己的packet_type结构加入到ptye_all列表当中,其中packet_type结构中定义了该协议的处理方法,对于ip协议来说,func方法就注册为ip_rcv。另外,一般协议packet_type结构的dev字段设为NULL,所以下面的ptype->dev就为NULL。        另外,如果我们需要增加自己的协议,则需要创建一个packet_type结构,用我们自己的协议处理函数填充该结构的func方法,并且调用dev_add_pack函数将我们自己的协议加入ptype_all数组当中。        */        for (ptype = ptype_all; ptype; ptype = ptype->next) {               /*               这里每一种协议在定义其packet_type结构时都设置接收这种               数据包协议类型的设备指针,如果设置为NULL,则可以从 任何设备接收数据包。 这里针对协议类型为ETH_P_ALL的情况进行处理,对于IP 协议来说,类型定义为ETH_P_IP,因此不在这里处理。               */               if (!ptype->dev || ptype->dev == skb->dev) {                      if (pt_prev) {                             if (!pt_prev->data) {                                    ret = deliver_to_old_ones(pt_prev, skb, 0);                             } else {                                    atomic_inc(&skb->users);                                    ret = pt_prev->func(skb, skb->dev, pt_prev);                             }                      }                      pt_prev = ptype;               }        }        /* 这里针对各种协议进行处理,IP包的类型为ETH_P_IP,因此在这里处理。 */        for (ptype=ptype_base[ntohs(type)&15];ptype;ptype=ptype->next) {               if (ptype->type == type &&                   (!ptype->dev || ptype->dev == skb->dev)) {                      if (pt_prev) {                             if (!pt_prev->data) {                                    ret = deliver_to_old_ones(pt_prev, skb, 0);                             } else {                                    atomic_inc(&skb->users);                                    ret = pt_prev->func(skb, skb->dev, pt_prev);                             }                      }                      pt_prev = ptype;               }        }          if (pt_prev) {               if (!pt_prev->data) {                      ret = deliver_to_old_ones(pt_prev, skb, 1);               } else {                      ret = pt_prev->func(skb, skb->dev, pt_prev);               }        } 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;        }          return ret; }        在软中断处理函数当中,我们根据数据包的类型,调用相应的底层数据处理函数,对于IP包来说,就是调用ip_rcv函数并且进一步向上层协议递交和处理。至此,内核的软中断的主要过程已经结束了。        接下来我们分析一下IP层以上的网络协议栈是如何进一步处理数据的,详细的说明文档见《网络数据接收的协议处理过程分析》。
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/joshua_yu/archive/2006/01/27/589451.aspx
相关阅读 更多 +
排行榜 更多 +
弓箭手战士酷跑

弓箭手战士酷跑

飞行射击 下载
三角洲行动全面战场攀升A点进攻指南

三角洲行动全面战场攀升A点进攻指南

飞行射击 下载
僵尸射手世界大战

僵尸射手世界大战

飞行射击 下载