内存屏障

关于多线程同步的一切:乱序执行和内存屏障 所以CPU0收到Read消息后,会把b的0值发给CPU1,所以继续在`while (b);简而言之,Core执行到write memory barrier(wmb)的时候,如果Store Buffer还有悬而未决的store操作,则都会被mark上,直到被标注的Store操作进入内存后,后续的Store操作才能被执行,因此wmb保障了barrier前后操作的顺序,它不关心barrier前的多个操作的内存序,以及barrier后的多个操作的内存序,是否与Global Memory Order一致。... 阅读详情
发信人: snnn (cm), 信区: LinuxDev
标 题: 好多人都在问内存屏障啊,我说下我的认识
发信站: 水木社区 (Wed Mar 14 00:21:27 2012), 站内

下面仅说x86平台。(含amd64)

在gcc中,插入内存屏障有两种方式:
1.内联汇编
2. 使用__sync_synchronize 函数。

gcc 4.2及以下版本,__sync_synchronize 这个函数是空实现。这其实是一个BUG。
gcc 4.4及以上版本,__sync_synchronize这个函数对应着mfence这个汇编指令。

4.2的这个BUG的原因在于,x86平台上,用于实现内存屏障的mfence指令,是否真的有效,是有很大争议的。

传统来讲,在QPI技术出现之前,也就是说,我们还在用数据总线、地址总线这样的方式通信的时候,mfence确实是像书上说的那样,为我们带来了强一致性。

intel从nelhalem开始引入点对点(QPI)互联技术。由于内存和CPU之间的总线没了,所以,事情有些变化。如果是多CPU,并且没有用lock指令加锁,那么得到是因果一致性。Memory ordering obeys causality (memory ordering respects transitive visibility). 这些信息是通过intel手册得到的,具体的含义还得咨询intel的工程师。也就是说,mfence是否还能达到之前我们所预期的效果,是未知的。也就是说,这对JAVA社区是一个极大的灾难。

不过这些还不是4.2版本的gcc不实现__sync_synchronize函数的理由,因为那个时候还没有nelhalem。感兴趣的童鞋请移步 http://gcc.gnu.org/bugzilla/show_bug.cgi?id=36793 (我没看懂)


与mfence类似的两个指令就是lfence/sfence。据说这两个指令实际什么事情都没做(在某些型号的CPU上,大概是P4那个时代)。所以mfence可能也是如此。所以我猜是因为这个,gcc一直无视它,认为intel不提供类似的指令。


那么什么时候需要内存屏障?
当你对顺序性有要求,但是又没有使用lock指令的时候。

gcc的__sync_fetch_and_add这样的指令,是带lock prefix的。所以它本身已经是一个内存屏障了。


java中的DCLP问题,我认为在C++中不存在。
// Broken multithreaded version
// "Double-Checked Locking" idiom
class Foo {
private Helper helper = null;
public Helper getHelper() {
if (helper == null)
synchronized(this) {
if (helper == null)
helper = new Helper();
}
return helper;
}
// other functions and members...
}


出synch块的时候,需要做解锁操作。在C++中,这个通常是由CAS指令实现的。CAS又带有lock prefix,那么就木有问题啊!
JAVA中其实也是一样。但是写JDK标准库的人,需要考虑跨平台啊,不同的CPU不同的表现啊,等等。


mfence的另外一个问题就是,它是写给CPU看的。但是编译器本身也可能会修改代码原有的执行顺序。那么它能不能读懂这个指令呢? 如果不能,你在此处用mfence的意义何在?至少,目前没有哪个编译器说它能读懂这个。编译器能读懂volatile。编译器能读懂pthread_mutex_lock/pthread_mutex_unlock。


以上表述,如你发现有误,或者怀疑可能有误,欢迎指出。因为上述结论基本都是靠读文档得到,我并未在实际环境作任何测试。






--
喜欢的生活:睡觉,没有忧郁的可以随时随地的大笑~

my blog: http://snnn.sinaapp.com/


※ 修改:·snnn 于 Mar 14 00:44:03 2012 修改本文·[FROM: 111.195.74.142]
※ 来源:·水木社区 newsmth.net·[FROM: 111.195.74.142]

发信人: Dieken (风催草低 - 明月何尝不照人), 信区: LinuxDev
标 题: Re: 好多人都在问内存屏障啊,我说下我的认识
发信站: 水木社区 (Wed Mar 14 01:06:57 2012), 站内

对 DCL 问题的描述,实在龌龊的很:

http://en.wikipedia.org/wiki/Double-checked_locking


【 在 snnn (cm) 的大作中提到: 】
: 下面仅说x86平台。(含amd64)
: 在gcc中,插入内存屏障有两种方式:
: 1.内联汇编
: ...................

--
Debian testing netinst 安装过程: (1) 下载 netinst iso 安装,软件源选择 debian.cn99.com; (2) 编辑 /etc/default/locale,加入如下三行:
LANG=en_US.UTF-8 LC_CTYPE=zh_CN.UTF-8 LANGUAGE=en_US:en
(3) dpkg-reconfigure locales,把 en_US.ISO8859_1, en_US.UTF-8, zh_CN.GBK, zh_CN.UTF-8 选择上;
(4) aptitude purge vim-tiny && aptitude install xorg gdm fcitx xfce4 xfonts-wqy ttf-arphic-uming vim-full
(5) 重启或者用 /etc/init.d/gdm start 进入 X 界面;
(6) 乐意的话可以装上 msttcorefonts, emacs-intl-fonts, xfonts-intl-chinese。


※ 来源:·水木社区 newsmth.net·[FROM: 114.241.183.*]

发信人: ComeAlong (卡梅隆), 信区: LinuxDev
标 题: Re: 好多人都在问内存屏障啊,我说下我的认识
发信站: 水木社区 (Wed Mar 14 10:53:59 2012), 站内

如果cpu不重排指令, 也不需要硬件内存屏障了, 不过我深切怀疑intel的cpu都没有做指
令重排的优化. 我个人认为这种优化意义有限.



【 在 snnn (cm) 的大作中提到: 】
: 标 题: 好多人都在问内存屏障啊,我说下我的认识
: 发信站: 水木社区 (Wed Mar 14 00:21:27 2012), 站内
:
: 下面仅说x86平台。(含amd64)
:
: 在gcc中,插入内存屏障有两种方式:
: 1.内联汇编
: 2. 使用__sync_synchronize 函数。
:
: gcc 4.2及以下版本,__sync_synchronize 这个函数是空实现。这其实是一个BUG。
: gcc 4.4及以上版本,__sync_synchronize这个函数对应着mfence这个汇编指令。
:
: 4.2的这个BUG的原因在于,x86平台上,用于实现内存屏障的mfence指令,是否真的有效,是有很大争议的。
:
: 传统来讲,在QPI技术出现之前,也就是说,我们还在用数据总线、地址总线这样的方式通信的时候,mfence确实是像书上说的那样,为我们带来了强一致性。
:
: intel从nelhalem开始引入点对点(QPI)互联技术。由于内存和CPU之间的总线没了,所以,事情有些变化。如果是多CPU,并且没有用lock指令加锁,那么得到是因果一致性。Memory ordering obeys causality (memory ordering respects transitive visibility). 这些信息是通过intel手册得到的,具体的含义还得咨询intel的工程师。也就是说,mfence是否还能达到之前我们所预期的效果,是未知的。也就是说,这对JAVA社区是一个极大的灾难。
:
: 不过这些还不是4.2版本的gcc不实现__sync_synchronize函数的理由,因为那个时候还没有nelhalem。感兴趣的童鞋请移步 http://gcc.gnu.org/bugzilla/show_bug.cgi?id=36793 (我没看懂)
:
:
: 与mfence类似的两个指令就是lfence/sfence。据说这两个指令实际什么事情都没做(在某些型号的CPU上,大概是P4那个时代)。所以mfence可能也是如此。所以我猜是因为这个,gcc一直无视它,认为intel不提供类似的指令。
:
:
: 那么什么时候需要内存屏障?
: 当你对顺序性有要求,但是又没有使用lock指令的时候。
:
: gcc的__sync_fetch_and_add这样的指令,是带lock prefix的。所以它本身已经是一个内存屏障了。
:
:
: java中的DCLP问题,我认为在C++中不存在。
: // Broken multithreaded version
: // "Double-Checked Locking" idiom
: class Foo {
: private Helper helper = null;
: public Helper getHelper() {
: if (helper == null)
: synchronized(this) {
: if (helper == null)
: helper = new Helper();
: }
: return helper;
: }
: // other functions and members...
: }
:
:
: 出synch块的时候,需要做解锁操作。在C++中,这个通常是由CAS指令实现的。CAS又带有lock prefix,那么就木有问题啊!
: JAVA中其实也是一样。但是写JDK标准库的人,需要考虑跨平台啊,不同的CPU不同的表现啊,等等。
:
:
: mfence的另外一个问题就是,它是写给CPU看的。但是编译器本身也可能会修改代码原有的执行顺序。那么它能不能读懂这个指令呢? 如果不能,你在此处用mfence的意义何在?至少,目前没有哪个编译器说它能读懂这个。编译器能读懂volatile。编译器能读懂pthread_mutex_lock/pthread_mutex_unlock。
:
:
: 以上表述,如你发现有误,或者怀疑可能有误,欢迎指出。因为上述结论基本都是靠读文档得到,我并未在实际环境作任何测试。
:
:
:
:
:
:
: --
: 喜欢的生活:睡觉,没有忧郁的可以随时随地的大笑~
:
: my blog:http://snnn.sinaapp.com/
:
:
: ※ 修改:·snnn 于 Mar 14 00:44:03 2012 修改本文·[FROM: 111.195.74.142]
: ※ 来源:·水木社区 newsmth.net·[FROM: 111.195.74.142]


--
什么罕物,连人之高低不择, 还说`通灵'不`通灵'呢!我也不要这劳什子了!
家里姐姐妹妹都没有,单我有,我说没趣,如今来了这们一个神仙似的妹妹也
没有,可知这不是个好东西.


※ 来源:·水木社区 newsmth.net·[FROM: 116.228.169.*]

发信人: abadcafe (abadcafe), 信区: LinuxDev
标 题: Re: 好多人都在问内存屏障啊,我说下我的认识
发信站: 水木社区 (Wed Mar 14 11:05:09 2012), 站内

我也搭车说说我对volatile的理解,以及为什么多线程程序需要这个关键字吧。不对的地方还请楼主指教。

我们先写一个C代码:

#include <pthread.h>
volatile int a = 1;
int b = 2;

void * thread(void * arg)
{
a = 0;
b = 0;
}

int main(void)
{
int i = 0;
pthread_t thread_id;

pthread_create(&thread_id, NULL, thread, NULL);

while (a) i++;
while (b) i++;

printf("%d %d %d\n", a, b, i);

return 0;
}

用armgcc -O2编译,再把它反汇编之(我对arm汇编比较熟悉):

int main(void)
{
84f4: e92d4010 stmdb sp!, {r4, lr}
int i = 0;
84f8: e3a04000 mov r4, #0 ; 0x0
pthread_t thread_id;

pthread_create(&thread_id, NULL, thread, NULL);
84fc: e1a01004 mov r1, r4
8500: e59f2068 ldr r2, [pc, #104] ; 8570 <.text+0x178>
8504: e1a03004 mov r3, r4
8508: e24dd004 sub sp, sp, #4 ; 0x4
850c: e1a0000d mov r0, sp
8510: ebffffaf bl 83d4 <.text-0x24>

while (a) i++;
8514: e59f1058 ldr r1, [pc, #88] ; 8574 <.text+0x17c>
8518: e5913000 ldr r3, [r1]
851c: e1530004 cmp r3, r4
8520: e1a02001 mov r2, r1
8524: 0a000003 beq 8538 <main+0x44>
8528: e5923000 ldr r3, [r2]
852c: e2844001 add r4, r4, #1 ; 0x1
8530: e3530000 cmp r3, #0 ; 0x0
8534: 1afffffb bne 8528 <main+0x34>
while (b) i++;
8538: e59f2038 ldr r2, [pc, #56] ; 8578 <.text+0x180>
853c: e5923000 ldr r3, [r2]
8540: e3530000 cmp r3, #0 ; 0x0
8544: 0a000001 beq 8550 <main+0x5c>
8548: e2844001 add r4, r4, #1 ; 0x1
854c: 1afffffd bne 8548 <main+0x54>

printf("%d %d %d\n", a, b, i);
8550: e5911000 ldr r1, [r1]
8554: e5922000 ldr r2, [r2]
8558: e1a03004 mov r3, r4
855c: e59f0018 ldr r0, [pc, #24] ; 857c <.text+0x184>
8560: ebffff98 bl 83c8 <.text-0x30>

return 0;
}

我们可以看到8534和854c的两处bne指令的跳转位置明显不一样:8534跳到了8528处,会重新从内存中load变量a,于是可以正确地退出,而854c则由编译器做了假设,假设变量b的值一直都是真。这样,整个逻辑就错了。
linux gcc 原子操作函数 type __sync_fetch_and_add (type ptr, type value, …) // 将value加到ptr上,结果更新到ptr,并返回操作之前ptr的值 type __sync_fetch_and_sub (type ptr, type value, …) // 从ptr减去value,结果更新到ptr,并返回操作之前ptr的值 type __sync_fetch_and_or (type ptr, type value, …) // 将ptr与value相或,结果更新到ptr, 阅读详情

相关推荐

基于嵌入式Linux下GCC编译器__sync_sub_and_fetch_4错误的解决心得

原子操作就是不可再分的操作,在多线程程序中原子操作是一个非常重要的概念,它常常用来实现一些同步机制。在Linux系统中的GCC或者嵌入式Linux的交叉编译GCC中从4.1.2提供了__sync_*系列的built-in函数,用于提供加减和逻辑运算的原子操作

嵌入式与人工智能天地 1万+

还是说Memory Model,gcc的__sync_synchronize要慎用。

出处 http://blog.sunchangming.com/post/47188394133

3355

内存屏障简介

为了限制性能下降,CPU被设计成在从内存 中获取数据的同时,可以执行其他指令和内存引用。这明显会导致指令 和内存引用乱序执行,并导致严重的混乱 仅仅在两个CPU之间或者CPU与设备之间存在需要交互的可能性 时,才需要内存屏障。任何代码只要能够保证没有这样的交互,这样代 码就不必使用内存屏障。但是,不使用内存屏障不代表不使用同步机制。 重排序问题无时无刻不在发生,源自三种场景: 编译器编译时的优化 处理器执行时的乱序优化 缓存同步顺序(导致可见性问题) ...

Roland_Sun的专栏 1万+

CPU乱序执行

CPU乱序执行 文章目录CPU乱序执行前言CPU乱序执行编译器乱序优化GCC编译器优化屏障和内存屏障C语言中的内存屏障CPU存储模型实际遇到的问题 前言 乱序优化包括: CPU乱序执行优化 编译器乱序优化 对应的限制乱序优化的方式: 内存屏障 优化屏障 CPU乱序执行 CPU在保证结果一致的情况下,把原来有序的指令列表,按照指令依赖关系和指令执行周期,重 新安排执行顺序。 // 原代码 int a = 10; int b = a; int c = 20; int d = c; // 实际

枫潇潇 4174

什么是内存屏障?为什么需要内存屏障?

1.什么是内存屏障?,为什么需要内存屏障? 首先,为了性能编译器和处理器都会对指令进行重排序。 什么是内存屏障:内存屏障是一条指令,该指令可以对编译器(软件)和处理器(硬件)的指令重排做出一定的限制,比如,一条内存屏障指令可以禁止编译器和处理器将其后面的指令移到内存屏障指令之前。 为什么需要内存屏障:编译器和处理器指令重排只能保证在单线程执行下逻辑正确,在多个线程同时读写多个变量的情况下,如果不对指令重排作出一定限制,代码的执行结果会根据指令重排后的顺序产生不同的结果。指令重排后的顺序每次执行时都可能

csbmww的博客 3841

内存屏障(cpu内存屏障 与java内存屏障)

CPU 内存屏障 定义 读写屏障指令 为什么会出现内存屏障 java内存屏障 java内存屏障存在意义 java中内存屏障的主要类型 LoadLoad 屏障 StoreStore 屏障 LoadStore 屏障 StoreLoad 屏障 Synchronized 锁 volatile语义中的内存屏障 final语义中的内存屏障

zzsaixuexi的博客 1370

内存屏障详解

1、什么是内存屏障? 现在大多数现代计算机为了提高性能而采取乱序执行,这可能会导致程序运行不符合我们预期,内存屏障就是一类同步屏障指令,是CPU或者编译器在对内存随机访问的操作中的一个同步点,只有在此点之前的所有读写操作都执行后才可以执行此点之后的操作。 2、为什么会出现内存屏障 内存屏障存在的意义就是为了解决程序在运行过程中出现的内存乱序访问问题,内存乱序访问行为出现的理由是为了提高程序运行时的性能,Memory Bariier能够让CPU或编译器在内存访问上有序。 在进一步剖析为什么会出现内存屏

weixin_39094034的博客 1万+

LINUX内核内存屏障

preempt_disable()和相应的preempt_enable()之间的代码是禁止内核抢占的, 通过对当前。因为b=f(a)这条指令依赖于第一条指令(a++)的运行结果, 所以。考虑这样一个系统, 它具有双CPU(1和2), 每一个CPU有一对并行的数据cache(CPU 1相应A/B,CPU 1上的写屏障是为了保证这种逻辑: 假设p == 1, 那么必然有a == 3 && b == 4.因此, 依据(1), (2)和(4), 一个无条件的LOCK跟在一个UNLOCK之后, 锁相当于一个完整的。

m0_74282605的博客 1313

C++内存屏障

内存屏障是一种同步原语,它能确保特定的内存操作顺序,防止编译器或处理器对这些操作进行重排序。内存屏障还能保证内存操作的可见性,即一个处理器对内存的修改对其他处理器可见。内存屏障是并发编程中确保正确内存访问顺序的关键机制。在C++中,虽然不直接操作内存屏障,但通过和内存序可以实现对内存访问顺序的精确控制。理解内存屏障和内存序对于编写高性能、正确的并发代码至关重要。正确使用这些机制可以避免数据竞争和其他并发问题,同时最小化性能开销。随着现代处理器架构的发展,内存屏障的实现和性能特性也在不断变化。

m0_60773087的博客 1100

PCB Layout总结

PCB Layout作为硬件设计中的一个环节,也是很重要的一个环节;在硬件电路设计合理的情况下,他其实是影响性能的一个绝对重要的指标。现在很多的PCB Layout工程师都是按照硬件工程师或者PI SI工程师给出的约束规则来完成布局布线的,这些也就是俗称的 “拉线工”。他们重复而机械的完成一块块PCB设计,一段时间设计后,他们中的一些或许已经有了这样的一些经验:哪些要做等长,哪些要走粗、哪些要平...

xydlxd118的博客 2583

【爬虫】Python实现爬取淘宝商品信息(超详细)

项目基于Python的第三方库Selenium模拟浏览器运行、PyQuery解析和操作HTML文档,获取淘宝平台中某类商品的详细信息(商品标题、价格、销量、商铺名称、地区、商品详情页链接、商铺链接等),并基于第三方库openpyxl建立、存储于Excel表格中。# 全局变量count = 1 # 写入Excel商品计数# 启动ChromeDriver服务# 关闭自动测试状态显示 // 会导致浏览器报:请停用开发者模式# 把chrome设为selenium驱动的浏览器代理;# 窗口最大化。

qq_46315152的博客 4万+

linux内存屏障

现代计算机为了提高性能多采取乱序执行,乱序执行又分为编译乱序和执行乱序两类,前者是编译器的行为,后者是cpu运行时的行为。编译乱序可以通过编译屏障来解决,而执行乱序一般通过读内存屏障、写内存屏障以及读写内存屏障来解决。

风一样的男子(quinta)的博客 1650

从补码原理到实践:RISC-V中addi指令的符号扩展机制详解

本文深入解析了RISC-V架构中addi指令的符号扩展机制。从补码的数学原理出发,详细阐述了12位立即数在指令编码中如何通过符号位进行扩展,并结合lui指令,揭示了加载任意32位立即数的正确方法与实践陷阱,帮助开发者编写高效可靠的底层代码。

weixin_29161719的博客 141

java内存屏障的原理与应用

目录 1.java内存屏障 2.java内存屏障的使用 一.java内存屏障 1.1 什么是内存屏障(Memory Barrier)? 内存屏障(memory barrier)是一个CPU指令。基本上,它是这样一条指令: a) 确保一些特定操作执行的顺序; b) 影响一些数据的可见性(可能是某些指令执行后的结果)。编译器和CPU可以在保证输出结果一样的情况下对指令重排序,使性能得...

breakout_alex的博客 1万+

四自由度机械臂Matlab仿真全流程:从DH参数建模到轨迹规划实战

本文详细介绍了四自由度机械臂在Matlab中的完整仿真流程,从DH参数建模到轨迹规划实战。通过Robotics Toolbox工具,读者可以学习如何构建机械臂模型、验证运动学特性,并实现平滑的轨迹规划。文章还提供了性能优化技巧和常见问题解决方案,帮助开发者高效完成机械臂仿真项目。

weixin_29061041的博客 241

RAG优化实战:微调Embedding模型提升检索精度全流程

在构建基于大语言模型(LLM)的问答系统时,检索增强生成(RAG)架构是关键技术路线。其核心原理在于,通过Embedding模型将文本转换为高维向量,并在向量空间中进行语义相似度计算,从而实现精准检索。这项技术的核心价值在于,它能有效利用专属知识库,为大模型提供准确的上下文信息,从而减少“幻觉”并提升答案的专业性与时效性。然而,通用Embedding模型在面对医疗、法律、金融等垂直领域的专业术语和行话时,往往存在语义理解偏差,导致检索不准,成为RAG系统的关键瓶颈。为解决此问题,微调(Fine-tuning

weixin_34088583的博客 502

Java的内存屏障

一、为什么要有内存屏障 内存屏障是为了解决因为cpu,高速缓存,主内存出现的时候,导致的可见性和重序性问题。 1、原理解释 因为计算机的运算任务需要CPU和内存相互配合共同完成,其中CPU负责逻辑计算,内存负责数据存储。但是在真正的实际开发中CPU是要与内存进行交互的,但因为内存和CPU的计算速度是有差距的,因此为了提高CPU的利用效率,现代处理器结构都加入了一层读写速度尽可能接近CPU运算速度的高速缓存来作为内存与CPU之间的缓冲:将运算需要使用的数据复制到缓存中,让CPU运算可以快速进行,计算结束

qq_48241564的博客 4250

开源DIY | 打造会“听”会“走”的智能移动垃圾桶

本文详细介绍了如何利用Arduino和超声波传感器打造一款会“听”会“走”的智能移动垃圾桶。通过开源硬件和简单编程,实现声控移动和自动避障功能,适合创客爱好者和家庭DIY。项目成本控制在200元内,涵盖硬件搭建、电路连接和核心功能实现,是入门级机器人制作的理想选择。

weixin_33670713的博客 233

内存屏障底层原理

内存屏障是一个很晦涩的概念,今天为了让大家彻底了解在高级并发里面的这个高级主题,我们从系统层面向大家讲解内存屏障的原理、种类、应用, 下面的内容可能牵涉的东西比较多并且比较长,但是如果你耐心的看完,并且完全的吸纳,那么你就会真正的了解内存屏障! 1 速度不对等 Cpu的速度比cpu之间的互联性能及cpu试图要访问的内存性能,都要快上几个数量级 上面展示的图显示现代处理...

m0_37561834的博客 7019

UVM概述及uvm_component和uvm_object(一)

UVM:Universal Verification Methodology(通用验证方法学) UVM:是建立在systemverilog平台上的一个库,提供了一系列的接口,让我们能够更方便的进行验证。验证平台组成: Driver:用来把不同的激励施加给DUT; Monitor:用来检测DUT的输出; Scoreboard:专门用来比较期望值与monitor检测到的DUT输出; Refer

南国之邱 1万+

(计算机组成原理)第二章数据的表示和运算-第二节:数字电路基础知识

本文介绍了计算机组成原理中的逻辑门电路、多路选择器和三态门。逻辑门电路包括基本逻辑运算(与、或、非)和复合逻辑运算(与非、或非、异或、同或),这些运算通过门电路实现,并满足分配律和结合律。多路选择器(MUX)用于从多个输入信号中选择一个输出,广泛应用于数据选择、资源共享和逻辑函数实现等场景。三态门则具有三种输出状态(高电平、低电平、高阻态),常用于总线控制和信号隔离。这些元件在数字电路设计和计算机系统中具有重要作用。

快乐江湖的博客 2477

Linux 内存屏障简介

Linux,内存屏障

JiMoKuangXiangQu的专栏 1649

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

上一篇: prezi是个好东西,途客圈就是用这个展示的
下一篇: API Hook的几种实现
code_farmer
博客等级 码龄22年 9粉丝 23原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值