关于bit操作(bit_set test_bit)

 

   今天发现一个奇怪的现象,用linux内核的bitops.h里面的bit_set和test_bit居然没有不用汇编的代码快。记录下来,备忘。
  
  下面是可执行程序,THUIRDB暂时没有bitops里面的实现,改用我自己写的实现了。当然bitops里的实现,nr可以很大,而我的代码nr只能是0-63之间的一个数,对THUIRDB的应用来说,足够了。
#include "stdint.h"
#include <iostream>
using namespace std;
#define BITOP_ADDR(x) "=m" (*(volatile long *) (x))
#define ADDR                            BITOP_ADDR(addr)
#ifdef CONFIG_SMP
#define LOCK_PREFIX /
                ".section .smp_locks,/"a/"/n"   /
                _ASM_ALIGN "/n"                 /
                _ASM_PTR "661f/n" /
                ".previous/n"                   /
                "661:/n/tlock; "
#else
#define LOCK_PREFIX ""
#endif
static inline void
set_bit(unsigned int nr, volatile unsigned long *addr)
{
        __asm__ volatile(LOCK_PREFIX "bts %1,%0"
                   : BITOP_ADDR(addr) : "Ir" (nr) : "memory");
}
static inline bool test_bit(int nr, volatile const unsigned long *addr)
{
        int oldbit;
        asm volatile("bt %2,%1/n/t"
                     "sbb %0,%0"
                     : "=r" (oldbit)
                     : "m" (*(unsigned long *)addr), "Ir" (nr));
        return oldbit!=0;
}
static  void _set_bit(int nr, uint64_t* addr)
{
        addr[0] |= ( 1UL << nr);
};
static bool _test_bit(int nr,uint64_t* addr)
{
        return  (0 < (addr[0] & (1UL<<nr)));
};
int main(void)
{
        uint64_t bit_arr=0;
        #ifdef T1
        for(int i = 0; i<1000000;++i)
        {
                _set_bit(id,&bit_arr);
        }
        #endif
        #ifdef T2
        for(int i = 0; i<1000000;++i)
        {
                set_bit(id,&bit_arr);
        }
        #endif
        #ifdef T3
        for(int i = 0; i<1000000;++i)
        {
                _test_bit(id,&bit_arr);
        }
        #endif
        #ifdef T4
        for(int i = 0; i<1000000;++i)
        {
                test_bit(id,&bit_arr);
        }
        #endif
        return 0;
}
以下是反汇编的结果,可以看出用内核的方法,代码短小,但效率较低,用移位与或的方法代码多,但效率高。
000000000040067c <_ZL7set_bitjPVm>: //set_bit
  40067c:       55                      push   %rbp
  40067d:       48 89 e5                mov    %rsp,%rbp
  400680:       89 7d fc                mov    % edi,-0x4(%rbp)
  400683:       48 89 75 f0             mov    %rsi,-0x10(%rbp)
  400687:       48 8b 45 f0             mov    -0x10(%rbp),%rax
  40068b:       8b 55 fc                mov    -0x4(%rbp),% edx
  40068e:       0f ab 10                bts    % edx,(%rax)
  400691:       c9                      leaveq 
  400692:       c3                      retq   
0000000000400693 <_ZL8test_bitiPVKm>://test_bit
  400693:       55                      push   %rbp
  400694:       48 89 e5                mov    %rsp,%rbp
  400697:       89 7d ec                mov    % edi,-0x14(%rbp)
  40069a:       48 89 75 e0             mov    %rsi,-0x20(%rbp)
  40069e:       48 8b 45 e0             mov    -0x20(%rbp),%rax
  4006a2:       8b 55 ec                mov    -0x14(%rbp),% edx
  4006a5:       0f a3 10                bt     % edx,(%rax)
  4006a8:       19 c0                   sbb    % eax,% eax
  4006aa:       89 45 fc                mov    % eax,-0x4(%rbp)
  4006ad:       83 7d fc 00             cmpl   $0x0,-0x4(%rbp)
  4006b1:       0f 95 c0                setne  %al
  4006b4:       c9                      leaveq 
  4006b5:       c3                      retq   
00000000004006b6 <_ZL8_set_bitiPm>://_set_bit
  4006b6:       55                      push   %rbp
  4006b7:       48 89 e5                mov    %rsp,%rbp
  4006ba:       53                      push   %rbx
  4006bb:       89 7d f4                mov    % edi,-0xc(%rbp)
  4006be:       48 89 75 e8             mov    %rsi,-0x18(%rbp)
  4006c2:       48 8b 45 e8             mov    -0x18(%rbp),%rax
  4006c6:       48 8b 10                mov    (%rax),%rdx
  4006c9:       8b 45 f4                mov    -0xc(%rbp),% eax
  4006cc:       bb 01 00 00 00          mov    $0x1,% ebx
  4006d1:       48 89 de                mov    %rbx,%rsi
  4006d4:       89 c1                   mov    % eax,% ecx
  4006d6:       48 d3 e6                shl    %cl,%rsi
  4006d9:       48 89 f0                mov    %rsi,%rax
  4006dc:       48 09 c2                or     %rax,%rdx
  4006df:       48 8b 45 e8             mov    -0x18(%rbp),%rax
  4006e3:       48 89 10                mov    %rdx,(%rax)
  4006e6:       5b                      pop    %rbx
  4006e7:       c9                      leaveq 
  4006e8:       c3                      retq   
00000000004006e9 <_ZL9_test_bitiPm>://_test_bit
  4006e9:       55                      push   %rbp
  4006ea:       48 89 e5                mov    %rsp,%rbp
  4006ed:       53                      push   %rbx
  4006ee:       89 7d f4                mov    % edi,-0xc(%rbp)
  4006f1:       48 89 75 e8             mov    %rsi,-0x18(%rbp)
  4006f5:       48 8b 45 e8             mov    -0x18(%rbp),%rax
  4006f9:       48 8b 10                mov    (%rax),%rdx
  4006fc:       8b 45 f4                mov    -0xc(%rbp),% eax
  4006ff:       48 89 d3                mov    %rdx,%rbx
  400702:       89 c1                   mov    % eax,% ecx
  400704:       48 d3 eb                shr    %cl,%rbx
  400707:       48 89 d8                mov    %rbx,%rax
  40070a:       83 e0 01                and    $0x1,% eax
  40070d:       5b                      pop    %rbx
  40070e:       c9                      leaveq 
  40070f:       c3                      retq   
T2用-O2优化后的代码,内联到main里面从400910到40093b
  

 

### 关于 `Bit_SET` 等于 0 的含义 在 IT 场景中,`Bit_SET` 并不是一个标准术语,但从上下文中推测,它可能指的是某个特定比特位被设置为 1 或者未被设置(即等于 0)。通常情况下,在 C/C++ 编程以及操作系统内核开发中,涉及位操作的宏定义和函数会用来管理硬件寄存器、标志状态或其他二进制数据结构。 当某一位被设置为 0 时,其具体含义取决于该位的设计用途。以下是几种常见的场景及其解释: #### 1. **标志位清零** 如果某一比特位表示某种状态或条件的存在与否,则将其设为 0 表示清除该状态或取消某些功能启用的状态。例如: ```c #define FLAG_ENABLED_BIT 0 // 假设第0位代表功能是否开启 unsigned long flags = 0x0F; // 初始值 clear_bit(FLAG_ENABLED_BIT, &flags); // 将第0位清零 ``` 这里通过调用 `clear_bit()` 函数将指定位置为 0[^2],从而关闭对应的功能或状态。 #### 2. **测试并重置** 使用 `test_and_clear_bit()` 可以先读取当前位的值再将其重置为 0。这常用于同步机制或者资源释放过程中。 ```c if (test_and_clear_bit(bit_number, address)) { printk(KERN_INFO "Bit was previously set.\n"); } ``` 上述代码片段展示了如何检测某位是否已设置,并随后将其清零[^5]。 #### 3. **初始化阶段** 在程序启动初期,可能会将整个字节序列中的所有位都初始化为 0 来确保干净的数据环境。这种做法常见于设备驱动加载期间对 I/O 寄存器的操作。 ```c memset(flags_array, 0, sizeof(flags_array)); // 批量清零 ``` --- ### 如何处理 `Bit_SET` 为 0 的情况? 针对不同的应用场景和技术需求,有多种方法来应对这种情况: #### 方法一:错误恢复逻辑 当发现目标位处于未激活状态(即为 0),可以根据业务规则决定采取何种措施进行修复或通知用户。比如在网络协议栈实现中遇到连接超时时可尝试重新建立链接。 #### 方法二:轮询等待策略 对实时性强的任务来说,持续监测直到满足预期条件为止是一种可行方案。下面是一个简单的例子说明如何利用循环配合延迟达到目的: ```c while (!test_bit(target_bit, status_register)) { usleep_range(min_delay_us, max_delay_us); if(timeout_exceeded()) break; } ``` 此处假设存在一个外部事件最终会使 target_bit 被触发变为高电平[^3]。 #### 方法三:中断驱动模型 更高效的替代方式是采用异步通知机制而非主动查询法。一旦关联硬件完成相应动作就会自动引发 CPU 中断进而执行预定义的服务例程更新软件副本里的映像信息。 --- ### 总结 综上所述,“Bit_SET 是否为 0”的实际意义需依据具体的软硬件设计而定;而在面对此类状况之时则可通过诸如上述提及的各种手段加以妥善处置。 ```python def simulate_bit_operation(): """ A Python simulation of setting/clearing bits. This is purely illustrative and does not represent actual kernel code behavior. """ flags = 0b1111 # Initial state with all four lower bits set def set_bit(n): nonlocal flags flags |= (1 << n) def clear_bit(n): nonlocal flags flags &= ~(1 << n) def check_bit(n): return bool(flags & (1 << n)) print(f'Initial Flags: {bin(flags)}') # Clear the second bit (index starts at 0) clear_bit(1) print(f'After clearing bit 1: {bin(flags)}') simulate_bit_operation() ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值