首先重现环境是 .Net Core 2.0 Windows (x86) Binaries, 下载可以到 https://www.microsoft.com/net/download/core#/sdk
重现的代码如下
using System;
using System.Threading;
namespace x86program
{
class Program
{
static void Main(string[] args)
{
Console.ReadLine();
Console.WriteLine("Hello World!");
Console.ReadLine();
long a = 0x1234567887564321;
Console.WriteLine(Interlocked.Increment(ref a));
}
}
}
需要的工具:
Visual Studio 2017 15.3 RTM
OllyDbg 1.1 (or 2.0)
首先下载了x86版的dotnet后,解压然后在命令行运行
F:\dotnet-sdk-2.0.0-win-x86\x86program>..\dotnet.exe run
运行后使用Visual Studio打开项目然后"附加到进程", 成功后在下图中的地方下断点
然后命令行回车, 可以触发此断点, 我们可以看到 Interlocked.Increment 调用了 0x76A7CA0 处的函数, 传入参数只有一个, 就是指向long变量的指针
之后在Visual Studio中取消附加, 然后使用ollydbg的Attach, 成功后在 0x76A7CA0 下断点
这个函数调用了函数 0x7428BE0, 传入了分割为两个int的long (1), ecx仍然指向原来的long变量
函数 0x7428BE0 是一个JIT桩(Stub), 第一次调用会触发JIT编译, 第二次调用会跳到JIT编译结果
JIT编译后的实现就在 0x3410F40 处,这里的就是 Interlocked.Increment(ref long) 的实现
我们可以看到这个实现使用了x86的CMPXCHG8B指令,如果出现long变量的值被其他线程修改,会检测出来并重试添加
当然,不是所有x86的CPU都支持CMPXCHG8B指令, 但至少可以运行.Net Core的CPU都会支持此指令, 也就是说实现 atomic long 不需要semaphore
http://www.geoffchappell.com/studies/windows/km/cpu/cx8.htm
上面的代码是Debug下编译得到的, Release下同样需要调用一个helper函数, 内部的逻辑是一样的
x64位上的 Interlocked.Increment(ref long) 就很简单了, 可以使用现成的指令 lock xadd
分析到此为止
如果您发现该资源为电子书等存在侵权的资源或对该资源描述不正确等,可点击“私信”按钮向作者进行反馈;如作者无回复可进行平台仲裁,我们会在第一时间进行处理!
加入交流群
请使用微信扫一扫!