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

《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 使用寄存器eax m 使用内存地址
b 使用寄存器ebx o 使用内存地址并可以加偏移值
c 使用寄存器ecx I 使用常数0-31
d 使用寄存器edx J 使用常数0-63
S 使用esi K 使用常数0-255
D 使用edi L 使用常数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; \
})

你可能感兴趣的:(linux0.11内核完全注释读书笔记)