对Linux kernel中 generic_hweight32函数的理解

对Linux kernel中 generic_hweight32函数的理解 收藏

generic_hweight32

Hamming weight is the number of "1" bits in the binary sequence.

/*

* hweightN: returns the hamming weight (i.e. the number

* of bits set) of a N-bit word.

*/

求32位二进制数中的'1'的个数:

static inline unsigned int generic_hweight32(unsigned int w)

{

unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555);

res = (res & 0x33333333) + ((res >> 2) & 0x33333333);

res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F);

res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF);

return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF);

}

分析:

这个函数的目的是求32位二进制数中的'1'的个数,我认为主要用到的思想就是算法中的"分治法"。以最为极端的情况来考虑

即w = 0xFFFFFFFF。

1).unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555);

这里需要注意一点:w的类型为unsigned int(无符号整型,32位),因此,当w左移时,无论w的最高位是否为1,高位都用0补充。

w = 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 UL

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

[array32] 32 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1

res = ( 0xFFFFFFFF & 0x55555555 ) + ( ( 0xFFFFFFFF >> 1 ) & 0x55555555 )

= 0x55555555 + ( 0x7FFFFFFF & 0x55555555 )

= 0x55555555 + 0x55555555

= 0xAAAAAAAA

= 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 UL

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

[array16] 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1

[array]是我为这个32位数标注的组号。

w当然就是一个有32个元素的组,我的目的不是要找到这32个元素中有多少个"1"么?好了,这里用到的最重要的思想就是:相邻组相加!!注意相邻组不是相邻位。第一次,相邻组就是1+2,3+4,5+6,... 31+32,这样加过来就从array32变成了array16了,函数的第一条语句相当于执行了16个运算!!!!

这里用图表示会更加形象:

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

w = 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 UL

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

[array32] 32 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1

| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |

->+| ->+| ->+| ->+| ->+| ->+| ->+| ->+| ->+| ->+| ->+| ->+| ->+| ->+| ->+| ->+|

V V V V V V V V V V V V V V V V

---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- --

res =1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 UL

---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- --

[array16] 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1

2). 如此将数组维度继续缩小,相邻组相加,这次是1+2, 3+4, 5+6, ... 15+16,这样将array16变为array8... 直到array中只剩下一个元素,所有的"1"就都加在了一起。

如图所示:

---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- --

res = 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 UL

---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- --

[array16] 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1

| | | | | | | | | | | | | | | |

-->+| -->+| --->+| -->+| -->+| --->+| -->+| --->+|

V V V V V V V V

---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- --

res = 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 UL

---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- --

[array8] 8 7 6 5 4 3 2 1

| | | | | | | |

--------->+| --------->+| --------->+| --------->+|

V V V V

------------------------- ------------------------ ------------------------ ------------------------ --

res = 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 UL

------------------------- ------------------------ ------------------------ ------------------------ --

[array4] 4 3 2 1

| | | |

---------------------->+| ----------------------->+|

V V

-------------------------------------------------- --------------------------------------------------- --

res = 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 UL

-------------------------------------------------- --------------------------------------------------- --

[array2] 2 1

| |

--------------------------------------------------->+|

V

------------------------------------------------------------------------------------------------------ --

res = 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 UL

------------------------------------------------------------------------------------------------------ --

[array1] 1

|

V

32

这样一来,五条语句相当于执行了16+8+4+2+1 = 31个运算了!!!!

下面是我自己写的一个模仿程序,以验证这个算法。该程序找到一个无符号8位数中"1"的个数

#include <stdio.h>

#include <linux/types.h>

static int my_hweight8( __u8 w )

{

__u8 ret = ( w & 0x55 ) + ( ( w >> 1 ) & 0x55 );

ret = ( ret & 0x33 ) + ( ( ret >> 2 ) & 0x33 );

ret = ( ret & 0x0F ) + ( ( ret >> 4 ) & 0x0F );

return (int)ret;

}__attribute__((always_inline))

int main()

{

int get_hweight8 = my_hweight8(0x6D);

printf("there are %d '1' in my 0x6D/n", get_hweight8);

return 0;

}

 

lientek@ubuntu16:~/linux/IMX6ULL/Linux_Drivers/1_chrdevbase$ make make -C /home/alientek/linux/IMX6ULL/linux/alientek_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga M=/home/alientek/linux/IMX6ULL/Linux_Drivers/1_chrdevbase modules make[1]: Entering directory '/home/alientek/linux/IMX6ULL/linux/alientek_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga' CC [M] /home/alientek/linux/IMX6ULL/Linux_Drivers/1_chrdevbase/chrdevbase.o In file included from ./arch/x86/include/asm/bitops.h:16:0, from include/linux/bitops.h:36, from include/linux/kernel.h:10, from include/linux/list.h:8, from include/linux/module.h:9, from /home/alientek/linux/IMX6ULL/Linux_Drivers/1_chrdevbase/chrdevbase.c:1: ./arch/x86/include/asm/arch_hweight.h: In function ‘__arch_hweight64’: ./arch/x86/include/asm/arch_hweight.h:53:42: error: expected ‘:’ or ‘’ before ‘POPCNT64’ asm (ALTERNATIVE("call __sw_hweight64", POPCNT64, X86_FEATURE_POPCNT ^ ./arch/x86/include/asm/alternative.h:125:28: note: in definition of macro ‘ALTINSTR_REPLACEMENT’ b_replacement(num)":\n\t" newinstr "\n" e_replacement(num) ":\n\t" ^ ./arch/x86/include/asm/arch_hweight.h:53:7: note: in expansion of macro ‘ALTERNATIVE’ asm (ALTERNATIVE("call __sw_hweight64", POPCNT64, X86_FEATURE_POPCNT ^ In file included from ./arch/x86/include/asm/pgtable_types.h:250:0, from ./arch/x86/include/asm/processor.h:18, from ./arch/x86/include/asm/thread_info.h:49, from include/linux/thread_info.h:54, from ./arch/x86/include/asm/preempt.h:6, from include/linux/preempt.h:18, from include/linux/spinlock.h:50, from include/linux/seqlock.h:35, from include/linux/time.h:5, from include/linux/stat.h:18, from include/linux/module.h:10,
03-22
<think>好的,我现在需要解决用户在编译Linux内核驱动时遇到的错误:arch/x86/include/asm/arch_hweight.h中的'error: expected ':' or ')' before 'POPCNT64''。首先,我得理解这个错误的原因。根据用户提供的引用信息,特别是引用[4],这个问题是因为在编译内核模块时没有正确指定ARCH和CROSS_COMPILE变量导致的。用户提到他们的内核版本是linux-imx-rel_imx_4.1.15_2.1.0_ga,所以这可能是一个ARM架构的编译,但错误出现在x86的路径下,这看起来有点矛盾。 接下来,我需要确认当用户编译驱动时是否正确地指定了目标架构。可能的情况是,用户正在为ARM架构(比如i.MX处理器)交叉编译驱动模块,但编译过程中错误地引用了x86的头文件,导致语法不兼容。这种情况通常是因为没有正确设置ARCH和CROSS_COMPILE环境变量,导致编译器使用了默认的x86配置。 根据引用[2]和[3],解决方案是在编译命令中加入ARCH=arm和CROSS_COMPILE=arm-linux-gnueabihf-(或类似的工具链前缀)。这确保编译时使用ARM架构的相关配置,而不是默认的x86。此外,引用[4]提到需要在主Makefile中指定这些变量,或者通过命令行传递。用户可能是在编译模块时没有正确设置这些变量,导致编译器尝试使用x86的架构头文件,而其中的POPCNT64宏可能与ARM环境不兼容。 还需要检查用户的Makefile是否正确导入了这些变量。例如,在驱动模块的Makefile中,应该设置KERNELDIR指向正确配置的内核源码路径,并且确保ARCH和CROSS_COMPILE在make命令中被正确传递。如果这些变量没有正确设置,内核的构建系统可能会错误地选择x86架构的头文件,从而导致语法错误。 另外,用户提供的引用[2]中的Makefile示例已经包含了设置ENV变量的部分,但需要确认是否实际执行时这些变量被正确应用。例如,在kernel_modules目标中,$(MAKE) $(ENV) -C $(KERNELDIR) M=$(CURRENT_PATH) modules,这里ENV应该包含ARCH和CROSS_COMPILE的定义。如果这些变量没有被正确传递,编译过程就会出错。 总结,解决方案应该是确保在编译驱动模块时,正确设置ARCH和CROSS_COMPILE变量,指定目标架构为arm,并使用对应的交叉编译工具链。同时,检查内核源码的配置是否正确,特别是主Makefile中是否有必要的变量设置。这样编译器会使用正确的架构头文件,避免x86特定的语法问题出现在ARM的编译环境中。</think>要解决Linux内核编译中出现的`arch/x86/include/asm/arch_hweight.h`文件内`error: expected ‘:’ or ‘)’ before ‘POPCNT64’`错误,需确保**正确指定目标架构和交叉编译工具链**。以下是具体步骤: --- ### **错误原因分析** 1. **架构配置错误** 该错误通常是由于内核编译时未正确指定目标架构(如`ARCH=arm`),导致编译器错误地引用了`x86`架构的头文件(如`arch_hweight.h`),而其中`POPCNT64`宏语法与当前平台不兼容[^4]。 2. **交叉编译工具链未指定** 交叉编译时缺少`CROSS_COMPILE`参数,导致编译器默认使用`x86`工具链,从而引发语法解析错误[^2][^3]。 --- ### **修复步骤** #### 1. **明确指定架构和工具链** 在编译命令中**强制指定目标架构和交叉编译工具链**: ```bash make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- ``` - **`ARCH=arm`**:声明目标架构为ARM(而非默认x86)。 - **`CROSS_COMPILE`**:指定交叉编译工具链前缀(需根据实际工具链名称调整)。 #### 2. **修改Makefile(推荐方法)** 在驱动模块的Makefile中直接定义变量: ```makefile KERNELDIR := /path/to/your/kernel/source # 替换为实际内核源码路径 CURRENT_PATH := $(shell pwd) ENV := ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- # 定义环境变量 obj-m := your_module.o build: kernel_modules kernel_modules: $(MAKE) $(ENV) -C $(KERNELDIR) M=$(CURRENT_PATH) modules clean: $(MAKE) $(ENV) -C $(KERNELDIR) M=$(CURRENT_PATH) clean ``` #### 3. **验证内核源码配置** 确保内核源码目录的`.config`文件已正确配置ARM架构,可通过以下命令生成默认配置: ```bash make ARCH=arm imx_v7_defconfig # imx_v7_defconfig为i.MX系列典型配置 ``` #### 4. **清理并重新编译** ```bash make clean make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- ``` --- ### **原理说明** - **架构一致性**:通过`ARCH=arm`确保编译时使用`arch/arm`目录下的头文件和配置,而非默认的`x86`。 - **工具链匹配**:`CROSS_COMPILE`指定ARM平台的GCC工具链,避免语法解析错误[^4]。 - **头文件隔离**:不同架构的头文件(如`arch/arm`与`arch/x86`)包含平台专用宏,错误引用会导致语法不兼容。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值