linux kernel(内核)代码理解 几个记录(属性、内联汇编、__builtin()、 likely()等)

1. GNU汇编器采用了AT&T语法,和流行的Intel/Microsoft语法形势存在差异(主要是元寄存器和目标寄存器的排列不同)。 AT&T汇编语法总结:

    a)寄存器通过在名称前加%引用,比如为使用eax寄存器,汇编代码中讲使用 %eax(说明:在C源代码中需使用2个%表示,即%%eax).

    b)  源寄存器总是在目标寄存器之前指定。比如 mov a, b表示将寄存器a的值复制到寄存器b, movl $5, %edx;将5存入edx。//对应Intel语法为 mov b, a,比如mov eax, 0x0;

    c)操作数的长度有汇编语句的后缀指定,b代表byte,l--long,w--word, 为在IA-32系统上讲一个长整数从eax寄存器移动到ebx寄存器,需要: movl %eax,%ebx。

    d)指针反引用需要将寄存器包含在括号中。例如 movl (%eax), %ebx 表示将寄存器eax的值指定的内存地震中的长整数值复制到寄存器ebx。//对应intel语法:  mov DWORD PTR [ebp-4], 0x3。将3存入ebp-4对应值的地址中去

    e)offset(register)指定寄存器值与一个偏移量联用,将偏移量加到寄存器的实际值上。例如 8(%eax)指定将eax+8作为一个操作数。

2、内联汇编

     GCC借助专门的语句,将简短的汇编语言代码片段插入到C代码中,编译器来承担联合代码生成的工作。

     asm语句用于指定汇编代码和所用的寄存器(也可以使用__asm__关键字),语法如下:

     asm("Assembler code";

                  : Output operand specification

                  : Input operand specification

                  : Modified registers. 即临时的工作寄存器

      );

     如下代码实现b=a的功能(不一定性能最优,仅做举例说明用):

      int move() {

         int a = 5;

         int b;

         asm  ("movl %1, %%eax;   /*%1 表示输入寄存器*/

                    movl %%eax, %0;"   /*%0 表示输入寄存器*/

                    : "=r" (b)  /*输出寄存器, =表示只写即丢弃前一个值直接替换为操作的输出值;+表示操作数是读写的; r表示寄存器,m表示内存地址*/

                    : "r" (a) /*输入寄存器*/

                    : "%eax"); /*临时工作寄存器*/

           printf("b: %u\n", b);

       }

      上述内联汇编代码使用了一个输入寄存器,一个输出寄存器和一个临时寄存器。%0, %1表示由编译器指定寄存器,代码不关心具体名字。

3、__builtin函数

     __builtin函数向编译器提供了其他选项,可以执行C语言常规能力范围之外的操作,又不比借助于内联汇编。常用的比如:

     __builtin_expect(long exp, long c)帮助编译器优化分支预测。exp指定一个将计算的表达式的结果值,而c返回预期结果(0或1)。

     比如如下代码 预期条件表达式在大多数情况下值为1,那么可用__builtin_expect优化性能:

     if( __builtin_expect(expression, 1)) {

         ... /*编译器通过预先计算该分支,来影响处理器的分支预测*/

     } else {

     ...

    }

    内核定义了一下两个宏,来标识代码中很可能和不太可能的分支:

    <complier.h>

    #define likely(x)      __builtin_expect( !!(x), 1)

    #define unlikely(x)  __builtin_expect( !!(x), 0)

    这里使用双重否定!!符号是为了:

    a)使得宏可以用于隐式转换为真值的指针;

    b)大于0的真值标准化为1,这是__builtin_expect所预期的值。

    代码示例(mm/slab.c)

    if (likely(ac-avail < ac->limit)) { ...} else { }  /*x 可以为运算表达式*/

 

4. container_or()

    如果结构A包含一个子结构B,则A称之为B的一个容器,例如:

    struct A {

       ...

         struct B {

         }element;

       ...

    } container;

   查找元素的容器对象实例(kernel.h):

   #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE*) 0)->MEMBER)  /*将空指针0转换为一个TYPE*的指针,成员地址即偏移量*/

   struct test {                                                         |   struct test {

     int a,b;                                                              |     struct list_head *f_list;

     struct list_head *f_list;                                      |      int a,b,c;

     int c;                                                                 |   } b;

    } a;                                                                       |

   offsetof(struct test, f_list) ==>值为8                  |   offsetof(struct test, f_list) ==>值为0

   

   /* container_of  从结构的成员来获得包含成员的结构实例

    * @ptr:            指向成员数据的指针

    * @type:         所嵌入到的容器结构的类型

    * @member:  成员在结构内的名称

    */

    #define container_of(ptr, type, member)   ( {               \

                 const typeof( ((type*) 0)->member) *__mptr = (ptr);  \

                 (type*) ( (char*) __mptr - offsetof(type, member) ); } )      /*为确保指针计算是按字节执行的,先将_mptr转为为char* 指针,计算完成后的赋值操作中再转回原来的数据类型*/

    比如 container_of(&a.f_list, struct test, f_list).  

 

5、LXR(本部分还未验证)

     LXR是一个交叉引用工具,可用于分析内核源代码并生成一个HTML形式的超文本表示,供浏览器查看。

     为在本地使用LXR,需要一个浏览器和一个WEB服务器,最好是apache,为随机字符串搜索,还需要glimpse搜索引擎。

     git库: git://lxr.linux.no/git/lxrng.git.     

     DDD data display debugger, 图形化gdb

     用户模式linux, User-Mode-Linux), 内核在linux系统上以一个用户空间进程的形式运行,方便调试。

 

    

  • 添加到短语集  
    • 没有此单词集:罗马尼亚语 → ...  
    • 创建新的单词集...
  • 拷贝
  • 添加到短语集  
    • 没有此单词集:英语 → ...  
    • 创建新的单词集...
  • 拷贝
上一篇:实验指导书


下一篇:string string.h cstring 区别