Linux内核判断大小端,linux kernel 如何处理大小端
暫時在用MPC8309,不太清楚大小端內(nèi)核是什么時候給轉(zhuǎn)的。
今天看了關(guān)于readl和writel具體實現(xiàn)的文章
今天就主要來分析下readl/writel如何實現(xiàn)高效的數(shù)據(jù)swap和寄存器讀寫。我們就以readl為例,針對big-endian處理器,如何來對寄存器數(shù)據(jù)進行處理。
kernel下readl定義如下,在include/asm-generic/io.h
#define readw(addr) __le32_to_cpu(__raw_readw(addr))
__raw_readl是最底層的寄存器讀寫函數(shù),很簡單,就從直接獲取寄存器數(shù)據(jù)。來看__le32_to_cpu的實現(xiàn),該函數(shù)針對字節(jié)序有不同的實現(xiàn),對于小端處理器,在./include/linux/byteorder/little_endian.h中,如下:
#define?__le32_to_cpu(x)?((__force?__u32)(__le32)(x))
相當(dāng)于什么都沒做。而對于大端處理器,在./include/linux/byteorder/big_endian.h中,如下:
#define?__le32_to_cpu(x)?__swab32((__force?__u32)(__le32)(x))
看字面意思也可以看出,__swab32實現(xiàn)數(shù)據(jù)翻轉(zhuǎn)。等下我們就來分析__swab32的實現(xiàn),精髓就在這個函數(shù)。
但是這之前先考慮一個問題,對于不同CPU,如arm mips ppc,怎么來選擇使用little_endian.h還是big_endian.h的呢。
答案是,針對不同處理器平臺,有arch/xxx/include/asm/byteorder.h頭文件,來看下arm mips ppc的byteorder.h分別是什么。
arch/arm/include/asm/byteorder.h
*??arch/arm/include/asm/byteorder.h
*
*?ARM?Endian-ness.??In?little?endian?mode,?the?data?bus?is?connected?such
*?that?byte?accesses?appear?as:
*??0?=?d0...d7,?1?=?d8...d15,?2?=?d16...d23,?3?=?d24...d31
*?and?word?accesses?(data?or?instruction)?appear?as:
*??d0...d31
*
*?When?in?big?endian?mode,?byte?accesses?appear?as:
*??0?=?d24...d31,?1?=?d16...d23,?2?=?d8...d15,?3?=?d0...d7
*?and?word?accesses?(data?or?instruction)?appear?as:
*??d0...d31
*/
#ifndef?__ASM_ARM_BYTEORDER_H
#define?__ASM_ARM_BYTEORDER_H
#ifdef?__ARMEB__
#include?
#else
#include?
#endif
#endif
arch/mips/include/asm/byteorder.h
/*
*?This?file?is?subject?to?the?terms?and?conditions?of?the?GNU?General?Public
*?License.??See?the?file?"COPYING"?in?the?main?directory?of?this?archive
*?for?more?details.
*
*?Copyright?(C)?1996,?99,?2003?by?Ralf?Baechle
*/
#ifndef?_ASM_BYTEORDER_H
#define?_ASM_BYTEORDER_H
#if?defined(__MIPSEB__)
#include?
#elif?defined(__MIPSEL__)
#include?
#else
#?error?"MIPS,?but?neither?__MIPSEB__,?nor?__MIPSEL__???"
#endif
#endif?/*?_ASM_BYTEORDER_H?*/
arch/powerpc/include/asm/byteorder.h
#ifndef?_ASM_POWERPC_BYTEORDER_H
#define?_ASM_POWERPC_BYTEORDER_H
/*
*?This?program?is?free?software;?you?can?redistribute?it?and/or
*?modify?it?under?the?terms?of?the?GNU?General?Public?License
*?as?published?by?the?Free?Software?Foundation;?either?version
*?2?of?the?License,?or?(at?your?option)?any?later?version.
*/
#include?
#endif?/*?_ASM_POWERPC_BYTEORDER_H?*/
可以看出arm mips在kernel下大小端都支持,arm mips也的確是可以選擇處理器字節(jié)序。ppc僅支持big-endian。(其實ppc也是支持選擇字節(jié)序的)
各個處理器平臺的byteorder.h將littlie_endian.h/big_endian.h又包了一層,我們在編寫driver時不需要關(guān)心處理器的字節(jié)序,只需要包含byteorder.h即可。
接下來看下最關(guān)鍵的__swab32函數(shù),如下:
在include/linux/swab.h中
/**
*?__swab32?-?return?a?byteswapped?32-bit?value
*?@x:?value?to?byteswap
*/
#define?__swab32(x)?????????????\
(__builtin_constant_p((__u32)(x))???\
___constant_swab32(x)?:?????????\
__fswab32(x))
宏定義展開,是一個條件判斷符。
__builtin_constant_p是一個gcc的內(nèi)建函數(shù), 用于判斷一個值在編譯時是否是常數(shù),如果參數(shù)是常數(shù),函數(shù)返回 1,否則返回 0。
如果數(shù)據(jù)是常數(shù),則__constant_swab32,實現(xiàn)如下:
#define?___constant_swab32(x)?((__u32)(?????????????\
(((__u32)(x)?&?(__u32)0x000000ffUL)?<
(((__u32)(x)?&?(__u32)0x0000ff00UL)?<
(((__u32)(x)?&?(__u32)0x00ff0000UL)?>>??8)?|????????\
(((__u32)(x)?&?(__u32)0xff000000UL)?>>?24)))
對于常數(shù)數(shù)據(jù),采用的是普通的位移然后拼接的方法,對于常數(shù),這樣的消耗是有必要的(這是kernel的解釋,不是很理解)
如果數(shù)據(jù)是運行時計算數(shù)據(jù),則使用__fswab32,實現(xiàn)如下:
static?inline?__attribute_const__?__u32?__fswab32(__u32?val)
{
#ifdef?__arch_swab32
return?__arch_swab32(val);
#else
return?___constant_swab32(val);
#endif
}
如果未定義__arch_swab32,則還是采用__constant_swab32方法翻轉(zhuǎn)數(shù)據(jù),但是arm mips ppc都定義了各自平臺的__arch_swab32,來實現(xiàn)一個針對自己平臺的高效的swap,分別定義如下:
arch/arm/include/asm/swab.h
static?inline?__attribute_const__?__u32?__arch_swab32(__u32?x)
{
__asm__?("rev?%0,?%1"?:?"=r"?(x)?:?"r"?(x));
return?x;
}
arch/mips/include/asm/swab.h
static?inline?__attribute_const__?__u32?__arch_swab32(__u32?x)
{
__asm__(
"???wsbh????%0,?%1??????????\n"
"???rotr????%0,?%0,?16??????\n"
:?"=r"?(x)
:?"r"?(x));
return?x;
}
arch/powerpc/include/asm/swab.h
static?inline?__attribute_const__?__u32?__arch_swab32(__u32?value)
{
__u32?result;
__asm__("rlwimi?%0,%1,24,16,23\n\t"
"rlwimi?%0,%1,8,8,15\n\t"
"rlwimi?%0,%1,24,0,7"
:?"=r"?(result)
:?"r"?(value),?"0"?(value?>>?24));
return?result;
}
可以看出,arm使用1條指令(rev數(shù)據(jù)翻轉(zhuǎn)指令),mips使用2條指令(wsbh rotr數(shù)據(jù)交換指令),ppc使用3條指令(rlwimi數(shù)據(jù)位移指令),來完成了32 bit數(shù)據(jù)的翻轉(zhuǎn)。這相對于普通的位移拼接的方法要高效的多!
其實從函數(shù)名__fswab也可以看出是要實現(xiàn)fast swap的。
原文:http://www.cnblogs.com/yangv/p/5553717.html
總結(jié)
以上是生活随笔為你收集整理的Linux内核判断大小端,linux kernel 如何处理大小端的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 4GB内存+64GB存储,够用吗?手机专
- 下一篇: 电脑发烧友必看!升级790i芯片组内存选