概述
以下所有分析基于linux-2.6.11
一、
根据系统是否定义“__ARMEB__”,决定调用下面哪一个宏函数:
#define set_bit(nr,p) ATOMIC_BITOP_LE(set_bit,nr,p)
#define set_bit(nr,p) ATOMIC_BITOP_BE(set_bit,nr,p)
说明:
ARMEB = ARM EABI Big-endian #大端字节序
ARMEL = ARM EABI Little-endian #小端字节序
EABI = Embedded Application Binary Interface
二、
#define ATOMIC_BITOP_LE(name,nr,p)
(__builtin_constant_p(nr) ?
____atomic_##name(nr, p) :
_##name##_le(nr,p))
#define ATOMIC_BITOP_BE(name,nr,p)
(__builtin_constant_p(nr) ?
____atomic_##name(nr, p) :
_##name##_be(nr,p))
根据参数nr是不是常量,调用不同的函数。
如果nr为常量,则调用:
____atomic_set_bit(nr, p);
函数____atomic_set_bit由C语言实现,所以不用考虑大小端的问题
如果nr为常量,则调用:
_set_bit_le(nr,p));
_set_bit_be(nr,p));
函数_set_bit_le和_set_bit_be由汇编语言实现,所以要考虑大小端的问题
说明:
int __builtin_constant_p(exp) [Built-in Function]
You can use the built-in function __builtin_constant_p to determine if a value is known to be constant at compile time and hence that GCC can perform constant folding on expressions involving that value.
常量检测
在编译时,可以使用 GCC 提供的一个内置函数判断一个值是否是常量。这种信息非常有价值,因为可以构造出能够通过常量叠算(constant folding)优化的表达式。__builtin_constant_p 函数用来检测常量。
注意,__builtin_constant_p 并不能检测出所有常量,因为 GCC 不容易证明某些值是否是常量。
三、
static inline void ____atomic_set_bit(unsigned int bit, volatile unsigned long *p)
{
unsigned long flags;
unsigned long mask = 1UL << (bit & 31);
p += bit >> 5;
local_irq_save(flags);
*p |= mask;
local_irq_restore(flags);
}
说明:
1、unsigned long mask = 1UL << (bit & 31);
因为unsigned long *p指针类型指向4个字节,此处mask的值就是对应单个指针域(4字节)内对应的位,比如bit=35,则mask=3,则具体的操作位应该位于下一个指针域的第3位。
2、p += bit >> 5;
确定指针域(每个指针域对应32位)。
四、
/*
* Purpose : Function to set a bit
* Prototype: int set_bit(int bit, void *addr)
*/
ENTRY(_set_bit_be)
eor r0, r0, #0x18 @ big endian byte ordering
ENTRY(_set_bit_le)
and r2, r0, #7
mov r3, #1
mov r3, r3, lsl r2
save_and_disable_irqs ip, r2
ldrb r2, [r1, r0, lsr #3]
orr r2, r2, r3
strb r2, [r1, r0, lsr #3]
restore_irqs ip
RETINSTR(mov,pc,lr)
函数_set_bit_be和_set_bit_le是用汇编语言实现。
通过调用set_bit(int bit, void *addr)时,参数bit传入寄存器r0,参数addr传入寄存器r1。
说明:
1、eor r0, r0, #0x18
假设对于4字节的unsigned long型数据:
小端字节序,有如下的存储形式(地址增加的方向为从左到右):
7 | ... | 0 | 15 | ... | 8 | 23 | ... | 16 | 31 | ... | 24 |
大端字节序,有如下的存储形式(地址增加的方向为从左到右):
31 | ... | 24 | 23 | ... | 16 | 15 | ... | 8 | 7 | ... | 0 |
先考虑2位二进制数异或11:
00 xor 11 = 11;
01 xor 11 = 10;
10 xor 11 = 01;
11 xor 11 = 00;
即对于2位二进制数,若 a xor 11 = b,则a+b=11。也可以理解为a若递增,则b递减。对于2位二进制数异或11,正好起到了倒序的作用!
现在再考虑语句“eor r0, r0, #0x18”,0x18对应二进制的11000,一个数异或11000,可以理解为颠倒了字节的存储顺序,但没有颠倒字节内部的存储顺序。正好解决了从大端字节序到小端字节序重新定位单个二进制位的问题!
2、and r2, r0, #7
mov r3, #1
mov r3, r3, lsl r2
只保留r0低3位,存入r2寄存器,则r2寄存器中的值对应字节内的位,r3寄存器中存入对应位的掩码。
3、ldrb r2, [r1, r0, lsr #3]
orr r2, r2, r3
strb r2, [r1, r0, lsr #3]
首先,r0右移3位,找到对应的字节“r1 + r0>>3”并把这一个字节的数据存入r2。
然后,r2与r3中保存的掩码进条或运算,置位相应的位,并把结果存入r2。
最后,把运算好的值存入正确的内存区。
最后
以上就是沉默云朵为你收集整理的linux arm set_bit分析的全部内容,希望文章能够帮你解决linux arm set_bit分析所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复