文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>Linux下Loopback流程分析

Linux下Loopback流程分析

时间:2010-11-23  来源:rainballdh

一.我们来看一下Loopback的初始化:

 

struct net_device loopback_dev = {
    .name             = "lo",
    .mtu            = (16 * 1024) + 20 + 20 + 12,
    .hard_start_xmit    = loopback_xmit,
    .hard_header        = eth_header,
    .hard_header_cache    = eth_header_cache,
    .header_cache_update    = eth_header_cache_update,
    .hard_header_len    = ETH_HLEN,    /* 14    */
    .addr_len        = ETH_ALEN,    /* 6    */
    .tx_queue_len        = 0,
    .type            = ARPHRD_LOOPBACK,    /* 0x0001*/
    .rebuild_header        = eth_rebuild_header,
    .flags            = IFF_LOOPBACK,
    .features         = NETIF_F_SG|NETIF_F_FRAGLIST
                 |NETIF_F_NO_CSUM|NETIF_F_HIGHDMA
                 |NETIF_F_LLTX,
    .ethtool_ops        = &loopback_ethtool_ops,
};

/* Setup and register the of the LOOPBACK device. */
int __init loopback_init(void)
{
    struct net_device_stats *stats;

    /* Can survive without statistics */
    stats = kmalloc(sizeof(struct net_device_stats), GFP_KERNEL);
    if (stats) {
        memset(stats, 0, sizeof(struct net_device_stats));
        loopback_dev.priv = stats;
        loopback_dev.get_stats = &get_stats;
    }
    
    return register_netdev(&loopback_dev);
};

Linux内核用结构体struct net_device表示一个网络设备接口,该结构体的成员hard_start_xmit是一个函数指针,用于完成数据报在网络上的发送工作,其原型是:

  int (*hard_start_xmit)( struct sk_buff *skb, struct net_device *dev );

  skb是待发送的数据缓冲区,dev是该网络设备接口本身的一个指针。

  环回设备接口由于是把数据报发给本机,所以其发送数据报函数比较特殊,它把skb稍加处理后,又转回给协议栈的数据报接收函数netif_rx。

其发送函数的函数名loopback_xmit。

二. 我们看一下loopback_xmit()函数的源码:


 

static int loopback_xmit(struct sk_buff *skb, struct net_device *dev)
{
    struct net_device_stats *lb_stats;

    skb_orphan(skb);
    skb->protocol=eth_type_trans(skb,dev);
    skb->dev=dev;
#ifndef LOOPBACK_MUST_CHECKSUM
    skb->ip_summed = CHECKSUM_UNNECESSARY;
#endif

    if (skb_shinfo(skb)->tso_size) {

        BUG_ON(skb->protocol != htons(ETH_P_IP));

        BUG_ON(skb->nh.iph->protocol != IPPROTO_TCP);



        emulate_large_send_offload(skb);

        return 0;

    }

    dev->last_rx = jiffies;
    lb_stats = &per_cpu(loopback_stats, get_cpu());
    lb_stats->rx_bytes += skb->len;
    lb_stats->tx_bytes += skb->len;
    lb_stats->rx_packets++;
    lb_stats->tx_packets++;
    put_cpu();
    netif_rx(skb);
    return(0);
}

首先,loopback_xmit调用skb_orphan把skb孤立,使它跟发送socket和协议栈不再有任何联系,也即对本机来说,这个skb的数据内容已经发送出去了,而skb相当于已经被释放掉了。

 


static inline void skb_orphan(struct sk_buff *skb)
{
    if (skb->destructor)
        skb->destructor(skb);
    skb->destructor = NULL;
    skb->sk        = NULL;
}


skb_orphan所做的实际事情是,首先从skb->sk(发送这个skb的那个socket)的sk_wmem_alloc减去skb->truesize,也即从socket的已提交发送队列的字节数中减去这个skb,表示这个skb已经发送出去了, 同时,如果有进程在这个socket上写等待,则唤醒这些进程继续发送数据报,然后把socket的引用计数减1,最后,令 sk->destructor和skb->sk都为NULL,使skb完全孤立。实际上,对于环回设备接口来说,数据的发送工作至此已经全部完成,接下来,只要把这个实际上还未被释放的skb传回给协议栈的接收函数即可。

 

如果 skb_shinfo(skb)->tso_size不为0(TSO是某些网络设备能够在传输之前把一帧大小分成更小的几帧(大小一般为MTU大小。它的实现是通过把TCP负载拆分几段,然后分别在段上加上同样的IP头。),就会调用emulate_large_send_offload()函数。emulate_large_send_offload()会调用netif_rx()函数接受数据。

 

如果tso_size 为0,则更新一下统计值,调用netif_rx()函数。

相关阅读 更多 +
排行榜 更多 +
辰域智控app

辰域智控app

系统工具 下载
网医联盟app

网医联盟app

运动健身 下载
汇丰汇选App

汇丰汇选App

金融理财 下载