linux内核中ffs(x)宏是平台相关的宏,在arm平台,该宏定义在

arch/arm/include/asm/bitops.h

#define ffs(x) ({ unsigned long __t = (x); fls(__t & -__t); })
static inline int fls(int x)
{
int ret; if (__builtin_constant_p(x))
return constant_fls(x); asm("clz\t%0, %1" : "=r" (ret) : "r" (x) : "cc");
ret = - ret;
return ret;
}

__t & -__t   等于找到__t 第一个为1的位(从低位开始),并把该位保留为1其余位清0.

例如 一32位整形数 6,用二进制表示它的低8位:00000110,  都知道负数为最高为1其余位取反加1.-6即 11111010

相与得 00000010,即6&-6. 把该值传递给函数fls().

再看fls函数.

if (__builtin_constant_p(x))
           return constant_fls(x);

__builtin_constant_p 是Gcc的内建函数 ,用于判断一个值是否为编译时常数,如果参数的值是常数,函数返回 1,否则返回 0。

如果是常数就用下面函数计算00000010中1的位置

static inline int constant_fls(int x)
{
int r = ; if (!x)
return ;
if (!(x & 0xffff0000u)) {
x <<= ;
r -= ;
}
if (!(x & 0xff000000u)) {
x <<= ;
r -= ;
}
if (!(x & 0xf0000000u)) {
x <<= ;
r -= ;
}
if (!(x & 0xc0000000u)) {
x <<= ;
r -= ;
}
if (!(x & 0x80000000u)) {
x <<= ;
r -= ;
}
return r;
}

算法就是折半法,这个函数计算的是从高位起第一个1位的位置.00000010, r=2.  由于__t&-__t只有一个1,所以就是找到该1的位置.

如果输入参数是00001010 那么 r=4.

如果参数是变量,直接使用arm指令运算.

执行   asm("clz\t%0, %1" : "=r" (ret) : "r" (x) : "cc");
           ret = - ret;
CLZ(Count Leading Zeros)指令对Rm中值的高位(leading zeros)个数进行计数,结果放到Rd中。若源寄存器全为0,则结果为32。若[31]为1,则结果为0。

clz指令计算高位0的个数, 然后拿32-ret 算出1的位置.

所以,ffs(x)这个宏的值就是x的第一个1的位置(从低位开始,数值从1开始,0代表x全0).

另外,该文件中还有很多linux关于位操作的函数,可以作为自己写应用程序时的有用参考.

linux内核中的宏ffs(x)的更多相关文章

  1. 剖析linux内核中的宏---------container_of

    #define container_of(ptr, type, member) ({ \ const typeof(((type *)0)->member) * __mptr = (ptr); ...

  2. Linux内核中container_of宏的详细解释

    上一节拒绝造轮子!如何移植并使用Linux内核的通用链表(附完整代码实现)我们在分析Linux内核链表的时候注意到内核在求解结构体偏移的时候巧妙的使用了container_of宏定义,今天我们来详细剖 ...

  3. Linux内核中的宏:__init and __exit

    ZZ FROM: http://blog.csdn.net/musein/article/details/742609 ======================================== ...

  4. 剖析linux内核中的宏-----------offsetof

    offsetof用于计算TYPE结构体中MEMBER成员的偏移位置. #ifndef offsetof#define offsetof(TYPE, MEMBER) ((size_t) &((T ...

  5. Linux内核中的fastcall和asmlinkage宏

    代码中看见:#define _fastcall 所以了解下fastcall -------------------------------------------------------------- ...

  6. (十)Linux内核中的常用宏container_of

    Container_of在Linux内核中是一个常用的宏,用于从包含在某个结构中的指针获得结构本身的指针,通俗地讲就是通过结构体变量中某个成员的首地址进而获得整个结构体变量的首地址. Containe ...

  7. Linux内核中的常用宏container_of

    Container_of在Linux内核中是一个常用的宏,用于从包含在某个结构中的指针获得结构本身的指针,通俗地讲就是通过结构体变量中某个成员的首地址进而获得整个结构体变量的首地址. Containe ...

  8. Linux内核中的常用宏container_of其实很简单【转】

    转自:http://blog.csdn.net/npy_lp/article/details/7010752 开发平台:Ubuntu11.04 编 译器:gcc version 4.5.2 (Ubun ...

  9. 向linux内核中添加外部中断驱动模块

    本文主要介绍外部中断驱动模块的编写,包括:1.linux模块的框架及混杂设备的注册.卸载.操作函数集.2.中断的申请及释放.3.等待队列的使用.4.工作队列的使用.5.定时器的使用.6.向linux内 ...

随机推荐

  1. (转)Hibernate框架基础——映射普通属性

    http://blog.csdn.net/yerenyuan_pku/article/details/52739871 持久化对象与OID 对持久化对象的要求 提供一个无参的构造器.使Hibernat ...

  2. CAD控件:QT开发使用控件入门

    1. 环境搭建: 3 1.1. 安装Qt 3 1.2. 安装Microsoft Windows SDK的调试包 6 2. QT中使用MxDraw控件 7 1.3. 引入控件 7 3. 打开DWG文件 ...

  3. returnValue of Chrome

    说实话,我一看到这个returnValue就有点反感,感觉这个就是IE式的老套的用法,因为项目中有用到就了解了下,以下主要是一些我的理解和发现吧. PS:returnValue是window的属性,s ...

  4. 【汇总】java中数组的声明、初始化及遍历

    java中数组用来存储固定大小的同类型元素 一维数组: 1.数组的声明: //声明一维数组,推荐用第一种 int[] a; int b[]; 2.数据的初始化:有三种初始化方式 (1).静态初始化 / ...

  5. oracle打开或者关闭flashback

    1.打开flashback: 关闭数据库 SQL>shutdown immediate; 启动到mount方式 SQL>startup mount; 如果归档没有打开,打开归档[因为fla ...

  6. linux diff3-比较3个文件不同的地方

    推荐:更多Linux 文件查找和比较 命令关注:linux命令大全 diff3命令用于比较3个文件,将3个文件的不同的地方显示到标准输出. 语法 diff3(选项)(参数) 选项 -a:把所有的文件都 ...

  7. orcad中注意的事情

    1.地的标识不能放到已经分配了网络的线上. 在用orcad画原理图的时候,把电源放到网络的时候需要特别的注意,如果,将电源地直接放到线上,而这根线又已经被分配了网络标号,那这个地会随已经分配了的网络号 ...

  8. unigui的ini文件读写【6】

    procedure THeaderFooterForm.writerParas; var IniFile : TIniFile; begin try IniFile:=TIniFile.Create( ...

  9. 文化之旅 2012年NOIP全国联赛普及组

    时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题目描述 Description 有一位使者要游历各国,他每到一个国家,都能学到一种文化,但他不愿意学习任何一种文化超 ...

  10. 【BZOJ3790】神奇项链(manacher,树状数组)

    题意: 思路:生成一些回文拼起来使生成的段数最小 显然存在一种最优的方案,使生成的那些回文是目标串的极长回文子串 求出对于每个位置的最长回文子串,问题就转化成了: 给定一些已知起始和终止位置的线段,求 ...