首页
社区
课程
招聘
求 Interlocked 系互锁函数的真相
发表于: 2010-6-7 11:43 5452

求 Interlocked 系互锁函数的真相

2010-6-7 11:43
5452
以 InterlockedExchange 为例

由 kernel32.dll 实现的 InterlockedExchange 在文档中有那么一句话:
This function is atomic with respect to calls to other interlocked functions.

具体实现如下:


WDK 的文档中对于 InterlockedExchange 有那么一句话:
A call to InterlockedExchange routine is atomic only with respect to other InterlockedXxx calls.

ntoskrnl.exe 中 InterlockedExchange 的实现如下:


关于 lock 前缀指令, Intel 手册里是那么说的:
Causes the processor’s LOCK# signal to be asserted during execution of the accompanying instruction (turns the instruction into an atomic instruction). In a multiprocessor environment, the LOCK# signal ensures that the processor has exclusive use of any shared memory while the signal is asserted.

The XCHG instruction always asserts the LOCK# signal regardless of the presence or absence of the LOCK prefix.

也就是说实际上两个 InterlockedExchange 都是靠 lock 指令实现的.

然后在下面这个网页里看到了关于 lock 指令的一种说法:
4dcK9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6h3y4G2k6r3g2E0j5h3g2K6N6s2u0G2i4K6u0W2j5$3!0E0i4K6u0r3M7X3g2$3K9h3g2%4M7#2)9J5c8U0R3`.

All threads and processes accessing the locked memory must ‘play fair’. This means that any one accessing the memory without trying to lock the address will not be protected by other processes locking the same address. For example:
Thread 1: lock inc ptr [edx]
Thread 2: inc ptr [edx]
If the two threads run simultaneously, thread 2 does not assert LOCK#. This means that it doesn’t check if the memory pointed by edx is locked. This means the lock is useless in this matter.

照这样说就变得跟其他一些同步方式一样了, 光自己用别人不用的话也没效果.

那么这个说法到底对吗?

主要是看到了 WDK 文档里关于 InterlockedExchange 的描述中那个 only 让我觉得似乎是在佐证这个说法.

如果对的话是不是意味着 Rootkits 那本书里那些 SSDT HOOK 的宏其实是有点问题的?
还是需要锁住每个核才是相对比较可靠的内核 patch 做法?

谢谢

[培训]科锐逆向工程师培训第53期2025年7月8日开班!

收藏
免费 0
支持
分享
最新回复 (6)
雪    币: 246
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
2
这个问题的实质是所谓的“原子操作”

    “原子操作”是指不可分割的、在执行完毕前不会被任何其它任务或事件中断的操作。

    在单CPU系统中,能够在单条指令中完成的操作都是"原子操作",因为中断只能发生于指令之间。

    但是,在多CPU结构中就不同了,由于系统中有多个CPU在独立地运行,即使能在单条指令中完成的操作也有可能受到干扰。我们以decl (递减指令)为例,这是一个典型的"读-改-写"过程,涉及两次内存访问。设想在不同CPU运行的两个进程都在递减某个内存单元的数值2,可能发生的情况是:
  1. CPU A从内存单元把当前数值2装载进它的寄存器中;
  2. CPU B从内存单元把当前数值2装载进它的寄存器中。
  3. CPU A在它的寄存器中将计数值递减为1;
  4. CPU B在它的寄存器中将计数值递减为1;
  5. CPU A把修改后的计数值1写回内存单元。
  6. CPU B把修改后的计数值1写回内存单元。
  我们看到,内存里的计数值应该是0,然而它却是1。

    可见原子操作不可能由软件单独保证--必须需要硬件的支持,因此是和架构相关的。在x86 平台上,CPU提供了在指令执行期间对总线加锁的手段。CPU芯片上有一条引线#HLOCK pin,如果汇编语言的程序中在一条指令前面加上前缀"LOCK",经过汇编以后的机器代码就使CPU在执行这条指令的时候把#HLOCK pin的电位拉低,持续到这条指令结束时放开,从而把总线锁住,这样同一总线上别的CPU就暂时不能通过总线访问内存了,保证了这条指令在多处理器环境中的原子性---这正是Interlocked 系互锁函数的本质和真象。
2010-6-8 16:21
0
雪    币: 246
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
3
互斥锁和原子操作:

    互斥锁是用来保证共享数据操作的完整性的。每个对象都对应于一个可称为" 互斥锁" 的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。可见互斥锁是软件实现的,它的开销比实现原子操作要大的多。
    因此:
    a、在确认一个操作是原子的情况下,多线程环境里面,我们可以避免仅仅为保护这个操作在外围加上性能开销昂贵的互斥锁。
    b、借助于原子操作,我们可以实现互斥锁。
    c、借助于互斥锁,我们可以把一系列操作变为原子操作。
2010-6-8 16:42
0
雪    币: 5
活跃值: (15)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
就x86平台而言, cpu支持lock汇编指令(参考CPU SPEC),操作时硬件锁定总线,其他访问被挂起等待该操作完成
2010-6-8 17:25
0
雪    币: 249
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
finally, 终于有回复, 十分感谢

那么 3e4K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6h3y4G2k6r3g2E0j5h3g2K6N6s2u0G2i4K6u0W2j5$3!0E0i4K6u0r3M7X3g2$3K9h3g2%4M7#2)9J5c8U0R3`. 链接中

All threads and processes accessing the locked memory must ‘play fair’. This means that any one accessing the memory without trying to lock the address will not be protected by other processes locking the same address. For example:
Thread 1: lock inc ptr [edx]
Thread 2: inc ptr [edx]
If the two threads run simultaneously, thread 2 does not assert LOCK#. This means that it doesn’t check if the memory pointed by edx is locked. This means the lock is useless in this matter.

这一段的说法就是不对的咯?
2010-6-8 17:46
0
雪    币: 246
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
6
o, 前面解释的可能不太全面和准确,其实这个问题一般的人都不大关注,看来LZ是个非常认真的人。应当这样:
      如果汇编语言的程序中在一条指令前面加上前缀"LOCK",经过汇编以后的机器代码就使CPU在执行这条指令的时候把#HLOCK pin的电位拉低,持续到这条指令结束时放开,从而“把总线锁住“,这样同一总线上别的CPU就暂时不能通过总线访问内存了,保证了这条指令在多处理器环境中的原子性。
     这里所说的是“把总线锁住”是相对于其他有lock前缀的指令,而其他普通指令还是可以照常运行的,否则系统不是要为一条指令整个都停下来。也就是说lock前缀有两个功能:1、拉低#HLOCK信号,2、在拉低之前要检查#HLOCK信号,在低的时候要等待,搞的时候才可执行。

     所以对于共享内存的操作要“play fair”,

     所以,在不合适的地方、不合适的指令使用LOCK前缀,会导致未定义的操作代码例外(#UD)。
   
     另外,从 P6 处理器开始,如果指令访问的内存区域已经存在于处理器的内部缓存中,则“lock” 前缀并不将引线 LOCK 的电位拉低,而是锁住本处理器的内部缓存,然后依靠缓存一致性协议保证操作的原子性。   
  
    再者,CPU的设计者也会考虑之所定该所定的内存地址,而其他可以正常访问;也会考虑次CPU的一、二缓存问题;。。。。
2010-6-8 21:32
0
雪    币: 249
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
十分感谢, 这下完全明白了.
2010-6-8 22:07
0
游客
登录 | 注册 方可回帖
返回