Linux 内核的 LOCK_PREFIX 的含义

Linux内核源码之信号量的实现 之前的一片博客介绍了用于Linux内核同步的自旋锁,即使用自旋锁来保护共享资源,今天介绍另外一种Linux内核同步机制——信号量。信号量在内核中的使用非常广泛,用于对各种共享资源的保护。信号量与自旋锁的实现机制是不一样的,用处也是不一样的。首先,自旋锁和信号量都使用了计数器来表示允许同时访问共享资源的最大进程数,但自旋锁的共享计数值是1,也就是说任意时刻只有一个进程在共享代码区运行;信号量却允许使用 阅读详情

发信人: RoachCock (安息吧), 信区: KernelTech
标  题: 终于弄明白了 Linux 内核的 LOCK_PREFIX 的含义
发信站: 水木社区 (Fri Mar 13 02:32:32 2009), 站内

x86 架构的内核实现原子整数的时候,用到了 LOCK_PREFIX 这个宏

static __inline__ void atomic_add(int i, atomic_t *v)
{
        __asm__ __volatile__(
                LOCK_PREFIX "addl %1,%0"
                :"+m" (v->counter)
                :"ir" (i));
}

在 CONFIG_SMP 的时候:

#define LOCK_PREFIX /
                ".section .smp_locks,/"a/"/n"   /
                "  .align 4/n"                  /
                "  .long 661f/n" /* address */  /
                ".previous/n"                   /
                "661:/n/tlock; "

展开后变成这样:

.section .smp_locks,"a"
  .align 4
  .long 661f
.previous
661:
        lock;

本来觉得直接加上 lock 前缀即可,前面一堆干吗用的一直不明白,终于决定要搞懂,
翻开 as 手册,查了个明白,现逐条解释如下:

.section .smp_locks,"a"
下面的代码生成到 .smp_locks 段里,属性为"a", allocatable,参考 as 7.76 .section
name

  .align 4
四字节对齐

  .long 661f
生成一个整数,值为下面的 661 标号的实际地址,f 表示向前引用,如果 661 标号出现
在前面,要写 661b。

.previous
代码生成恢复到原来的段,也就是 .text

661:
数字标号是局部标号,5.3 Symbol Names

        lock;
开始生成指令,lock 前缀

这段代码汇编后,在 .text 段生成一条 lock 指令前缀 0xf0,在 .smp_locks 段生成
四个字节的 lock 前缀的地址,链接的时候,所有的 .smp_locks 段合并起来,形成一个
所有 lock 指令地址的数组,这样统计 .smp_locks 段就能知道代码里有多少个加锁的
指令被生成,猜测是为了调试目的。

----

搜索完成,果然找到了引用处:
linux-2.6.23.12/arch/i386/kernel/module.c

当一个内核模块被加载后调用这个
int module_finalize(const Elf_Ehdr *hdr,
                    const Elf_Shdr *sechdrs,
                    struct module *me)
{
        const Elf_Shdr *s, *text = NULL, *alt = NULL, *locks = NULL,
                *para = NULL;
        char *secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;

        for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) {
                if (!strcmp(".text", secstrings + s->sh_name))
                        text = s;
                if (!strcmp(".altinstructions", secstrings + s->sh_name))
                        alt = s;
                if (!strcmp(".smp_locks", secstrings + s->sh_name))
                        locks= s;
                if (!strcmp(".parainstructions", secstrings + s->sh_name))
                        para = s;
        }

        if (alt) {
                /* patch .altinstructions */
                void *aseg = (void *)alt->sh_addr;
                apply_alternatives(aseg, aseg + alt->sh_size);
        }
        if (locks && text) {
                void *lseg = (void *)locks->sh_addr;
                void *tseg = (void *)text->sh_addr;
                alternatives_smp_module_add(me, me->name,
                                            lseg, lseg + locks->sh_size,
                                            tseg, tseg + text->sh_size);
        }

        if (para) {
                void *pseg = (void *)para->sh_addr;
                apply_paravirt(pseg, pseg + para->sh_size);
        }

        return module_bug_finalize(hdr, sechdrs, me);
}

上面的代码说,如果模块有 .text 和 .smp_locks 段,就调这个来处理,做什么呢?
void alternatives_smp_module_add(struct module *mod, char *name,
                                 void *locks, void *locks_end,
                                 void *text,  void *text_end)
{
        struct smp_alt_module *smp;
        unsigned long flags;

        if (noreplace_smp)
                return;

        if (smp_alt_once) {
                if (boot_cpu_has(X86_FEATURE_UP))
                        alternatives_smp_unlock(locks, locks_end,
                                                text, text_end);
                return;
        }

。。。
}
上面的代码说,如果是单处理器(UP),就调这个:
static void alternatives_smp_unlock(u8 **start, u8 **end, u8 *text, u8
*text_end)
{
        u8 **ptr;
        char insn[1];

        if (noreplace_smp)
                return;

        add_nops(insn, 1);
        for (ptr = start; ptr < end; ptr++) {
                if (*ptr < text)
                        continue;
                if (*ptr > text_end)
                        continue;
                text_poke(*ptr, insn, 1);
        };
}

看到这里就能明白,这是内核配置了 smp,但是实际运行到单处理器上时,通过运行期间
打补丁,根据 .smp_locks 里的记录,把 lock 指令前缀替换成 nop 以消除指令加锁的
开销,这个优化真是极致了……,可能考虑很多用户直接使用的是配置支持 SMP 编译好
的内核而特地对 x86/x64 做的这个优化。

看来学点汇编还是有用的。

顺手搜了一下:
http://www.google.cn/search?hl=zh-CN&newwindow=1&q=alternative+kernel+smp&meta=&
aq=f&oq=

又看到这段注释……,本来仔细看一下就明白了,折腾啊。

/*
 * Alternative inline assembly for SMP.
 *
 * The LOCK_PREFIX macro defined here replaces the LOCK and
 * LOCK_PREFIX macros used everywhere in the source tree.
 *
 * SMP alternatives use the same data structures as the other
 * alternatives and the X86_FEATURE_UP flag to indicate the case of a
 * UP system running a SMP kernel.  The existing apply_alternatives()
 * works fine for patching a SMP kernel for UP.
 *
 * The SMP alternative tables can be kept after boot and contain both
 * UP and SMP versions of the instructions to allow switching back to
 * SMP at runtime, when hotplugging in a new CPU, which is especially
 * useful in virtualized environments.
 *
 * The very common lock prefix is handled as special case in a
 * separate table which is a pure address list without replacement ptr
 * and size information.  That keeps the table sizes small.
 */

linux内核部件分析(二)——原子性操作atomic_t 在任何处理器平台下,都会有一些原子性操作,供操作系统使用,我们这里只讲x86下面的。在单处理器情况下,每条指令的执行都是原子性的,但在多处理器情况下,只有那些单独的读操作或写操作才是原子性的。为了弥补这一缺点,x86提供了附加的lock前缀,使带lock前缀的读修改写指令也能原子 阅读详情

相关推荐

【精通内核】Linux 内核写锁实现原理与源码解析

也就是说,当有读锁持有时,如果有读任务,则可以直接获得读锁;然而,如果在持有写锁的情况下,那么读锁肯定获取失败,然后进入等待队列中,写锁被释放后,如果有锁等待,那会唤醒等待任务。有时候我们需要在获取到写锁后,进行降级为读锁,这可以通过 downgrade_write 方法进行锁降级有先原子性的降锁状态从写锁状态置为读锁状态,如果结果小于 0,则表明有任务正在等待被唤醒,此时可以调用 rwsem_downgrade_wake 函数唤醒等待读锁的任务,因为此时写锁已经被释放,可以让等待读锁的任务一起并行执行。

知识分享官 1057

lock prefix

 关于同步的原语,无非就那么几种,用于进程间同步的记录锁、信号量(record lock, semaphore), 用于线程间同步的互斥量、读写锁、条件变量(mutex, rwlock, condition variable),在某些特殊的场合下,我们可以自己实现简单的用户态的锁来进行同步,这依赖于lock prefix。根据intel 64 and IA-32 architectures sof

overcomeunicom的专栏 1831

Linux C/C++并发编程实战(10)x86 上的原子操作实现--lock 指令前缀

当指令执行完毕,这个锁定动作也就会消失。在单处理器系统( UniProcessor,简称 UP)中,能够在单条指令中完成的操作都可以认为是原子操作,因为中断只能发生在指令与指令之间。在多处理器系统( Symmetric Multi-Processor,简称 SMP)中情况有所不同,由于系统中有多个处理器在独立的运行,即使在能单条指令中完成的操作也可能受到干扰。(以及所有以 ‘X’ 开头的指令)都能够保证在多处理器系统下的原子操作,它们总会宣告一个 “LOCK#” 信号,而不管有没有 LOCK 前缀。

二进制君 729

linux同步之原子操作原理

x86和ARM64平台下内核原子操作的原理

小立仔 1720

pwm电机调速的原理介绍与代码实现

1、pwm实现调速的原理与介绍 PWM(Pulse Width Modulation)脉冲宽度调制。 1)占空比 pwm占空比就是一个脉冲周期内有效电平在整个周期所占的比例。 通过调节PWM的占空比就能调节IO口上电压的持续性变化,因此也能够控制外设的功率进行持续性变化,也就能控制直流电机的转速快慢。 那么重点就在于如何调节PWM波形的输出。如下图所示 图中的ARR是我们给定时器的一个预装载值,CCRx的上下变化是产生PWM波的关键。我们假设ARR大于CCRx的部分输出为高电平(即t1-t2、t3-t4

Clichong 7万+

如何使用superpowers

"这个 bug 帮我修一下" → 我会自动走 systematic-debugging → TDD → verify 流程。- "帮我添加一个登录功能" → 我会自动走 brainstorming → plan → TDD → verify 流程。只要任务有 1% 的可能性适用某个技能,我就会先调用它再行动。→ TDD(测试驱动开发)→ verification(验证)→ code review(代码审查)提出需求 → brainstorming(头脑风暴)→ writing-plans(编写计划)

xiaojingjianqiang的博客 612

X64指令前缀概览

X64指令前缀概览

ComputerInBook的专栏 1775

Verilog语法之十:过程块(initial和always)

我将Verilog的语法进行了总结,写了13篇文章,让小白“快速见识猪如何跑”,每一篇讲解一类语法,结合正确的示例代码和错误的示例代码,有的章节对个别语法进行了归纳总结,帮助初学者加深理解。

航天领域资深码农,型号经验丰富,一线干货分享 1672

从JAVA、JVM以及汇编三个层面去理解volatile关键字

深入理解volatile关键字,了解他在不同层面的实现原理!

qq_35436158的博客 785

【Qt】QByteArray详解

对象共享同一份数据,直到修改时才进行深拷贝,以节省内存和计算资源。:可存储任意二进制数据(如图片、音频、协议数据),不依赖字符编码。与编码(如 UTF-8、Latin-1)之间的桥梁(例如。• 补零规则确保奇数字符串能正确解析,避免数据截断。:内部自动分配和释放内存,无需手动管理。将输入的字符串视为十六进制编码数据,返回实际数据长度(不包括结尾的。的输入必须是有效的十六进制字符(,但针对 Qt 框架做了优化。:处理加密后的二进制结果(如。• 如果输入字符串长度为奇数,• 若输入长度为奇数,自动在。

CSUC的博客 1598

【C2000系列DSP的Bootloader详解】如何打包源文件为库文件且加密仅授权使用,一步步教你如何实现

如果想要在 TI 的 CCS 开发环境中为 C2000 系列 MCU 生成 lib 库文件,并对这些库文件进行加密保护,确保只有授权方(持有密钥)才能调用和使用,以此保护源代码的知识产权。

昔时扬尘处 177

操作系统——操作系统锁

操作系统对数据同步的方法有:对最重要的几种锁(原子变量,关中断,信号量,自旋锁)。

庄小焱 2847

【ArcGIS遇上Python】ArcGIS10.8 Python代码批量完美实现MODIS NDVI数据格式转换和投影变换

由于论文的需要,将MODIS NDVI数据进行投影变换和格式转换,具体操作可以参照:《ArcGIS10.8完美实现MODIS NDVI数据格式转换和投影变换》,但是该文章中的做法只能一次性实现一景影像的转换,没法批量,虽然ArcGIS中提供了Batch的方法但是需要挨个添加数据,确定输出路径等等,本文就实现以ArcGIS10.8 Python代码批量完美实现MODIS NDVI数据格式转换和投影变换。先来看投影后的效果: 在实现批量投影转换之前,需要两个投影文件:Sinusoidal.prj和MyAl

智慧空间实验室 3815

Linux 内核 LOCK_PREFIX 的含义

发信人: RoachCock (安息吧), 信区: KernelTech 标  题: 终于弄明白了 Linux 内核的 LOCK_PREFIX 的含义 发信站: 水木社区 (Fri Mar 13 02:32:32 2009), 站内 x86 架构的内核实现原子整数的时候,用到了 LOCK_PREFIX 这个宏 static __inline__ void atomic_add(in

作一个独立连续的思考者 3303

OPTEE:TA命令操作的实现(三)

上文我们创建好了通道,现在就是操作,怎么操作TA呢?REE侧的CA执行创建会话操作成功后,CA就可使用获取到的调用来让TA执行特定的命令。通过ioctl的系统调用发送到OP-TEE的驱动中,保存在共享内存中,并触发安全监控模式调用(smc)切换到Monitor模式(ARMv7)或EL3(ARMv8)中进行安全世界状态的处理。

叫好与叫座虽然不是对立面,但想在同一个作品中达到双重效果很难。 1268

2025华为od面试手撕代码【求字符串表达式结果】

2025华为od面试手撕代码【求字符串表达式结果】

MISAYAONE的博客 1353

终于弄明白了Linux内核的LOCK_PREFIX的含义

x86 架构的内核实现原子整数的时候,用到了 LOCK_PREFIX 这个宏     static __inline__ void atomic_add(int i, atomic_t *v)     {      __asm__ __volatile__(      LOCK_PREFIX "addl %1,%0"      :"+m" (v->counter)

geometriclife的专栏 1948

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

LINUX内核之普通自旋锁

LINUX内核之普通自旋锁 @CopyLeft by ICANTH,I Can do ANy THing that I CAN THink!~ Author:WenHui,WuHan University,2012-6-15   PDF版阅读地址:http://www.docin.com/p1-424285718.html   普通自旋锁 自旋锁最常见的使用场景是创建一段临界

intheworld 996

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

linux内核原子操作的实现

所谓原子操作,就是“不可中断的一个或一系列操作”。硬件级的原子操作:在单处理器系统(UniProcessor)中,能够在单条指令中完成的操作都可以认为是“原子操作”,因为中断只发生在指令边缘。在多处理器结构中(Symmetric Multi-Processor)就不同了,由于系统

vivi的技术博客 2万+

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

linux内核同步

同步是linux内核中一种很重要的操作.它为内核提供了一种临界区和SMP系统中的数据保护机制.今天就来分析一下在linux内核是怎么样实现这些操作的.一:原子操作(摘自《understanding the linux kernel 2.4》)原子操作是指在执行过程中不能被打断的操作.它包括以下几种类型:进行一次或者零次对齐内存的访问操作都是原子操作.因为这些指令一般都是单指令.不可

少帅的天空 2644

Linux内核中的cmpxchg函数

Linux内核中的cmpxchg函数 http://www.longene.org/forum/viewtopic.php?t=2216 前几天,为了这个函数花了好多时间,由于参考的资料有误,一直都没有看明白,直到google之后,总算搞明白了,因此写出来大家分享一下。 在Linux内核中,提供了比较并交换的函数cmpxchg,代码在include/asm-i386/cmpx

zdy0_2004的专栏 2万+

Linux内核原子操作(2)接口与实现

处理器在提供了硬件级别的原子操作保障后,软件可以基于硬件机制实现软件级别的原子操作接口,提供给软件模块使用。

Aspiresky的专栏 1007

瞧一瞧Linux:Linux的自旋锁和信号量如何实现?

你好,我是程序员贵哥。上节课,我们学习了解决数据同步问题的思路与方法。Linux作为成熟的操作系统内核,当然也有很多数据同步的机制,它也有原子变量、开启和关闭中断、自旋锁、信号量。那今天我们就来探讨一下这些机制在Linux中的实现。看看Linux的实现和前面我们自己的实现有什么区别,以及Linux为什么要这么实现,这么实现背后的机理是什么。

qq_33240556的博客 733

Linux内核同步机制——自旋锁

之前已经给出了原子操作部分内容,现将自旋锁部分内容整理如下,其中代码引用自os网站上源码阅读连接中的2.6.22.6版本,体系结构选用了i386。 信号量部分将在后续给出。 有疑义,欢迎共同探讨!2.自旋锁自旋锁是Linux内核常用的锁机制,它只能被一个执行进程持有。当一个进程去获取已被其他进程占有的自旋锁时,该进程会一直等待,直到锁可用为止。由于自旋锁的这种忙等机制,使得自旋锁不适合长期加锁

学海无涯,回头是岸........ 3213

linux 内核 锁,深入linux内核架构--内核锁

简介在多线程(进程)并发模型中,多个线程如果同时处理同一块数据的话,会引发竞态问题,以及随之而来的线程安全问题。而锁是解决线程安全的重要手段,其中主要包括原子性操作atomic,自旋锁spin_lock,信号量semaphore,互斥信号量mutex,读写锁rw_lock等等。临界区临界区是指一个访问共用资源(例如:共用设备或是共用存储器)的程序片段,而这些共用资源又无法同时被多个访问的特性。当一...

weixin_35023786的博客 797
上一篇: How to Install and Configure the RedBoot ROM Monitor
下一篇: 分析Linux中Spinlock在ARM及X86平台上的实现
flyingcloud_2008
博客等级 码龄19年 10粉丝 13原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值