您的位置:首页 > 运维架构 > Linux

linux0.11内核完全注释读书笔记

2014-10-25 16:56 393 查看

《Linux0.11内核完全注释》读书笔记

嵌入汇编

asm("汇编语句":输出寄存器
:输入寄存器
:会被修改的寄存器);


kernel/traps.c文件中第22行开始的一段代码作为例子。

#define get_seg_byte(seg,addr) \({ \
register char __res; \ //定义了一个寄存器变量__res。
__asm__("push %%fs; \ //首先保存fs寄存器原值(段选择符)。
mov %%ax, %%fs; \ //然后用seg设置fs。
movb %%fs:%2, %%al; \//取seg:addr处1字节内容到al寄存器中。
pop %%fs" \ //恢复fs寄存器原内容。
:"=a" (__res) \ //输出寄存器列表
:"0" (seg), "m" (*(addr))); \//出入寄存器列表__res;
})


这段10行代码定义了一个嵌入汇编语言宏函数。通常使用汇编语句最方便的方式是把它们放在一个宏内。用圆括号括住的组合语句(花括号中的语句):“({})”可以作为表达式使用,其中最后一行上的变量__res(第10行)是该表达式的输出值。
再看一个简单的例子。

asm("cld\n\t"
"rep\n\t"
"stol":/*没有输出寄存器*/
:"c"(count-1), "a"(fill_value),"D"(dest)
:"%ecx", "%edi");


常用寄存器加载代码说明

代码说明代码说明
a使用寄存器eaxm使用内存地址
b使用寄存器ebxo使用内存地址并可以加偏移值
c使用寄存器ecxI使用常数0-31
d使用寄存器edxJ使用常数0-63
S使用esiK使用常数0-255
D使用ediL使用常数0-65535
q使用动态分配字节可寻址寄存器(eac、ebx、ecx或edx)M使用常数0-3
r使用任意动态分配的寄存器N使用1字节常数(0-255)
g使用通用有效的地址即可(eax、ebx、ecx、edx、或内存变量)O使用常数0-31
A使用eax与edx联合(64位)=输出操作数。输出值将替换前值
+表示操作数可读可写&早期会变的(earlyclobber)操作数,表示在使用完操作数之前,内容会修改
关键词volatile也可以放在函数名前来修饰函数,用来通知gcc编译器该函数不会返回。这样就可以让gcc产生更好的一些代码。

volatile void do_exit(long code);static inline volatile void oom(void)
{
printk("out of memory\n\r");
do_exit(SIGSEGV);
}



圆括号中的组合语句

花括号对”{…}“用于把变量声明和语句组合成一个复合语句(组合语句)或一个语句块,这样在语义上这些语句就等同于一条语句。圆括号中的组合语句,即形如”({…})“的语句,可以在GNU C中用作一个表达式使用。这样就可以在表达式中使用loop、switch语句和局部变量,因此这种形式的语句通常称为语句表达式。

({int y = foo(); int z; if (y>0) z=y; else z = -1; 3+z;})


其中组合语句最和一条语句必须是后面跟随一个分号的表示式。这个表达式(”3+z“)的值即用作整个圆括号括住语句的值。如果最后一条语句不是表达式,那么整个语句表达式具有void类型,因此没有值。另外表达式中声明的局部变量会在语句块结束后失效。这个实例可以像如下形式的赋值语句来使用:

res=x+({...})+b;


当然,一般这种语句表达式通常用来定义宏。例如内核源码init/main.c程序中读取CMOS时钟信息的宏定义:

#define CMOS_READ(addr) ({ \
outb_p(0x80|addr, 0x70); \
inb_p(0x70); \
})


再看一个例子在include/asm/io.h中读I/O端口port的宏定义,其中最后变量_v的值就是inb()的返回值。

#define inb(port) ({ \
unsigned char _v; \
__asm__ volatile ("inb %%dx, %%al":"=a" (_v):"d" (port)); \
_v; \
})
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: