linux内核中的汇编语言

Linux内核代码中,有一部分是用汇编语言编写的。其大部分是关于中断与异常处理的底层程序,还有就是与初始化有关的程序,以及一些核心代码中调用的公用子程序。

用汇编语言编写内核代码中的部分代码,大体上是出于如下几个方面考虑:

1linux内核中的底层程序直接与硬件打交道,需要一些专用的指令,而这些指令在C语言中并无对应的语言成分。

2)内核中实现某些操作的过程、程序段或函数,在运行时会非常频繁的被调用,这时用汇编语言编写,其时间效率会有大幅度提高。

3)在某些特殊的场合,一段程序的空间效率也非常重要,比如操作系统的引导程序一定要容纳在磁盘的第一个扇区中,多一个字节都不行。这时只能用汇编语言编写。

Linux内核代码中,以汇编语言编写的程序或者程序段,有两种不同的形式。一是完全的汇编代码,这样的代码采用.s作为文件名的后缀。第二种是嵌入在C程序中的汇编语言片断。

对于新接触linux内核源码的读者,哪怕他比较熟悉i386汇编语言,在理解这些汇编程序时都会感到困难,有的甚至会望而却步。其原因是:在内核汇编代码中GNU采用不同于常用Intel i386汇编语言的AT&T格式的汇编语言;而在嵌入C程序的片断中,更增加了一些指导汇编工具如何分配使用寄存器、以及如何与C程序中定义的变量相结合的语言成分。这些成分使得嵌入C程序的汇编语言片断实际上变成了一种介乎386汇编和C之间的一种中间语言。

首先我们讲一下AT&T格式与Intel格式汇编语言的以下主要区别,其它的详细情况可以参考AT&T汇编语言手册。

1)在Intel格式中大多使用大写字母,而AT&T格式中都使用小写字母。

2)在AT&T格式中,寄存器名上要加“%”作为前缀,而Intel格式则不带前缀。

3)在AT&T格式中,指令的源操作数在前,目标在后,恰好与Intel格式完全相反。

4)在AT&T格式中,访内指令的操作数大小由操作码后缀来决定。用作操作码后缀的字母有b(表示8位),w(表示16位),l(表示32位)。而在Intel格式中,则是在表示内存单元的操作数前面加上“BYTE PTR”,“WORD PTR”,“DWORD PTR”来表示。


当需要在C语言的程序中嵌入一段汇编语言程序段时,可以使用gcc提供的“asm”语句功能,例如,在include/asm/io.h中有这么一行:

#define __SLOW_DOWN_IO __asm__ __volatile__(“outb %a1,$0x80”)

这里,在asmvolatile前面的两个“__”字符,这是gccC语言的一种补充,含义我们在前面已经讲过了。下面我们看括号里面加上了引号的汇编指令,这是一条8位输出指令,如前所述在操作符上加上后缀“b”表示是8位操作,而0x80因为是常数,所以要加上前缀“$”,而寄存器a1也加了前缀“%”

上面这个例子还是很容易理解的,因为这就是简单的一条汇编语句,下面这个例子就困难多了(取子include/asm/atomic.h):

Static __inline__ void atomic_add(int i,atomic_t *v)

{

  __asm__ __volatile__

(

LOCK “addl %1,%0”

:”=m”(v->counter)

:”ir”(i),”m”(v->counter)

);

}

插入C代码中的汇编语言代码可以分成四部分,以冒号“:”加以分隔,其一般形式为:

指令部 : 输出部 : 输入部 : 损坏部

第一部分就是汇编语句本身,这一部分可以称为指令部,是必需的,而其它各部分则可以视情况而定。所以在最简单的情况下就与常规的汇编语句基本相同,如前面第一个例子。

当指令中的操作数要与C语言中的某些变量结合时,情况就复杂多了。如此例中,iv都是C语言函数的输入部分,怎么将其与汇编语言结合在一起呢?因为程序员无法确切知道gcc在嵌入点的前后会把哪一个寄存器分配用于哪一个变量,而且还得有个手段把使用寄存器的要求告诉gcc,反过来影响它对寄存器的分配。针对这个问题,gcc采用的办法是:程序员只提供具体的指令,而对寄存器的使用则只提供一个样板和一些约束条件,而把到底如何与变量结合的问题留给gccgas去处理。

在指令部中,数字加上前缀%,表示需要使用寄存器的样板操作数。这样,指令部中用到了几个不同的这种操作数,就说明有几个变量需要与寄存器结合,由gccgas在编译和汇编时根据后面的约束条件自行变通处理。那么,怎样表达对变量结合的约束条件呢?这就是其余几个部分的作用。输出部用以规定对输出变量的约束条件,必要时输出部可以有多个约束,互相之间用逗号分隔。每个输出约束以“=”号开头,然后是一个字母表示对操作数类型的说明,然后是关于变量结合的约束。例如在本例中,输出部里只有一个约束,“=m”表示相应的操作数(指令部中的%0)是一个内存单元v->counter

输出部后面是输入部,输入约束的格式与输出约束相似,但不带号。在本例中有两个输入约束,第一个为”ir”(i),表示指令中的%1可以是一个寄存器中的直接操作数(i表示immediater表示任何寄存器),并且该操作数来自C代码中的变量名i;第二个约束为”m”(v->counter)表示这是一个内存单元。

表示约束条件的字母有很多,主要有:“m””v””o”表示内存单元;”r”表示任何寄存器;”q”表示寄存器eaxebxecxedx之一;”i””h”表示直接操作数;”a””b””c””d”分别表示要求使用寄存器eax,ebx,ecxedx”S””D”分别表示要使用esiedi”I”表示常数(031)。

在有些操作中,除用于输入操作和输出操作数的寄存器以外,还要将若干寄存器用于计算或操作的中间结果。这样,这些寄存器原有的内容就损坏了,所以要在损坏部对操作的副作用加以说明,让gcc采取相应的措施,不过,有时候就直接把这些说明放在输出部了。另外还应注意,当输出部为空,即没有输出约束时,如果有输入约束存在,则必须保留分隔标记“:”号。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/383348.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构课程设计---c语言实现通讯录(动态扩容+文件存储)

1 题目一 : 通讯录 1.1问题描述 编写一个通讯录管理系统,以把所学数据结构知识应用到实际软件开发中去。每条信息至包含 :姓名(NAME )街道(STREET)城市(CITY)邮编&#…

linux内核panic

1. Linux Kernel Panic的产生的原因 panic是英文中是惊慌的意思,Linux Kernel panic正如其名,linux kernel不知道如何走了,它会尽可能把它此时能获取的全部信息都打印出来。 有两种主要类型kernel panic,后面会对这两类panic做详细…

数据结构课程设计------c实现散列表(二次探测再哈希)电话簿(文件存储)

题目二 :散列表的设计与实现 2.1问题描述 设计散列表实现电话号码查找系统,使得平均查找长度不超过2基本要求 (1)设每个记录有下列数据项:电话号码、用户名、地址; (2)从键盘输入各…

科技论文----论搜索引擎现状及发展趋势

搜索引擎现状及发展趋势 【摘要】 随着最近10年中国互联网的快速发展菜互联网已经彻底改变了人们的生活方式,而在互联网的发展过程中。搜索引擎发挥了巨大的推动作用。本文对搜索引擎的发展历史采用的技术,发展现状出现的问题以及未来发展方向进行了综述…

inittab文件格式

/etc/inittab文件是Linux系统第一个进程init的配置文件。其每个记录占一行,每行最多512个字符。该文件的每个记录的格式为: id:runlevel:action:process 其中,id是一个不超过4个字符的标识,用来唯一标识一条记录。runlevel表明该条…

数据结构课程设计------扫雷游戏(升级版,可展开)

本程序由团队中的一个人所写,本人看懂并写下此文章 题目:扫雷 3.1问题描述 扫雷游戏 [基本要求] (1)完成棋盘的初始化并在标准显示器中显示 (2)通过输入行列值确定用户输入 (3)游…

C语言的编译链接过程的介绍

发布时间: 2012-11-08 10:17 作者: 未知 来源: 51Testing软件测试网采编 字体: 小 中 大 | 上一篇 下一篇 | 打印 | 我要投稿 | 推荐标签: DotNet 软件开发 | 感言十年 C语言的编译链接过程要把我们编写的一个c程序(源代码&#x…

vs2013链接Mysql时出现 (由于找不到libmysql.dll,无法继续执行代码。重新安装程序可能会解决此问题)

将MySQL安装目录下的lib文件夹中 的libmysql.dll文件拷贝到C:\Windows\System32目录下即可

gcc 优化选项 -O1 -O2 -O3 -Os 优先级,-fomit-frame-pointer

少优化->多优化: O0 -->> O1 -->> O2 -->> O3 -O0表示没有优化,-O1为缺省值,-O3优化级别最高 英文解析: -O -O1 Optimize. Optimizing compilation takes somewhat more time, an…

const 和 #define 区别总结

const有类型,可进行编译器安全检查,#define 无类型,不可进行类型检查const 有作用域,而#define 不重视作用域,默认定义在指定作用域下有效的常量,那么#define 就不能用(可以用#undef结束宏定义生…

Eclipse : Unresolved inclusion

Eclipse 中新建C 或C 到项目时&#xff0c;头文件报警&#xff0c;显示“Unresolved inclusion:<stdio.h>” 虽然不影响项目到编译和运行&#xff0c;确也无法查看头文件&#xff0c;让人感觉实在不爽。下面是在国外到网站上看到解决方案&#xff0c;自己整理了一下拿来分…

c++对const增强 和cosnt分配内存情况

const增强 c语言中const是伪常量&#xff0c;可以通过指针修改 c中const会放到符号表中 c语言中const默认是外部连接&#xff0c;c中const默认是内部链接 #include<iostream> using namespace std;const int m_a 10; //在全局区域里&#xff0c;受到保护&…

Linux下crontab命令的用法

任务调度的crond常驻命令 crond 是linux用来定期执行程序的命令。当安装完成操作系统之后&#xff0c;默认便会启动此任务调度命令。crond命令每分锺会定期检查是否有要执行的工作&#xff0c;如果有要执行的工作便会自动执行该工作。而linux任务调度的工作主要分为以下两类&am…

c++中引用的作用

引用的基本语法 用途起别名 Type &别名原名 引用必须初始化 一旦初始化后&#xff0c;不能修改 对数组建立引用 #include<iostream>using namespace std;//1.引用基本语法 Type &别名原名void test01(){int a 10;int &b a;cout << "a"…

LVM (Logic Volume Management,逻辑卷管理)

是传统商业Unix就带有的一项高级磁盘管理工具&#xff0c;异常强大。后来LVM移植到了Linux操作系统上&#xff0c;尽管不像原来Unix版本那么强大&#xff0c;但瘦死的骆驼比马大&#xff0c;Linux的LVM仍然非常强大&#xff0c;可以在生产运行系统上面直接在线扩展硬盘分区&…

cpu中的MMU的作用

虚拟内存与物理内存之间的映射 用户空间映射到物理内存是独立的&#xff0c;提高安全性修改内存访问级别 &#xff08;0是最高级&#xff09;

Linux命令行与Shell脚本编程大全读书笔记

Linux内核4大主要功能&#xff1a; 内存管理 进程管理 设备管理 文件系统管理 Linux系统启动的进程和脚本管理 1./etc/inittab 管理系统开机时会自动启动的进程 2./etc/init.d 管理开机时启动或停止某个应用的脚本放在这个目录下&#xff0c;/etc/rcX.d目录在启动时&…

拷贝构造函数的总结

构造函数的分类及调用 按照参数分类 1.无参构造&#xff08;默认构造&#xff09; 2.有参构造按照类型分类 1.普通构造函数2.拷贝构造函数无参构造写法和调用 Person p1; 注意不能写Person (),因为编译器认为这个是函数声明有参构造函数写法 和调用 Person p2(10) 或者Per…

技术与技巧札记

Linux常用命令及技巧&#xff1a; &#xff08;1&#xff09;cat /proc/version 查看当前内核的版本 (2) 挂载nfs文件夹&#xff1a;需要先确认在&#xff0f;etc&#xff0f;exports文件&#xff0c;可以用于开发板挂载的文件夹 mount -o nolock 10.0.22.30:/root/sharednfs …

c++中new的总结(动态管理,malloc存在的问题,malloc与new的区别)

c中使用malloc出现的问题 程序员必须确定对象的长度malloc 返回一个&#xff08;void *&#xff09;指针 &#xff0c;c不允许将&#xff08;void*) 赋值给其它指针&#xff0c;必须强转malloc可能申请内存失败&#xff0c;所以必须判断返回值来保存内存分配成功用户在使用对象…