免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1318 | 回复: 0
打印 上一主题 下一主题

[转载]终于弄明白了 Linux 内核的 LOCK_PREFIX 的含义 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-07-17 12:38 |只看该作者 |倒序浏览
发信人: 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 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  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.
*/
--
少壮不努力,老大搞挨踢。
读书不用功,长大当民工。
在校人太傻,毕业写代码。
干啥啥不行,只能当码农。
※ 来源:·水木二站 2.newsmth.net·[FROM: 221.179.17.*]
※ 修改:·RoachCock 于 Mar 13 02:40:14 2009 修改本文·[FROM: 221.179.17.*]
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/12325/showart_1999057.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP