linux kernel(内核)代码理解 几个记录(属性、内联汇编、__builtin()、 likely()等)
2021/7/19 7:04:51
本文主要是介绍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系统上以一个用户空间进程的形式运行,方便调试。
这篇关于linux kernel(内核)代码理解 几个记录(属性、内联汇编、__builtin()、 likely()等)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-12如何创建可引导的 ESXi USB 安装介质 (macOS, Linux, Windows)
- 2024-11-08linux的 vi编辑器中搜索关键字有哪些常用的命令和技巧?-icode9专业技术文章分享
- 2024-11-08在 Linux 的 vi 或 vim 编辑器中什么命令可以直接跳到文件的结尾?-icode9专业技术文章分享
- 2024-10-22原生鸿蒙操作系统HarmonyOS NEXT(HarmonyOS 5)正式发布
- 2024-10-18操作系统入门教程:新手必看的基本操作指南
- 2024-10-18初学者必看:操作系统入门全攻略
- 2024-10-17操作系统入门教程:轻松掌握操作系统基础知识
- 2024-09-11Linux部署Scrapy学习:入门级指南
- 2024-09-11Linux部署Scrapy:入门级指南
- 2024-08-21【Linux】分区向左扩容的方法