Linux协议栈——compare_ether_addr_64bits函数分析

从陈老哥的博文中,获得一种新体验。在学习kernel代码的时候,不仅要注重其内在的流程和架构,也可以多多关注一下代码的实现细节,从小的地方去学习编程之道。

今天以一个小函数compare_ether_addr_64bits,作为一个尝试。

static inline unsigned compare_ether_addr_64bits(const u8 addr1[6+2],
                         const u8 addr2[6+2])
{
#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
    unsigned long fold = ((*(unsigned long *)addr1) ^
             (*(unsigned long *)addr2));

    if (sizeof(fold) == 8)
        return zap_last_2bytes(fold) != 0;

    fold |= zap_last_2bytes((*(unsigned long *)(addr1 + 4)) ^
                (*(unsigned long *)(addr2 + 4)));
    return fold != 0;
#else
    return compare_ether_addr(addr1, addr2);
#endif
}

该函数用于比较ethernet 网卡地址,即MAC地址是否相同。MAC为6字节长度,为什么这个函数的参数声明为[6+2]呢?并且作为一个形参的数组,其实际上就是一个指针类型。那么这里的6+2并没有什么实际意义。难道是为了体现对齐?

这个函数的目的是为了取代memcmp(addr1, addr2, ETH_ALEN)的用法。因为按照这个函数的实现,是不需要有任何branch的。

看条件编译宏CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS。当平台对于不对齐的内存访问,性能不错时,该宏为enabled的;否则为disabled。

先看CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS enable的时候。将addr1和addr2强制转换为unsigned long型,并进行异或运算。看到这里,应该就可以明白为什么参数addr1和addr2要声明为[6+2]的字节数组了。因为64位的机器上,long为8个字节。所以addr1和addr2需要有8个有效的字节。尽管其实质为一个指针地址,但是从形参的定义上看,可以告诉读者,这个addr1和addr2是需要有8个合法可访问的字节的,尽管有用的只有6个。所以才写成了6+2的形式。当得到异或的结果fold后,下面判断sizeof(fold)是否为8。如果为8,那么调用zap_last_2bytes函数舍去后面的两个字节,再判断结果是否为0。如为0,表明该MAC地址相等。如果sizeof(fold)为不为8,那就是为4了。就再次对后面4个字节做异或,并舍去最后2个字节。相当于将MAC地址分为两部分分别判断,最后将结果相或。这里虽然有一个sizeof的判断,但是由于这里的sizeof的值是在编译期间可以确定的值,因此编译器肯定会对其进行优化。因此在最终的二进制代码中,不会有branch的跳转代码。

那么当CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS disable的时候,进入函数compare_ether_addr

static inline unsigned compare_ether_addr(const u8 *addr1, const u8 *addr2)
{
    const u16 *a = (const u16 *) addr1;
    const u16 *b = (const u16 *) addr2;

    BUILD_BUG_ON(ETH_ALEN != 6);
    return ((a[0] ^ b[0]) | (a[1] ^ b[1]) | (a[2] ^ b[2])) != 0;
}

这个代码非常简单。将addr1和addr2转换为u16*的类型,然后每两个字节做异或,最后将结果或起来,检查是否为0。光看这个代码,其实我认为有一点问题。这里的addr1和addr2为u8*类型,然后强制转换为u16*类型。对于某些平台,如果addr1和addr2不是2字节对齐的话,这样的强制转换会引起问题,甚至crash。不过我想,因为这里的比较都是针对mac地址的比较,可以保证其为2字节对齐的。所以这里既没有注释,也没有字节对齐的处理。在compare_ether_addr_64bits的注释中,反而说明了参数要求2字节对齐。

下面列一下不同情况,compare_ether_addr_64bits需要的操作:
1. CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS enable且64位机:一次异或,一次移位,一次比较;
2. CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS enable且32位机:两次异或,一次移位,一次或运算,一次比较;
3. CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS disable:三次异或,两次或运算,一次比较;

本文转载自http://blog.chinaunix.net/uid-23629988-id-3248661.html 作者:gfree.wind@gmail.com

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值