背景
阅读linux0.11源码的kernel/traps.c文件里包含了几个语句是内嵌的汇编程序,虽知道整体的意思,但是细化后就不明所以然,这里记录下。
C中嵌入汇编程序
格式
asm("汇编语句"
: 输出寄存器
: 输入寄存器
: 会被修改的寄存器
);
带冒号的行可以省略,“输出寄存器”表示汇编执行完后,存放输出数据的寄存器,“输入寄存器”开始执行代码的时候,指定指定寄存器存放值。
实例代码
示例1
int a=10,b;
asm("movl %1, %%eax;
movl %%eax, %0;"
:"=r"(b)
:"r"(a)
:"%eax"
);
表示C语言里的“b=a;”。
“r”表示使用任意寄存器,%0、%1表示使用两个寄存器,一般只能%0~%9共十个操作数,按输入输出寄存器出现顺序进行映射。
寄存器用两个百分号,是因为使用了%0%1这些数字使百分号有了特殊意义,所以在操作数出现的寄存器必须用双百分表示。
会被修改的寄存器里边的%eax表示eax寄存器在汇编代码块执行过程中会被改写,在执行前要保护好,这是提交给编译器决定的。
示例2
#define get_seg_byte(seg,addr) ({ \
register char __res; \
__asm__("push %%fs; \ // 将fs寄存器值压栈
mov %%ax,%%fs; \ // 将eax中的段值赋值给fs寄存器
movb %%fs:%2,%%al; \ // 将fs:(*(addr)) --> al
pop %%fs" \ // 弹出fs
:"=a" (__res) \ // 将eax值 --> __res
:"0" (seg),"m" (*(addr))); \
__res;})
表示取段seg中地址addr处的一个字符。
上面是kernel/traps.c中的一段代码,其中:
参数:seg - 段选择符;addr - 段内指定地址。
输出:%0 - eax(__res);输入:%1 - eax(seg);%2 - 内存地址(*(addr))。
“=a”中的’a’称为加载代码,’=’表示这是输出寄存器,执行完后,eax的值将赋给_res,’”0” (seg)’表示在这段代码开始运行时将seg放到eax寄存器中,”0”(0可以省略)表示这里使用的寄存器和上面输出寄存器一样。
编译器规定:标号从输出寄存器的最左边开始,被编号为0,往右和往下将被依次编号。所以这里的”0”表示eax,”1”表示seg,”2”表示*(addr)。
%%fs:%2是使用编号为2的变量(*(addr)),这里加上%%fs的意思??
示例3
// 取段seg中地址addr处的一个字长(4字节)。
// 参数:seg - 段选择符;addr - 段内指定地址。
// 输出:%0 - eax(__res);输入:%1 - eax(seg);%2 - 内存地址(*(addr))。
#define get_seg_long(seg,addr) ({ \
register unsigned long __res; \
__asm__("push %%fs; \
mov %%ax,%%fs; \
movl %%fs:%2,%%eax; \
pop %%fs" \
:"=a" (__res) \
:"0" (seg),"m" (*(addr))); \
__res;})
示例4
// 取fs段寄存器的值(选择符)。
// 输出:%0 - eax(__res)
#define _fs() ({ \
register unsigned short __res; \
__asm__("mov %%fs,%%ax" \
:"=a" (__res) \
: // 没有输入寄存器
); \
__res;})