CPU虚拟化教程:虚拟机切入和退出

KVM模块中切入Guest模式的代码使用GCC的内联汇编编写,为了理解这段代码,我们需要简要地介绍一下这段内联汇编涉及的语法,其基本语法模板如下:


  1. asm volatile ( assembler template  
  2.     : output operands                  /* optional */ 
  3.     : input operands                   /* optional */ 
  4.     : list of clobbered registers      /* optional */ 
  5.     ); 

1. 关键字asm和volatile

asm为GCC关键字,表示接下来要嵌入汇编代码,如果asm与程序中其他命名冲突,可以使用__asm__。

volatile为可选关键字,表示不需要GCC对下面的汇编代码做任何优化,类似的,GCC也支持__volatile__。

2. 汇编指令(assembler template)

这部分即要嵌入的汇编指令,由于是在C语言中内联汇编代码,因此须用双引号将命令括起来。如果内嵌多行汇编指令,则每条指令占用1行,每行指令使用双引号括起来,以后缀\n\t结尾,其中\n为newline的缩写,\t为tab的缩写。由于GCC将每条指令以字符串的形式传递给汇编器AS,所以我们使用\n\t分隔符来分隔每一条指令,示例代码如下:


  1. __asm__ ("movl %eax, %ebx \n\t" 
  2.           "movl $56, %esi \n\t" 
  3.           "movl %ecx, $label(%edx,%ebx,$4) \n\t" 
  4.           "movb %ah, (%ebx) \n\t"); 

当使用扩展模式,即包含output、input和clobber list部分时,汇编指令中需要使用两个“%”来引用寄存器,比如%%rax;使用一个“%”来引用输入、输出操作数,比如%1,以便帮助GCC区分寄存器和由C语言提供的操作数。

3. 输出操作数(output operands)

内联汇编有零个或多个输出操作数,用来指示内联汇编指令修改了C代码中的变量。如果有多个输出参数,则需要对每个输出参数进行分隔。每个输出操作数的格式为:


  1. [[asmSymbolicName]] constraint (cvariablename) 

我们可以为输出操作数指定一个名字asmSymbolicName,汇编指令中可以使用这个名字引用输出操作数。

除了使用名字引用操作数外,还可以使用序号引用操作数。比如输出操作数有两个,那么可以用%0引用第1个输出操作数,%1引用第2个操作数,以此类推。

输出操作数的约束部分必须以“=”或者“+”作为前缀,“=”表示只写,“+”表示读写。在前缀之后,就可以是各种约束了,比如“=a”表示先将结果输出至rax/eax寄存器,然后再由rax/eax寄存器更新相应的输出变量。

cvariablename为代码中的C变量名字,需要使用括号括起来。

4. 输入操作数(input operands)

内联汇编可以有零个或多个输入操作数,输入操作数来自C代码中的变量或者表达式,作为汇编指令的输入,每个输入操作数的格式如下:


  1. [[asmSymbolicName]] constraint (cexpression) 

同输出操作数相同,也可以为每个输入操作数指定名字asmSymbolicName,汇编指令中可以使用这个名字引用输入操作数。

除了使用名字引用输入操作数外,还可以使用序号引用输入操作数。输入操作数的序号以最后一个输出操作数的序号加1开始,比如输出操作数有两个,输入操作数有3个,那么需要使用%2引用第1个输入操作数,%3引用第2个输入操作数,以此类推。

除了不必以“=”或者“+”前缀开头外,输入操作数的前缀与输出操作数基本相同。除了寄存器约束外,在后面的代码中我们还会看到“i”这个约束,表示这个输入操作数是个立即数(immediate integer)。

cexpression为代码中的C变量或者表达式,需要使用括号括起来。

【声明】:芜湖站长网内容转载自互联网,其相关言论仅代表作者个人观点绝非权威,不代表本站立场。如您发现内容存在版权问题,请提交相关链接至邮箱:bqsm@foxmail.com,我们将及时予以处理。

相关文章