关于作者

用户名:dudumami
笔名:dudumami
地区:
行业:其他

日历  

快速登录

+ 用户名:
+ 密 码:

在线留言



访问统计:
文章个数:17
评论个数:6
留言条数:8




Powered by BlogDriver 2.1

dudumami的博客

 

文章

初期简易辅食食谱(4-6个月)

 1、五谷根茎类食物中,最好先由米糊或麦糊开始喂,当宝宝适应米糊后,再尝试其他的食物。  
2、尽可能选用新鲜的蔬菜、水果,制成汤汁、稀释果汁喂宝宝。  
3、选择有带皮的水果种类,以及受农药污染与病原感染机会较少的水果,例如:柳丁、橘子、苹果、香蕉、木瓜等。  
每日建议食用量  
食物类别 食物名称 一天食用量  
五谷根茎类 米糊或麦糊 3/4-1碗  
蔬菜类 胡萝卜、菠菜等菜汤 1/3-2/3茶匙  
水果类 橘子、苹果、葡萄等稀释果汁 1/3-2/3茶匙  
蔬菜类简易制作  
菜汤——将深绿色、黄红色的蔬菜,例如菠菜、胡萝卜等,洗净、切碎、加少放水煮开,放至稍凉将汤汁倒出即可。  
菜泥——选择嫩叶蔬菜如菠菜、青江菜,或纤维少的南瓜、马铃薯等,洗净切小段或小块,加水煮熟后,捞出置于碗中,用汤匙刮下嫩叶或压成泥状即可。  
水果类简易制作  
果汁——柳丁、橘子;对切成两半后压汁。喂食前加入等量的冷开水稀释。  
葡萄、番茄:洗净后以热开水浸泡2分钟,去皮以干净的纱布包起来,用汤匙压出汁。  
香瓜、西瓜:用汤匙挖出果肉后,以干净纱布包住压汁。  
果泥——挑选果肉多、纤维少的水果,例如:香蕉、木瓜、苹果等,洗净去皮后,用汤匙挖出果肉并压成泥状即可。

苹果泥  
材料:苹果——半个  
做法:1、将苹果洗净、去皮、切半。  
2、用研磨板磨成泥状,盛在碗中即可。  
蔬菜泥  
材料:绿色蔬菜——10克  
牛奶——2汤匙  
玉米粉——1/5-1/4小匙  
做法:1、将绿色蔬菜嫩叶部分煮熟或蒸熟后,磨碎、过滤。  
2、取1中10-20克与少许水至锅中,边搅边煮。  
3、快好时,加入牛奶以及由1/5-1/4小匙玉米粉用等量水调好的玉米粉水,继续加热搅拌煮成泥状即可。  
乌龙面糊  
材料:乌龙面——10克  
水——1/2杯  
蔬菜泥——少量  
做法:1、乌龙面倒入沸水中煮至熟软捞起备用。  
2、煮熟的乌龙面与水同时倒入小锅内捣烂,煮开。  
3、起锅后加入少量蔬菜泥即可


- 作者: dudumami 2005年03月28日, 星期一 13:35  回复(1) |  引用(0) 加入博采

中期简易辅食食谱(7-9个月)
1、可以开始添加含蛋白质的食物了,如蛋黄、鱼、肉、豆腐等。切记:此时不能喂蛋白,以免造成宝宝过敏。  
2、食物的形态可从汤汁或糊状,渐渐转变为泥状或固体。  
3、五谷根茎类的食物种类,可以增加稀饭、面条、土司面包、馒头等。  
4、纤维较粗的蔬果和太油腻、辛辣刺激或筋太多的食物,仍然不适合喂宝宝吃。  
5、喂食前,先试试食物的温度,别烫着宝宝了。  
每日建议食用量  
食物类别 食物名称 一天食用量 一份食物的量  
鱼肉豆蛋类 蛋黄泥、豆腐或豆浆、 由右列食物中任选 蛋黄泥一天不超过1个,鱼、肉 1-1.5份 豆腐1块、豆浆1杯 肝泥鱼松或肉松 鱼、肉、肝泥2汤匙  
鱼松或肉松2汤匙  
五谷根蒸类 米糊或麦糊、馒头 由右列食物中 米糊或麦糊1碗  
土司面包、稀饭 任选2.5-4份 中馒头1/3个、土司面包1片  
面条或面线 稀饭1/2碗、面条1/2碗  
蔬菜类 胡萝卜、菠菜、空心菜、 1-2汤匙   
碗豆等菜汤或菜泥  
水果类 橘子、柳丁、苹果、香瓜、 1-2汤匙   
木瓜、葡萄等果汁或果泥   
五谷根茎类简易制作  
米糊——将米粉加入奶水或开水调成糊状即可。  
面包、馒头——可撕成长条状,让长牙的宝宝用手拿着吃;或去掉硬皮,撕成小块泡在牛奶中喂宝宝吃。  
稀饭——白米加入7或8倍体积的水,以小火慢煮成浓粥或薄粥。注意放凉后再喂食。  
  
  
中期简易辅食食谱  
肝泥粥  
材料:鸡肝——20g  
米——20g  
水——1.5杯  
做法:  
1、鸡肝去膜、去筋、剁碎成泥状备用。  
2、米加水煮开后,改开小火,加盖焖煮至烂,拌入肝泥,再煮开即可。  
小鱼蛋花粥  
材料:白饭——1/2碗  
水——1杯  
吻仔鱼——10g  
蛋黄——1/2个  
做法:  
1、将白饭与水放入小锅煮至烂。  
2、取吻仔鱼放入锅中小煮一下,将蛋黄打散淋在粥上,搅拌至熟即可。  
蔬菜豆腐泥  
材料:去皮红萝卜——5g  
嫩豆腐——1/6块  
豌豆——1/2条  
蛋黄——1/2个  
水——1/2杯  
酱油——少许  
做法:  
1、将去皮红萝卜及豌豆烫熟后切成极小块。  
2、将水与(1)料放入小锅,嫩豆腐边捣碎边加进去,加少许酱油,煮到汤汁变少。  
3、最后将蛋黄打散加入锅里煮熟即可。

- 作者: dudumami 2005年03月28日, 星期一 13:33  回复(0) |  引用(0) 加入博采

初学单片机几个不易掌握的概念
初学单片机几个不易掌握的概念
    随着电子技术的迅速发展,计算机已深入地渗透到我们的生活中,许多电子爱好者开始学习单片机知识,但单片机的内容比较抽象,相对电子爱好者已熟悉的模拟电路、数字电路,单片机中有一些新的概念,这些概念非常基本以至于一般作者不屑去谈,教材自然也不会很深入地讲解这些概念,但这些内容又是学习中必须要理解的,下面就结合本人的学习、参与教学所获得的一些经验,对这些最基本概念作一说明,希望对自学者有所帮助。

一、总线
    我们知道,一个电路总是由元器件通过电线连接而成的,在模拟电路中,连线并不成为一个问题,因为各器件间一般是串行关系,各器件之间的连线并不很多,但计算机电路却不一样,它是以微处理器为核心,各器件都要与微处理器相连,各器件之间的工作必须相互协调,所以就需要的连线就很多了,如果仍如同模拟电路一样,在各微处理器和各器件间单独连线,则线的数量将多得惊人,所以在微处理机中引入了总线的概念,各个器件共同享用连线,所有器件的8根数据线全部接到8根公用的线上,即相当于各个器件并联起来,但仅这样还不行,如果有两个器件同时送出数据,一个为0,一个为1,那么,接收方接收到的究竟是什么呢?这种情况是不允许的,所以要通过控制线进行控制,使器件分时工作,任何时候只能有一个器件发送数据(可以有多个器件同时接收)。器件的数据线也就被称为数据总线,器件所有的控制线被称为控制总线。在单片机内部或者外部存储器及其它器件中有存储单元,这些存储单元要被分配地址,才能使用,分配地址当然也是以电信号的形式给出的,由于存储单元比较多,所以,用于地址分配的线也较多,这些线被称为地址总线。

二、数据、地址、指令
    之所以将这三者放在一起,是因为这三者的本质都是一样的──数字,或者说都是一串‘
0'和‘1'组成的序列。换言之,地址、指令也都是数据。指令:由单片机芯片的设计者规定的一种数字,它与我们常用的指令助记符有着严格的一一对应关系,不可以由单片机的开发者更改。地址:是寻找单片机内部、外部的存储单元、输入输出口的依据,内部单元的地址值已由芯片设计者规定好,不可更改,外部的单元可以由单片机开发者自行决定,但有一些地址单元是一定要有的(详见程序的执行过程)。数据:这是由微处理机处理的对象,在各种不同的应用电路中各不相同,一般而言,被处理的数据可能有这么几种情况:

1.地址(如MOV  DPTR #1000H),即地址1000H送入DPTR
2.方式字或控制字(如MOV  TMOD ,#3),3即是控制字。
3.常数(如MOV TH0#10H10H即定时常数。
4.实际输出值(如P1口接彩灯,要灯全亮,则执行指令:MOV P1#0FFH,要灯全暗,则执行指令:MOV P1#00H)这里0FFH00H都是实际输出值。又如用于LED的字形码,也是实际输出的值。理解了地址、指令的本质,就不难理解程序运行过程中为什么会跑飞,会把数据当成指令来执行了。

三、P0口、P2口和P3的第二功能用法
    初学时往往对P0口、P2口和P3口的第二功能用法迷惑不解,认为第二功能和原功能之间要有一个切换的过程,或者说要有一条指令,事实上,各端口的第二功能完全是自动的,不需要用指令来转换。如P3.6P3.7分别是WRRD信号,当微片理机外接RAM或有外部I/O口时,它们被用作第二功能,不能作为通用I/O口使用,只要一微处理机一执行到MOVX指令,就会有相应的信号从P3.6P3.7送出,不需要事先用指令说明。事实上‘不能作为通用I/O口使用'也并不是‘不能'而是(使用者)‘不会'将其作为通用I/O口使用。你完全可以在指令中按排一条SETB P3.7的指令,并且当单片机执行到这条指令时,也会使P3.7变为高电平,但使用者不会这么去做,因为这通常这会导致系统的崩溃(即死机)。

四、程序的执行过程
    单片机在通电复位后8051内的程序计数器(PC)中的值为‘0000',所以程序总是从‘0000'单元开始执行,也就是说:在系统的ROM中一定要存在‘0000'这个单元,并且在‘0000'单元中存放的一定是一条指令。

五、堆栈
    堆栈是一个区域,是用来存放数据的,这个区域本身没有任何特殊之处,就是内部RAM的一部份,特殊的是它存放和取用数据的方式,即所谓的‘先进后出,后进先出',并且堆栈有特殊的数据传输指令,即‘PUSH'和‘POP',有一个特殊的专为其服务的单元,即堆栈指针SP,每当执一次PUSH指令时,SP就(在原来值的基础上)自动加1,每当执行一次POP指令,SP就(在原来值的基础上)自动减1。由于SP中的值可以用指令加以改变,所以只要在程序开始阶段更改了SP的值,就可以把堆栈设置在规定的内存单元中,如在程序开始时,用一条MOV SP#5FH指令,就时把堆栈设置在从内存单元60H开始的单元中。一般程序的开头总有这么一条设置堆栈指针的指令,因为开机时,SP的初始值为07H,这样就使堆栈从08H单元开始往后,而08H1FH这个区域正是8031的第二、三、四工作寄存器区,经常要被使用,这会造成数据的浑乱。不同作者编写程序时,初始化堆栈指令也不完全相同,这是作者的习惯问题。当设置好堆栈区后,并不意味着该区域成为一种专用内存,它还是可以象普通内存区域一样使用,只是一般情况下编程者不会把它当成普通内存用了。

六、单片机的开发过程
    这里所说的开发过程并不是一般书中所说的从任务分析开始,我们假设已设计并制作好硬件,下面就是编写软件的工作。在编写软件之前,首先要确定一些常数、地址,事实上这些常数、地址在设计阶段已被直接或间接地确定下来了。如当某器件的连线设计好后,其地址也就被确定了,当器件的功能被确定下来后,其控制字也就被确定了。然后用文本编缉器(如
EDITCCED等)编写软件,编写好后,用编译器对源程序文件编译,查错,直到没有语法错误,除了极简单的程序外,一般应用仿真机对软件进行调试,直到程序运行正确为止。运行正确后,就可以写片(将程序固化在EPROM中)。在源程序被编译后,生成了扩展名为HEX的目标文件,一般编程器能够识别这种格式的文件,只要将此文件调入即可写片。在此,为使大家对整个过程有个认识,举一例说明:

 

 ORG 0000H 
 

LJMP START

 
 ORG 040H 
START:MOV SP#5FH;设堆栈
LOOP:NOP 
 LJMP LOOP;循环
 END;结束
1

 

 03000000020040BB
 0700400075815F000200431F
 表2

02 00 40 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 75 81 5F 00 02 00 43

3

    所以1为源程序,表2是汇编后得到的HEX文件,表3是由HEX文件转换成的目标文件,也就是最终写入EPROM的文件,它由编程器转换得到,也可以由HEXBIN一类的程序转换得到。学过手工汇编者应当不难找出表3与表1的一一对应关系,值得注意的是从02 00 40后开始的一长串‘FF',直到75 81,这是由于伪指令:ORG 040H造成的结果。  

七、仿真、仿真机
    仿真是单片机开发过程中非常重要的一个环节,除了一些极简单的任务,一般产品开发过程中都要进行仿真,仿真的主要目的是进行软件调试,当然借助仿真机,也能进行一些硬件排错。一块单片机应用电路板包括单片机部份及为达到使用目的而设计的应用电路,仿真就是利用仿真机来代替应用电路板(称目标机)的单片机部份,对应用电路部份进行测试、调试。仿真有CPU仿真和ROM仿真两种,所谓CPU仿真是指用仿真机代替目标机的CPU,由仿真机向目标机的应用电路部份提供各种信号、数据,进行调试的方法。这种仿真可以通过单步运行、连续运行等多种方法来运行程序,并能观察到单片机内部的变化,便于改正程序中的错误。所谓ROM仿真,就是用仿真机代替目标机的ROM,目标机的CPU工作时,从仿真机中读取程序,并执行。这种仿真其实就是将仿真机当成一片EPROM,只是省去了擦片、写片的麻烦,并没有多少调试手段可言。通常这是二种不同类型的仿真机,也就是说,一台仿真机不能既做CPU仿真,又做ROM仿真。可能的情况下,当然以CPU仿真好。 所以以上系个人对单片机的理解,如有不对之处,请多多指点 


- 作者: dudumami 2005年03月24日, 星期四 09:58  回复(0) |  引用(0) 加入博采

单片机基础知识
单片机基础知识

单片机的组成

  单片机要自动完成计算,它应该具有哪些最重要的部分呢?


  我们以打算盘为例计算一道算术题。例:36+163×156-166÷34。现在要进行运算,首先需要一把算盘,其次是纸和笔。我们把要计算的问题记录下来,然后第一步先算163×156,把它与36相加的结果记在纸上,然后计算166÷34,再把它从上一次结果中减去,就得到最后的结果。

  现在,我们用单片机来完成上述过程,显然,它首先要有代替算盘进行运算的部件,这就是"运算器";其次,要有能起到纸和笔作用的器件,即能记忆原始题目、原始数据和中间结果,还要记住使单片机能自动进行运算而编制的各种命令。这类器件就称为"存贮器"。此外,还需要有能代替人作用的控制器,它能根据事先给定的命令发出各种控制信号,使整个计算过程能一步步地进行。但是光有这三部分还不够,原始的数据与命令要输入,计算的结果要输出,都需要按先后顺序进行,有时还需等待。

  如上例中,当在计算163×156时,数字36就不能同时进入运算器。因此就需要在单片机上设置按控制器的命令进行动作的"门",当运算器需要时,就让新数据进入。或者,当运算器得到最后结果时,再将此结果输出,而中间结果不能随便"溜出"单片机。这种对输入、输出数据进行一定管理的"门"电路在单片机中称为"口"(Port)。在单片机中,基本上有三类信息在流动,一类是数据,即各种原始数据(如上例中的36、163等)、中间结果(如166÷34所得的商4、余数30等)、程序(命令的集合)等。这样要由外部设备通过"口"进入单片机,再存放在存贮器中,在运算处理过程中,数据从存贮器读入运算器进行运算,运算的中间结果要存入存贮器中,或最后由运算器经"出入口"输出。

  用户要单片机执行的各种命令(程序)也以数据的形式由存贮器送入控制器,由控制器解读(译码)后变为各种控制信号,以便执行如加、减、乘、除等功能的各种命令。所以,这一类信息就称为控制命令,即由控制器去控制运算器一步步地进行运算和处理,又控制存贮器的读(取出数据)和写(存入数据)等。第三类信息是地址信息,其作用是告诉运算器和控制器在何处去取命令取数据,将结果存放到什么地方,通过哪个口输入和输出信息等。

  存贮器又分为只读存贮器和读写存贮器两种,前者存放调试好的固定程序和常数,后者存放一些随时有可能变动的数据。顾名思义,只读存贮器一旦将数据存入,就只能读出,不能更改(EPROM、E2PROM等类型的ROM可通过一定的方法来更改、写入数据——编者注)。而读写存贮器可随时存入或读出数据。

  实际上,人们往往把运算器和控制器合并称为中央处理单元——CPU。单片机除了进行运算外,还要完成控制功能。所以离不开计数和定时。因此,在单片机中就设置有定时器兼计数器,其基本结构与本连载之(二)中的举例类似。到这里为止,我们已经知道了单片机的基本组成,即单片机是由中央处理器(即CPU中的运算器和控制器)、只读存贮器(通常表示为ROM)、读写存贮器(又称随机存贮器通常表示为RAM)、输入/输出口(又分为并行口和串行口,表示为I/O口)等等组成。实际上单片机里面还有一个时钟电路,使单片机在进行运算和控制时,都能有节奏地进行。另外,还有所谓的"中断系统",这个系统有"传达室"的作用,当单片机控制对象的参数到达某个需要加以干预的状态时,就可经此"传达室"通报给CPU,使CPU根据外部事态的轻重缓急来采取适当的应付措施。

  现在,我们已经知道了单片机的组成,余下的问题是如何将它们的各部分连接成相互关联的整体呢?实际上,单片机内部有一条将它们连接起来的"纽带",即所谓的"内部总线"。此总线有如大城市的"干道",而CPU、ROM、RAM、I/O口、中断系统等就分布在此"总线"的两旁,并和它连通。从而,一切指令、数据都可经内部总线传送,有如大城市内各种物品的传送都经过干道进行。 

 

单片机指令系统与汇编语言程序

  前面已经讲述了单片机的几个主要组成部分,这些部分构成了单片机的硬件。所谓硬件(Hardware),就是看得到,摸得到的实体。但是,光有这样的硬件,还只是有了实现计算和控制功能的可能性。单片机要真正地能进行计算和控制,还必须有软件(Software)的配合。软件主要指的是各种程序。只有将各种正确的程序"灌入"(存入)单片机,它才能有效地工作。单片机所以能自动地进行运算和控制,正是由于人把实现计算和控制的步骤一步步地用命令的形式,即一条条指令(Instruction)预先存入到存贮器中,单片机在CPU的控制下,将指令一条条地取出来,并加以翻译和执行。就以两个数相加这一简单的运算来说,当需要运算的数已存入存贮器后,还需要进行以下几步:


第一步:把第一个数从它的存贮单元(Location)中取出来,送至运算器。
第二步:把第二个数从它所在的存贮单元中取出来,送至运算器;
第三步:相加; 第四步:把相加完的结果,送至存贮器中指定的单元。

  所有这些取数、送数、相加、存数等等都是一种操作(Operation),我们把要求计算机执行的各种操作用命令的形式写下来,这就是指令。但是怎样才能辨别和执行这些操作呢?这是在设计单片机时由设计人员赋予它的指令系统所决定的。一条指令,对应着一种基本操作;单片机所能执行的全部指令,就是该单片机的指令系统(Iustruction Set),不同种类的单片机,其指令系统亦不同。


   使用单片机时,事先应当把要解决的问题编成一系列指令。这些指令必须是选定的单片机能识别和执行的指令。单片机用户为解决自己的问题所编的指令程序,称为源程序(Source Program)。指令通常分为操作码(Opcode)和操作数(Operand)两大部分。操作码表示计算机执行什么操作,即指令的功能;操作数表示参加操作的数或操作数所在的地址(即操作数所存放的地方编号)。因为单片机是一种可编程器件,只"认得"二进码(0、1)。要单片机运作,单片机系统中的所有指令,都必须以二进制编码的形式来表示。例如,在Intel公司的MCS-51系列单片机中,从存贮器中取出一数到CPU中的累加器(在运算器中,参与运算、存放运算结果的专用寄存器)的指令代码为74H,累加器内容加立即数的代码为24H,再加上立即数代码,累加器送数到内部RAM存贮器的代码为F6H~F7H等。这些指令是用十六进制表示二进制的机器码。

  MCS-51单片机的字长为8位,有时,要完成某些操作用一个字节尚不能充分表达。所以,在指令系统中有单字节指令,也有多字节指令。机器码是由一连串的0和1组成,没有明显的特征,不好记忆,不易理解,易出错。所以,直接用它来编写程序十分困难。因而,人们就用一些助记符(Mue monic)——通常是指令功能的英文缩写来代替操作码,如MCS-51中数的传送常用MOV(Move的缩写)、加法用Add(Addition的缩写)来作为助记符。这样,每条指令有明显的动作特征,易于记忆和理解,也不容易出错。用助记符来编写的程序称为汇编语言程序。但是,助记符编写的程序便于人理解,可单片机却只认识二进制机器代码,因此,为了让单片机能"读懂"汇编语言程序必须再转换成由二进制机器码构成的程序,这种转换过程,就称为"汇编"。汇编可借助于人工查表法来实现,也可借助PC机通过所谓"交叉汇编程序"来完成。由机器码构成的用户程序一旦"进入"了单片机,再"启动"单片机,就可让它执行输入程序所规定的任务。

MCU--51 CPU和存储器

单片机8051的CPU由运算器和控制器组成。

  一、运算器                                       

    运算器以完成二进制的算术/逻辑运算部件ALU为核心,再加上暂存器TMP、累加器ACC、寄存器B、程序状态标志寄存器PSW及布尔处理器。累加器ACC是一个八位寄存器,它是CPU中工作最频繁的寄存器。在进行算术、逻辑运算时,累加器ACC往往在运算前暂存一个操作数(如被加数),而运算后又保存其结果(如代数和)。寄存器B主要用于乘法和除法操作。标志寄存器PSW也是一个八位寄存器,用来存放运算结果的一些特征,如有无进位、借位等。其每位的具体含意如下所示。PSW CY AC FO RS1 RS0 OV - P对用户来讲,最关心的是以下四位。

  1进位标志CY(PSW7)。它表示了运算是否有进位(或借位)。如果操作结果在最高位有进位(加法)或者借位(减法),则该位为1,否则为0。

  2辅助进位标志AC。又称半进位标志,它反映了两个八位数运算低四位是否有半进位,即低四位相加(或减)有否进位(或借位),如有则AC为1状态,否则为0。

  3溢出标志位OV。MCS-51反映带符号数的运算结果是否有溢出,有溢出时,此位为1,否则为0。

  4奇偶标志P。反映累加器ACC内容的奇偶性,如果ACC中的运算结果有偶数个1(如11001100B,其中有4个1),则P为0,否则,P=1。
PSW的其它位,将在以后再介绍。由于PSW存放程序执行中的状态,故又叫程序状态字?运算器中还有一个按位(bit)进行逻辑运算的逻辑处理机(又称布尔处理机)。其功能在介绍位指令时再说明。

二、控制器

  控制器是CPU的神经中枢,它包括定时控制逻辑电路、指令寄存器、译码器、地址指针DPTR及程序计数器PC、堆栈指针SP等。这里程序计数器PC是由16位寄存器构成的计数器。要单片机执行一个程序,就必须把该程序按顺序预先装入存储器ROM的某个区域。单片机动作时应按顺序一条条取出指令来加以执行。因此,必须有一个电路能找出指令所在的单元地址,该电路就是程序计数器PC。当单片机开始执行程序时,给PC装入第一条指令所在地址,它每取出一条指令(如为多字节指令,则每取出一个指令字节),PC的内容就自动加1,以指向下一条指令的地址,使指令能顺序执行。只有当程序遇到转移指令、子程序调用指令,或遇到中断时(后面将介绍),PC才转到所需要的地方去。8051 CPU碢C指定的地址,从ROM相应单元中取出指令字节放在指令寄存器中寄存,然后,指令寄存器中的指令代码被译码器译成各种形式的控制信号,这些信号与单片机时钟振荡器产生的时钟脉冲在定时与控制电路中相结合,形成按一定时间节拍变化的电平和时钟,即所谓控制信息,在CPU内部协调寄存器之间的数据传输、运算等操作。

三、存储器

  存储器是单片机的又一个重要组成部分,图6给出了一种存储容量为256个单元的存储器结构示意图。其中每个存储单元对应一个地址,256个单元共有256个地址,用两位16进制数表示,即存储器的地址(00H~FFH)。存储器中每个存储单元可存放一个八位二进制信息,通常用两位16进制数来表示,这就是存储器的内容。存储器的存储单元地址和存储单元的内容是不同的两个概念,不能混淆。

一、程序存储器
   程序是控制计算机动作的一系列命令,单片机只认识由"0"和"1"代码构成的机器指令。如前述用助记符编写的命令MOV A,#20H,换成机器认识的代码74H、20H:(写成二进制就是01110100B和00100000B)。在单片机处理问题之前必须事先将编好的程序、表格、常数汇编成机器代码后存入单片机的存储器中,该存储器称为程序存储器。程序存储器可以放在片内或片外,亦可片内片外同时设置。由于PC程序计数器为16位,使得程序存储器可用16位二进制地址,因此,内外存储器的地址最大可从0000H到FFFFH。8051内部有4k字节的ROM,就占用了由0000H~0FFFH的最低4k个字节,这时片外扩充的程序存储器地址编号应由1000H开始,如果将8051当做8031使用,不想利用片内4kROM,全用片外存储器,则地址编号仍可由0000H开始。不过,这时应使8051的第{31}脚(即EA脚)保持低电平。当EA为高电平时,用户在0000H至0FFFH范围内使用内部ROM,大于0FFFH后,单片机CPU自动访问外部程序存储器。

二、数据存储器

  单片机的数据存储器由读写存储器RAM组成。其最大容量可扩展到64k,用于存储实时输入的数据。8051内部有256个单元的内部数据存储器,其中00H~7FH为内部随机存储器RAM,80H~FFH为专用寄存器区。实际使用时应首先充分利用内部存储器,从使用角度讲,搞清内部数据存储器的结构和地址分配是十分重要的。因为将来在学习指令系统和程序设计时会经常用到它们。8051内部数据存储器地址由00H至FFH共有256个字节的地址空间,该空间被分为两部分,其中内部数据RAM的地址为00H~7FH(即0~127)。而用做特殊功能寄存器的地址为80H~FFH。在此256个字节中,还开辟有一个所谓"位地址"区,该区域内不但可按字节寻址,还可按"位(bit)"寻址。对于那些需要进行位操作的数据,可以存放到这个区域。从00H到1FH安排了四组工作寄存器,每组占用8个RAM字节,记为R0~R7。究竟选用那一组寄存器,由前述标志寄存器中的RS1和RS0来选用。在这两位上放入不同的二进制数,即可选用不同的寄存器组,如附表1所示。

三、特殊功能寄存器

  特殊功能寄存器(SFR)的地址范围为80H~FFH。在MCS-51中,除程序计数器PC和四个工作寄存器区外,其余21个特殊功能寄存器都在这SFR块中。其中5个是双字节寄存器,它们共占用了26个字节。各特殊功能寄存器的符号和地址见附表2。其中带*号的可位寻址。特殊功能寄存器反映了8051的状态,实际上是8051的状态字及控制字寄存器。用于CPU PSW便是典型一例。这些特殊功能寄存器大体上分为两类,一类与芯片的引脚有关,另一类作片内功能的控制用。与芯片引脚有关的特殊功能寄存器是P0~P3,它们实际上是4个八位锁存器(每个I/O口一个),每个锁存器附加有相应的输出驱动器和输入缓冲器就构成了一个并行口。MCS-51共有P0~P3四个这样的并行口,可提供32根I/O线,每根线都是双向的,并且大都有第二功能。其余用于芯片控制的寄存器中,累加器A、标志寄存器PSW、数据指针DPTR等的功能前已提及,而另一些寄存器的功能在后面有关部分再作进一步介绍

单片机的指令系统和寻址方式

  单片机要正常运作,事先需编制程序,再把程序放入存贮器中,然后由CPU执行该程序。程序是由指令组成的,指令的基本组成是操作码和操作数。单片机的品种很多,设计时怎样表示操作码和操作数,都有各自的规定,再有指令代码也各不相同,因此,必须对所选单片机的全部指令,也就是所谓"指令系统",有足够的了解。各个系列的单片机虽然有不同的指令系统,但也有其共同性。掌握一种单片机的指令系统,对其它系列单片机可以起到触类旁通的作用。MCS-51单片机应用广泛,派生品种多,具有代表性,所以,这里以MCS-51系列的指令系统为例说明"指令"的组成和应用。

1、MOV A,#20H
   这条指令表示把20H这个数送入累加器A中(一个特殊功能寄存器)。


2、ADD A,70H
   这条指令表示把累加器A中的内容(在上例中送入的#20H)和存贮器中地址为70H单元中的内容(也是一个数字),通过算术逻辑单元(英文缩写为ALU)相加,并将结果保留在A中。这里MOV、ADD等称为操作码,而A、#20H、70H等均称为操作数。在汇编语言程序中,操作码通常由英文单词缩写而成,这样有助于记忆,所以又称助记符。如MOV就是英文单词MOVE的缩写,含有搬移的意思;而ADD即为英文单词,其意为相加。因此,对于略懂英语的用户,掌握单片机指令的含意是较为方便的。操作数有多种表示法,如以上的#20H称为立即数,即20H就是真正的操作数。而70H是存贮器中某个单元的地址,在该单元中,放着操作数(比如说是3AH),ADD A,70H不是将70H和A中的内容相加,而是从存贮器70H单元中将3AH取出和A中的内容相加。由上可知,要找到实际操作数,有时就要转个弯,甚至转几个弯,这个过程称为寻址,MCS-51共有7种寻址方式,现介绍如下:


一、立即寻址:                                     

    操作数就写在指令中,和操作码一起放在程序存贮器中。把"#"号放在立即数前面,以表示该寻址方式为立即寻址,如#20H。


二、寄存器寻址:                                    

    操作数放在寄存器中,在指令中直接以寄存器的名字来表示操作数的地址。例如MOV A,R0就属于寄存器寻址,即将R0寄存器的内容送到累加器A中。


三、直接寻址:                                     

    操作数放在单片机的内部RAM某单元中,在指令中直接写出该单元的地址。如前例的ADD A,70H中的70H。


四、寄存器间接寻址:

    操作数放在RAM某个单元中,该单元的地址又放在寄存器R0或R1中。 如果RAM的地址大于256,则该地址存放在16位寄存器DPTR(数据指针)中,此时在寄存器名前加@符号来表示这种间接寻址。如MOV A,@ R0。其它还有变址寻址、相对寻址、位寻址等,待以后再详细介绍。可能有人会问,在指令中直接给出实际操作数,不是简单、明了吗?为什么还要用其它几种寻址方式呢?这是因为在编制程序时很难一下子就给出操作数。如用单片机控制温度时,时时需要将给定的控制温度(如20℃)减去环境温度,而环境温度时时有变化,显然无法在程序指令中给出,只有通过一定方式,将其送入某个输入/输出口,再存放在某个寄存器中,这就必须用到寄存器寻址。又如要进行算术运算,要计算每班学员各科成绩的平均值,如果把每个学员的各科都编一个程序,在程序中直接给出该学员各科成绩,再求平均值,显然太麻烦。这里可以编一个求平均成绩的通用程序,把每位学员的成绩送入存贮器的各个单元中,这时可采取直接寻址,一个程序可供每个学员用,不是更方便吗?所以,寻址方式越多,编制程序就越方便、灵活,适用范围就越广。

  寻址有如找人,如被找的人有手机、BP机、座机电话等多种联系方式则就容易找到他,单片机也是如此,寻址方式越多,找操作数越方便,单片机的功能就越强。前面介绍51系列单片机的寻址方式时,常遇到单片机内部的一些寄存器、累加器A、通用寄存器R0~R7、数据指针DPTR和存贮器等。在以后介绍指令时,数据就要在这些寄存器、存贮器之间传送,或者进行运算。因此,编制程序就需熟悉单片机的内部结构。


8051单片机的内部总体结构其基本特性


8位CPU、片内振荡器
4k字节ROM、128字节RAM
21个特殊功能寄存器
32根I/O线
可寻址的64k字节外部数据、程序存贮空间
2个16位定时器、计数器
中断结构:具有二个优先级、五个中断源
一个全双口串行口
位寻址(即可寻找某位的内容)功能,适于按位进行逻辑运算的位处理器。除128字节RAM、4k字节ROM和中断、串行口及定时器模块外,还有4组I/O口P0~P3,余下的就是CPU的全部组成。把4kROM换为EPROM就是8751的结构,如去掉ROM/EPROM部分即为8031的框图,如果将ROM置换为Flash存贮器或EEPROM,或再省去某些I/O,即可得到51系列的派生品种,如89C51、AT89C2051等单片机的框图。

  单片机各部分是通过内部的总线有机地连接起来的


- 作者: dudumami 2005年03月24日, 星期四 09:58  回复(1) |  引用(0) 加入博采

辅食的制作
辅食的制作

桔子汁:先将手用肥皂水洗净,再用消毒的自来水或温开水冲洗干净。取纱布一小块,大口奶瓶一个,上锅蒸煮消毒,亦可用滚烫的开水消毒。把洗净的桔子去皮,将消毒的纱布覆盖在大口奶瓶上,用手将桔子瓣汁挤在纱布上过滤即可。用时加1倍水稀释,亦可加入少许白糖。

  菜汁:菠菜、油菜、小白菜等均可。将任何一种菜洗净切碎,放入等量沸水中,盖好锅盖,急火煮10分钟,离火后带盖放置20-30分钟,用汤匙压菜取汁,加少许盐即可饮用。注意:不要将碎菜放入冷水中再上火加热,这样会使维生素损失40%左右。

  番茄汁:将成熟的番茄洗净后,放在大碗中开水烫软去皮,搅碎后用消过毒的纱布过滤取汁,汁中放入少许白糖,用适量温开水稀释后喂食。

  山楂水:取山楂片100克,浸入冷水中快速洗净,放入茶壶中,以沸水冲泡,加盖焖至水温降到微温时,把山楂水倒入杯中,加入适量白糖,充分搅匀即可饮用。

  胡萝卜汤:取胡萝卜500克,洗净切碎,放入锅中加水适量,煮沸约1小时离火,待温,用纱布过滤取汁,放入少许白糖调匀,即可饮用。

  清炖肉汤:将鸡肉或牛肉500克,用沸水冲去血污,切成碎块放入锅中,加入葱花、姜丝和水适量,急火烧开后撇去浮沫,加入食盐少许,再慢火炖3个小时,至肉酥烂离火。吃面条、面片或豆腐脑时适量加入食用。

  鸡蛋面片汤:取大碗1只,加入面粉100克,打入鸡蛋1个,用筷子搅成面团,放在面板上擀成薄片,用刀切细。将锅内加水适量,烧开后下面片,煮烂后离火,加入1匙酱油和少许香油,即可喂用。

  浓米汤:取大米或小米。淘洗干净后,放入锅中加水适量,煮成烂粥后,撇取米汤即可喂用。

蛋黄泥:取鸡蛋1个,煮熟后去壳及蛋白,取其蛋黄,加开水少许,用汤匙搅烂成泥即可。亦可将蛋黄用牛奶、米汤、菜汁调成糊状喂食。

  肝泥:将猪肝用沸水冲去血污。锅内加水适量,放入葱花、姜丝及酱油少许,同猪肝一块急火烧开,撇去浮沫,慢火炖熟。肝熟后放入碗中,加少许原汤,用汤匙搅成泥状即可。


  菜泥:原料同菜汁,将洗净切碎的菜投入沸水中,急火煮烂(时间越短越好,但要煮软)捞出后盛入碗中,用汤匙捣烂,去除粗渣纤维,加入少许盐即可。

  水果泥:各种熟透的水果如香蕉、萍果、梨等。将水果洗净去皮,用小勺轻轻刮削果肉成泥状即可

  土豆泥:取土豆洗净去皮,切成小块,放入水中煮烂,盛入碗中,用汤匙捣成泥状,然后用少许油、盐、葱花炒过即可。

  菜末:菜末不同于菜泥,不用煮,可用急火炒,这样不仅维生素损失少,而且滋味比较好,能增加孩子食欲。可将各种摘洗干净的鲜嫩蔬菜,如油菜、油菜、小白菜、黄瓜等切成碎末。油锅炝葱花,放入菜末急炒(炒透炒熟),加盐、味精等少许调味即可。

  鱼肉末:取鱼肉适量,上锅蒸熟,取出后捣烂,加盐少许拌匀即可喂用。

  肉末:取瘦猪肉或瘦牛肉或鸡脯肉适量,剁碎。油锅炝葱花,加盐、酱油少许,至全熟加味精少许即成。在喂粥或面条时加入。

  烂米粥:将好大米或小米50克。淘洗干净后,放入小锅中,加水500克,浸泡1小时左右,使米粒吸水膨胀,上急米烧开后,再慢火煮至极烂熟即可喂用。

  肝泥粥:将猪肝洗净,置锅中煮熟,去筋膜。在熟砧板上剁成泥状,放入粥或面条中煮沸后食用。

  蛋花粥:先将鸡蛋1个打碎,取浓米汤一碗,置于火上,将蛋倒入边烧边搅,烧沸煮透成蛋花米汤状,再将粥一碗放入,煮沸加盐、熟油、味精即成。

  鱼粥:将鱼(黄鱼、带鱼等)洗净去鳞、腮及内脏,去骨,根据需要量将鱼肉压碎混入粥中,加入少许食盐、味精及葱、姜等同煮,即成鱼粥。

  菜肉粥:取绿叶蔬菜适量,洗净切碎,将大米或小米60克淘洗干净后放入锅中加水适量煮粥,待熟时下入菜末或肉末及食盐少许,煮烂即成。可作为9-10个月婴儿的主食。


- 作者: dudumami 2005年03月19日, 星期六 20:27  回复(0) |  引用(0) 加入博采

在MIPS开发板上建立Linux系统及开发环境 (转)
MIPS开发板上建立Linux系统及开发环境
 
 
    介绍了利用已有的MIPS版Linux源文件包,编译可运行的Linux内核的方式;讨论了在主机上建立相关服务器,通过NFS服务从主机上下载内核,在无盘开发板上启动Linux的方法。
    关键词:MIPS NFS 启动Linux
    一、引言
    大多数基于MIPS处理器的平台上都没有提供软、硬盘接口,一般情况下,也没有在板载Flash里烧入可使用的操作系统。没有操作系统,用户的使用就会受到很多限制,应用程序的开发、调试过程就会变得相当复杂。因此,建立合适的操作系统及开发环境,可以方便用户,简化开发调试过程。本文以RedHat Linux 为例,说明在RM7000A处理器无盘开发板上建立Linux系统的方法。所用硬件设备:带有10/100Mb以太网接口和用于显示的串口的开发板、装有Redhat Linux 9.0的主机(Host PC)。如图1:

二、编译内核
l         准备工作   
编译前,需要有MIPS处理器版的Linux源文件包,简单的方法是从MIPS公司或者SGI公司等的FTP服务器上下载(ftp://ftp.mips.com/pub/linux  ftp://oss.sgi.com  ftp://ftp.linux-mips.org/linux/mips )。另外,由于所用主机是x86体系,要编译在MIPS处理器上运行的内核,需要有能在x86处理器上工作的交叉编译工具;为了使Linux启动后,可以在开发板上直接开发应用程序,还需要MIPS版的NFS根目录结构包,它包括了Linux运行需要的目录结构、配置文件、工具等等。同样,根目录结构包和工具包也可在上述网站的服务器上下载。下载根目录结构包和交叉编译工具时,要注意选择与开发板设置的数据格式(高序Big Endian或低序Little Endian)一致的版本。
        获得源文件包、根目录结构包文件后,将其解压至指定目录,如分别指定为:/tftpboot/tftpboot/mips,包内文件和子目录就全部展开在相应目录中;
对于rpm格式的工具包文件,可在终端窗口用命令行方式展开: 
rpm -ihv toolchain-mips-*.rpm
交叉编译工具自动安装到/usr/tools的目录下。进入/tftpboot/linux目录,打开Makefile文件,更改以下语句为:
        ARCH=mips
        CROSS_COMPILE=/usr/tools/bin/mips-linux-
l         配置工作
        接下来,是对编译能否顺利通过至关重要的配置过程。所谓配置,就是根据开发板具体情况,只选择必需选项,使重编译的内核既提供所需要的功能,又占据最小存储空间。由于Linux系统各功能模块之间存在不同程度的依赖关系,配置过程中有可能会破坏这些关联;因此配置完成后不一定能通过关联检测,可能需要反复地进行配置取舍。
        配置过程需要注意:
1  数据格式与开发板的设置和交叉编译工具一致,才能生成正确的内核;
2  设置正确的处理器,以及相近的开发板类型;
3  需提供对标准串口的支持和通过串口显示的功能;
4  由于编译内核的主机不是MIPS处理器,所以一定要设置CROSSCOMPILE项;
5  因为要通过网络下载内核来启动,对PCI设备、网络协议、网络设备的支持必不可少,此外,在网络文件系统中还要选择NFS_FSROOT_NFS选项;
以上为配置基本选项,其余功能可根据开发板具体情况取舍。配置步骤如下:  
cd /tftpboot/linux  -- 进入目录/tftpboot/linux
make config        -- 进行行命令配置
make dep           -- 关联检测
l         编译内核
如果无出错提示,表明配置正确,可以编译内核:
          make boot
编译完成后在/tftpboot/linux/tftpboot/linux/arch/mips/boot中都会出现不同格式的内核映像文件vmlinux、及System.map文件。
三、设置主机服务
1  设置TFTP服务器
RedHat Linux安装CD3中找到tftp文件包:tftp-0.32-4.i386.rpm,在终端输入:  rpm -ihv tftp-0.32-4.i386.rpm,系统自动安装好tftp服务器;
cd /etc/xinetd.d    -- 进入目录/etc/xinetd.d
vi tftp             --只有在安装了tftp服务后,才会出现tftp文件
修改其中disableserver_args项,其余可保持不变。以下为一个完整的tftp文件:
Service tftp
{
socket_type  = dgram
protocol     = udp
wait         = yes
user         = root
server       =/usr/sbin/in.tftpd
server_args  = -s /tftpboot/linux   -- 根据需要设置tftp服务默认目录
disable      = no                   -- 默认为yes,应修改成no
per_source   = 11
cps          = 100 2
flags        = IPv4
        }      
2  设置DHCP服务器
RedHat Linux安装CD2中找到dhcp文件包:dhcp-3.0pl1-23.i386.rpm,在终端输入:  rpm -ihv dhcp-3.0pl1-23.i386.rpm,系统自动安装好dhcp服务器;
 cd /etc                -- 进入目录/etc
vi dhcpd.conf          -- 建立文件dhcpd.conf
dhcpd.conf文件中写入内容如下:
ddns-update-style interim;
ignore client-updates;
subnet 192.168.0.0 netmask 255.255.255.0 {
      option routers         192.168.0.10;
      option subnet-mask     255.255.255.0;
      option domain-name     "LocalHost";
option domain-name-server  192.168.0.10;
option time-offset       18000;
range dynamic-bootp    192.168.0.1 192.168.0.15;
default-lease-time     21600;
max-lease-time         43200;
}
其中,192.168.0.10为主机ip地址;192.168.0.1192.168.0.15dhcp服务可动态分配的ip有效地址范围;
3  设置NFS服务器
cd /etc        -- 进入目录/etc
vi exports     -- 打开exports文件
    添加语句:/tftpboot/mips  *(rw,sync,no_root_squash)
此语句设置主机上的目录/tftpboot/mips为客户机通过NFS服务可访问的根目录,并且任何客户机的访问都被视为是主机root身份,对/tftpboot/mips目录具有读写功能。这样设置是为了方便以后在开发板上的开发与调试工作。
4  启动服务
RedHat Linux9.0程序启动栏里,系统设置—〉服务器设置—〉服务,选上dhcpdtftpxinetdnfsportmap服务选项,点击开始或者重新开始来启动dhcptftpnfs服务。其中tftp服务要通过启动xinetd才能工作。
四、通过NFS服务在开发板上启动Linux系统
1.设置minicom
minicom是一个RedHat Linux9.0自带的通信终端程序,通过minicom可以设置、监视串口工作状态,接收、显示串口收到的信息,并且在主机和开发板之间传递数据和控制指令,从而实现通过主机上调试开发板的目的。以下的操作都是通过主机键盘在minicom窗口里进行的。
在主机上打开一个终端窗口,启动minicom,设置主机串口:波特率9600、数据位8位、停止位1位、奇偶校验位无、数据流控制无;保存设置后,重新启动minicom。这时minicom处于工作状态;
2  开发板加电
开发板上有一个512KB ROM,烧有bootloarder - pmon2000,加电后自动完成处理器、缓存、内存控制器、网络控制器等初始化工作;同时pmon2000拥有少量行命令集,用于启动后对内存的读写校验、板载Flash的擦写、ip设置、串口设置以及跟主机之间进行数据通信,包括上传、下传文件,接收主机下达指令等等。
开发板加电后,minicom窗口显示初始化信息;启动完成,显示pmon>
3.启动Linux
加载内核:pmon>boot 192.168.0.10:vmlinux
pmon2000内部命令boot通过网络,将主机上位于NFS根目录/tftpboot/mips下可执行的vmlinux映像文件,加载到内存,显示入口地址(Entry point);
启动Linuxpmon>g ./root=/dev/nfs nfsroot=/tftpboot/mips ip=dhcp
g为执行载入文件的命令;其后的参数,是传递给内核的有关根目录的信息,通过NFS访问主机的客户机(开发板),其ip由主机dhcp服务动态分配。pmon2000将内核拷贝到内存空间,随后将控制权交给内核,由其完成系统的启动。在启动完成登录行出现时,输入:root,即可进入运行于开发板上的Linux系统。由于操作系统是在内存里运行的,断电后,要重复步骤23,重新载入内核后才能再次启动Linux
4.应用举例
前面设置了客户机具有对根目录/tftpboot/mips的完全控制功能,因此可以在客户机上(minicom窗口中)直接编写、编译、调试程序。这时所使用的编译调试工具是安装在主机/tftpboot/mips/usr/bin目录里,根目录结构包自带的MIPSGNU gccg++gdbld等等;程序编译完成可直接运行,不需要在主机用交叉编译工具编译后,再切换到客户机下载运行,这样提高了开发效率。
在开发板上(minicom窗口),
       cd /home
       mkdir test   -- /home目录里建立一个test子目录,实际上是建在主机的/tftpboot/mips/home
       cd test
       vi hello.c              -- vi编写一个名为helloc语言程序
       gcc -o hello hello.c   -- 编译产生可执行文件hello.o
       ./hello                 -- 执行该文件
至此,可以看见程序运行结果。
五、结束语
        文中所编译出来的内核文件vmlinux占空间小(1.9MB),功能有限,需要与主机配合才能发挥作用。在实际的应用中,有可能要加入更多的功能、服务,比如:http服务、ftp服务、mail服务、对GUI的支持等等,内核的占据空间必然增大,因此,设计时在功能和资源之间就要有所取舍。另外,系统实际应用中不一定带有网络,可能是一个独立的系统,此时内核必须放在系统自身的存储单元里(DOCIDE硬盘、CF卡等)。因此,有必要进一步研究能够直接从存储单元启动操作系统的技术。
 
 

- 作者: dudumami 2005年03月11日, 星期五 10:31  回复(0) |  引用(0) 加入博采

Linux汇编语言开发指南(转)

一、简介

作为最基本的编程语言之一,汇编语言虽然应用的范围不算很广,但重要性却勿庸置疑,因为它能够完成许多其它语言所无法完成的功能。就拿 Linux 内核来讲,虽然绝大部分代码是用 C 语言编写的,但仍然不可避免地在某些关键地方使用了汇编代码,其中主要是在 Linux 的启动部分。由于这部分代码与硬件的关系非常密切,即使是 C 语言也会有些力不从心,而汇编语言则能够很好扬长避短,最大限度地发挥硬件的性能。

大多数情况下 Linux 程序员不需要使用汇编语言,因为即便是硬件驱动这样的底层程序在 Linux 操作系统中也可以用完全用 C 语言来实现,再加上 GCC 这一优秀的编译器目前已经能够对最终生成的代码进行很好的优化,的确有足够的理由让我们可以暂时将汇编语言抛在一边了。但实现情况是 Linux 程序员有时还是需要使用汇编,或者不得不使用汇编,理由很简单:精简、高效和 libc 无关性。假设要移植 Linux 到某一特定的嵌入式硬件环境下,首先必然面临如何减少系统大小、提高执行效率等问题,此时或许只有汇编语言能帮上忙了。

汇编语言直接同计算机的底层软件甚至硬件进行交互,它具有如下一些优点:


能够直接访问与硬件相关的存储器或 I/O 端口;
能够不受编译器的限制,对生成的二进制代码进行完全的控制;
能够对关键代码进行更准确的控制,避免因线程共同访问或者硬件设备共享引起的死锁;
能够根据特定的应用对代码做最佳的优化,提高运行速度;
能够最大限度地发挥硬件的功能。

同时还应该认识到,汇编语言是一种层次非常低的语言,它仅仅高于直接手工编写二进制的机器指令码,因此不可避免地存在一些缺点:


编写的代码非常难懂,不好维护;
很容易产生 bug,难于调试;
只能针对特定的体系结构和处理器进行优化;
开发效率很低,时间长且单调。

Linux 下用汇编语言编写的代码具有两种不同的形式。第一种是完全的汇编代码,指的是整个程序全部用汇编语言编写。尽管是完全的汇编代码,Linux 平台下的汇编工具也吸收了 C 语言的长处,使得程序员可以使用 #include、#ifdef 等预处理指令,并能够通过宏定义来简化代码。第二种是内嵌的汇编代码,指的是可以嵌入到C语言程序中的汇编代码片段。虽然 ANSI 的 C 语言标准中没有关于内嵌汇编代码的相应规定,但各种实际使用的 C 编译器都做了这方面的扩充,这其中当然就包括 Linux 平台下的 GCC。

二、Linux 汇编语法格式

绝大多数 Linux 程序员以前只接触过DOS/Windows 下的汇编语言,这些汇编代码都是 Intel 风格的。但在 Unix 和 Linux 系统中,更多采用的还是 AT&T 格式,两者在语法格式上有着很大的不同:


在 AT&T 汇编格式中,寄存器名要加上 '%' 作为前缀;而在 Intel 汇编格式中,寄存器名不需要加前缀。例如:

AT&T 格式  pushl %eax
Intel 格式 push eax


在 AT&T 汇编格式中,用 '$' 前缀表示一个立即操作数;而在 Intel 汇编格式中,立即数的表示不用带任何前缀。例如:

AT&T 格式 pushl $1
Intel 格式 push 1


AT&T 和 Intel 格式中的源操作数和目标操作数的位置正好相反。在 Intel 汇编格式中,目标操作数在源操作数的左边;而在 AT&T 汇编格式中,目标操作数在源操作数的右边。例如:

AT&T 格式 addl $1, %eax
Intel 格式 add eax, 1


在 AT&T 汇编格式中,操作数的字长由操作符的最后一个字母决定,后缀'b'、'w'、'l'分别表示操作数为字节(byte,8 比特)、字(word,16 比特)和长字(long,32比特);而在 Intel 汇编格式中,操作数的字长是用 "byte ptr" 和 "word ptr" 等前缀来表示的。例如:

AT&T 格式
movb val, %al
Intel 格式 mov al, byte ptr val

在 AT&T 汇编格式中,绝对转移和调用指令(jump/call)的操作数前要加上'*'作为前缀,而在 Intel 格式中则不需要。
远程转移指令和远程子调用指令的操作码,在 AT&T 汇编格式中为 "ljump" 和 "lcall",而在 Intel 汇编格式中则为 "jmp far" 和 "call far",即:

AT&T 格式 ljump $section, $offset     lcall $section, $offset
Intel 格式 jmp far section:offset     call far section:offset


与之相应的远程返回指令则为:

AT&T 格式 lret $stack_adjust
Intel 格式 ret far stack_adjust


在 AT&T 汇编格式中,内存操作数的寻址方式是


section:disp(base, index, scale)



而在 Intel 汇编格式中,内存操作数的寻址方式为:


section:[base + index*scale + disp]



由于 Linux 工作在保护模式下,用的是 32 位线性地址,所以在计算地址时不用考虑段基址和偏移量,而是采用如下的地址计算方法:


disp + base + index * scale



下面是一些内存操作数的例子:

AT&T 格式                         Intel 格式
movl -4(%ebp), %eax               mov eax, [ebp - 4]
movl array(, %eax, 4), %eax       mov eax, [eax*4 + array]
movw array(%ebx, %eax, 4), %cx    mov cx, [ebx + 4*eax + array]
movb $4, %fs:(%eax)               mov fs:eax, 4



三、Hello World!

真不知道打破这个传统会带来什么样的后果,但既然所有程序设计语言的第一个例子都是在屏幕上打印一个字符串 "Hello World!",那我们也以这种方式来开始介绍 Linux 下的汇编语言程序设计。

在 Linux 操作系统中,你有很多办法可以实现在屏幕上显示一个字符串,但最简洁的方式是使用 Linux 内核提供的系统调用。使用这种方法最大的好处是可以直接和操作系统的内核进行通讯,不需要链接诸如 libc 这样的函数库,也不需要使用 ELF 解释器,因而代码尺寸小且执行速度快。

Linux 是一个运行在保护模式下的 32 位操作系统,采用 flat memory 模式,目前最常用到的是 ELF 格式的二进制代码。一个 ELF 格式的可执行程序通常划分为如下几个部分:.text、.data 和 .bss,其中 .text 是只读的代码区,.data 是可读可写的数据区,而 .bss 则是可读可写且没有初始化的数据区。代码区和数据区在 ELF 中统称为 section,根据实际需要你可以使用其它标准的 section,也可以添加自定义 section,但一个 ELF 可执行程序至少应该有一个 .text 部分。下面给出我们的第一个汇编程序,用的是 AT&T 汇编语言格式:

例1. AT&T 格式


#hello.s
.data                    # 数据段声明
       msg : .string "Hello, world!\\n" # 要输出的字符串
       len = . - msg                   # 字串长度

.text                    # 代码段声明
.global _start           # 指定入口函数
       
_start:                  # 在屏幕上显示一个字符串
       movl $len, %edx  # 参数三:字符串长度
       movl $msg, %ecx  # 参数二:要显示的字符串
       movl $1, %ebx    # 参数一:文件描述符(stdout)
       movl $4, %eax    # 系统调用号(sys_write)
       int  $0x80       # 调用内核功能
       
                        # 退出程序
       movl $0,%ebx     # 参数一:退出代码
       movl $1,%eax     # 系统调用号(sys_exit)
       int  $0x80       # 调用内核功能



初次接触到 AT&T 格式的汇编代码时,很多程序员都认为太晦涩难懂了,没有关系,在 Linux 平台上你同样可以使用 Intel 格式来编写汇编程序:

例2. Intel 格式



; hello.asm
section .data            ; 数据段声明
       msg db "Hello, world!", 0xA     ; 要输出的字符串
       len equ $ - msg                 ; 字串长度

section .text            ; 代码段声明
global _start            ; 指定入口函数

_start:                  ; 在屏幕上显示一个字符串
       mov edx, len     ; 参数三:字符串长度
       mov ecx, msg     ; 参数二:要显示的字符串
       mov ebx, 1       ; 参数一:文件描述符(stdout)
       mov eax, 4       ; 系统调用号(sys_write)
       int 0x80         ; 调用内核功能

                        ; 退出程序
       mov ebx, 0       ; 参数一:退出代码
       mov eax, 1       ; 系统调用号(sys_exit)
       int 0x80         ; 调用内核功能



上面两个汇编程序采用的语法虽然完全不同,但功能却都是调用 Linux 内核提供的 sys_write 来显示一个字符串,然后再调用 sys_exit 退出程序。在 Linux 内核源文件 include/asm-i386/unistd.h 中,可以找到所有系统调用的定义。

四、Linux 汇编工具

Linux 平台下的汇编工具虽然种类很多,但同 DOS/Windows 一样,最基本的仍然是汇编器、连接器和调试器。

1.汇编器

汇编器(assembler)的作用是将用汇编语言编写的源程序转换成二进制形式的目标代码。Linux 平台的标准汇编器是 GAS,它是 GCC 所依赖的后台汇编工具,通常包含在 binutils 软件包中。GAS 使用标准的 AT&T 汇编语法,可以用来汇编用 AT&T 格式编写的程序:


[xiaowp@gary code]$ as -o hello.o hello.s



Linux 平台上另一个经常用到的汇编器是 NASM,它提供了很好的宏指令功能,并能够支持相当多的目标代码格式,包括 bin、a.out、coff、elf、rdf 等。NASM 采用的是人工编写的语法分析器,因而执行速度要比 GAS 快很多,更重要的是它使用的是 Intel 汇编语法,可以用来编译用 Intel 语法格式编写的汇编程序:


[xiaowp@gary code]$ nasm -f elf hello.asm



2.链接器

由汇编器产生的目标代码是不能直接在计算机上运行的,它必须经过链接器的处理才能生成可执行代码。链接器通常用来将多个目标代码连接成一个可执行代码,这样可以先将整个程序分成几个模块来单独开发,然后才将它们组合(链接)成一个应用程序。 Linux 使用 ld 作为标准的链接程序,它同样也包含在 binutils 软件包中。汇编程序在成功通过 GAS 或 NASM 的编译并生成目标代码后,就可以使用 ld 将其链接成可执行程序了:


[xiaowp@gary code]$ ld -s -o hello hello.o



3.调试器

有人说程序不是编出来而是调出来的,足见调试在软件开发中的重要作用,在用汇编语言编写程序时尤其如此。Linux 下调试汇编代码既可以用 GDB、DDD 这类通用的调试器,也可以使用专门用来调试汇编代码的 ALD(Assembly Language Debugger)。

从调试的角度来看,使用 GAS 的好处是可以在生成的目标代码中包含符号表(symbol table),这样就可以使用 GDB 和 DDD 来进行源码级的调试了。要在生成的可执行程序中包含符号表,可以采用下面的方式进行编译和链接:


[xiaowp@gary code]$ as --gstabs -o hello.o hello.s
[xiaowp@gary code]$ ld -o hello hello.o



执行 as 命令时带上参数 --gstabs 可以告诉汇编器在生成的目标代码中加上符号表,同时需要注意的是,在用 ld 命令进行链接时不要加上 -s 参数,否则目标代码中的符号表在链接时将被删去。

在 GDB 和 DDD 中调试汇编代码和调试 C 语言代码是一样的,你可以通过设置断点来中断程序的运行,查看变量和寄存器的当前值,并可以对代码进行单步跟踪。


图1 用 DDD 中调试汇编程序

汇编程序员通常面对的都是一些比较苛刻的软硬件环境,短小精悍的ALD可能更能符合实际的需要,因此下面主要介绍一下如何用ALD来调试汇编程序。首先在命令行方式下执行ald命令来启动调试器,该命令的参数是将要被调试的可执行程序:


[xiaowp@gary doc]$ ald hello
Assembly Language Debugger 0.1.3
Copyright (C) 2000-2002 Patrick Alken

hello: ELF Intel 80386 (32 bit), LSB, Executable, Version 1 (current)
Loading debugging symbols...(15 symbols loaded)
ald>



当 ALD 的提示符出现之后,用 disassemble 命令对代码段进行反汇编:


ald> disassemble -s .text
Disassembling section .text (0x08048074 - 0x08048096)
08048074  BA0F000000                 mov edx, 0xf
08048079  B998900408                 mov ecx, 0x8049098
0804807E  BB01000000                 mov ebx, 0x1
08048083  B804000000                 mov eax, 0x4
08048088  CD80                       int 0x80
0804808A  BB00000000                 mov ebx, 0x0
0804808F  B801000000                 mov eax, 0x1
08048094  CD80                       int 0x80



上述输出信息的第一列是指令对应的地址码,利用它可以设置在程序执行时的断点:


ald> break 0x08048088
Breakpoint 1 set for 0x08048088



断点设置好后,使用 run 命令开始执行程序。ALD 在遇到断点时将自动暂停程序的运行,同时会显示所有寄存器的当前值:


ald> run
Starting program: hello
Breakpoint 1 encountered at 0x08048088
eax = 0x00000004 ebx = 0x00000001 ecx = 0x08049098 edx = 0x0000000F
esp = 0xBFFFF6C0 ebp = 0x00000000 esi = 0x00000000 edi = 0x00000000
ds  = 0x0000002B es  = 0x0000002B fs  = 0x00000000 gs  = 0x00000000
ss  = 0x0000002B cs  = 0x00000023 eip = 0x08048088 eflags = 0x00000246

Flags: PF ZF IF


08048088  CD80                       int 0x80



如果需要对汇编代码进行单步调试,可以使用 next 命令:


ald> next
Hello, world!
eax = 0x0000000F ebx = 0x00000000 ecx = 0x08049098 edx = 0x0000000F
esp = 0xBFFFF6C0 ebp = 0x00000000 esi = 0x00000000 edi = 0x00000000
ds  = 0x0000002B es  = 0x0000002B fs  = 0x00000000 gs  = 0x00000000
ss  = 0x0000002B cs  = 0x00000023 eip = 0x0804808F eflags = 0x00000346

Flags: PF ZF TF IF


0804808F  B801000000                 mov eax, 0x1



若想获得 ALD 支持的所有调试命令的详细列表,可以使用 help 命令:


ald> help
Commands may be abbreviated.
If a blank command is entered, the last command is repeated.
Type `help <command>' for more specific information on <command>.

General commands
attach         clear          continue       detach         disassemble
enter          examine        file           help           load
next           quit           register       run            set
step           unload         window         write

Breakpoint related commands
break          delete         disable        enable         ignore
lbreak         tbreak



五、系统调用

即便是最简单的汇编程序,也难免要用到诸如输入、输出以及退出等操作,而要进行这些操作则需要调用操作系统所提供的服务,也就是系统调用。除非你的程序只完成加减乘除等数学运算,否则将很难避免使用系统调用,事实上除了系统调用不同之外,各种操作系统的汇编编程往往都是很类似的。

在 Linux 平台下有两种方式来使用系统调用:利用封装后的 C 库(libc)或者通过汇编直接调用。其中通过汇编语言来直接调用系统调用,是最高效地使用 Linux 内核服务的方法,因为最终生成的程序不需要与任何库进行链接,而是直接和内核通信。

和 DOS 一样,Linux 下的系统调用也是通过中断(int 0x80)来实现的。在执行 int 80 指令时,寄存器 eax 中存放的是系统调用的功能号,而传给系统调用的参数则必须按顺序放到寄存器 ebx,ecx,edx,esi,edi 中,当系统调用完成之后,返回值可以在寄存器 eax 中获得。

所有的系统调用功能号都可以在文件 /usr/include/bits/syscall.h 中找到,为了便于使用,它们是用 SYS_<name> 这样的宏来定义的,如 SYS_write、SYS_exit 等。例如,经常用到的 write 函数是如下定义的:


ssize_t write(int fd, const void *buf, size_t count);



该函数的功能最终是通过 SYS_write 这一系统调用来实现的。根据上面的约定,参数 fb、buf 和 count 分别存在寄存器 ebx、ecx 和 edx 中,而系统调用号 SYS_write 则放在寄存器 eax 中,当 int 0x80 指令执行完毕后,返回值可以从寄存器 eax 中获得。

或许你已经发现,在进行系统调用时至多只有 5 个寄存器能够用来保存参数,难道所有系统调用的参数个数都不超过 5 吗?当然不是,例如 mmap 函数就有 6 个参数,这些参数最后都需要传递给系统调用 SYS_mmap:


void  *  mmap(void *start, size_t length, int prot , int flags, int fd, off_t offset);




当一个系统调用所需的参数个数大于 5 时,执行int 0x80 指令时仍需将系统调用功能号保存在寄存器 eax 中,所不同的只是全部参数应该依次放在一块连续的内存区域里,同时在寄存器 ebx 中保存指向该内存区域的指针。系统调用完成之后,返回值仍将保存在寄存器 eax 中。

由于只是需要一块连续的内存区域来保存系统调用的参数,因此完全可以像普通的函数调用一样使用栈(stack)来传递系统调用所需的参数。但要注意一点,Linux 采用的是 C 语言的调用模式,这就意味着所有参数必须以相反的顺序进栈,即最后一个参数先入栈,而第一个参数则最后入栈。如果采用栈来传递系统调用所需的参数,在执行int 0x80 指令时还应该将栈指针的当前值复制到寄存器 ebx中。

六、命令行参数

在 Linux 操作系统中,当一个可执行程序通过命令行启动时,其所需的参数将被保存到栈中:首先是 argc,然后是指向各个命令行参数的指针数组 argv,最后是指向环境变量的指针数据 envp。在编写汇编语言程序时,很多时候需要对这些参数进行处理,下面的代码示范了如何在汇编代码中进行命令行参数的处理:

例3. 处理命令行参数


# args.s
.text
.globl _start
       
_start:
       popl %ecx # argc

vnext:
       popl %ecx # argv
       test  %ecx, %ecx      # 空指针表明结束
       jz exit

       movl %ecx, %ebx
       xorl %edx, %edx
strlen:
       movb (%ebx), %al
       inc %edx
       inc %ebx
       test %al, %al
       jnz strlen
       movb $10, -1(%ebx)

       movl $4, %eax        # 系统调用号(sys_write)
       movl $1, %ebx        # 文件描述符(stdout)
       int $0x80

       jmp vnext

exit:
       movl $1,%eax         # 系统调用号(sys_exit)
       xorl %ebx, %ebx      # 退出代码
       int  $0x80

       ret




七、GCC 内联汇编

用汇编编写的程序虽然运行速度快,但开发速度非常慢,效率也很低。如果只是想对关键代码段进行优化,或许更好的办法是将汇编指令嵌入到 C 语言程序中,从而充分利用高级语言和汇编语言各自的特点。但一般来讲,在 C 代码中嵌入汇编语句要比"纯粹"的汇编语言代码复杂得多,因为需要解决如何分配寄存器,以及如何与C代码中的变量相结合等问题。

GCC 提供了很好的内联汇编支持,最基本的格式是:


__asm__("asm statements");




例如:


__asm__("nop");



如果需要同时执行多条汇编语句,则应该用"\\n\\t"将各个语句分隔开,例如:


__asm__( "pushl %%eax \\n\\t"
        "movl $0, %%eax \\n\\t"
        "popl %eax");



通常嵌入到 C 代码中的汇编语句很难做到与其它部分没有任何关系,因此更多时候需要用到完整的内联汇编格式:


__asm__("asm statements" : outputs : inputs : registers-modified);




插入到 C 代码中的汇编语句是以":"分隔的四个部分,其中第一部分就是汇编代码本身,通常称为指令部,其格式和在汇编语言中使用的格式基本相同。指令部分是必须的,而其它部分则可以根据实际情况而省略。

在将汇编语句嵌入到C代码中时,操作数如何与C代码中的变量相结合是个很大的问题。GCC采用如下方法来解决这个问题:程序员提供具体的指令,而对寄存器的使用则只需给出"样板"和约束条件就可以了,具体如何将寄存器与变量结合起来完全由GCC和GAS来负责。

在GCC内联汇编语句的指令部中,加上前缀'%'的数字(如%0,%1)表示的就是需要使用寄存器的"样板"操作数。指令部中使用了几个样板操作数,就表明有几个变量需要与寄存器相结合,这样GCC和GAS在编译和汇编时会根据后面给定的约束条件进行恰当的处理。由于样板操作数也使用'%'作为前缀,因此在涉及到具体的寄存器时,寄存器名前面应该加上两个'%',以免产生混淆。

紧跟在指令部后面的是输出部,是规定输出变量如何与样板操作数进行结合的条件,每个条件称为一个"约束",必要时可以包含多个约束,相互之间用逗号分隔开就可以了。每个输出约束都以'='号开始,然后紧跟一个对操作数类型进行说明的字后,最后是如何与变量相结合的约束。凡是与输出部中说明的操作数相结合的寄存器或操作数本身,在执行完嵌入的汇编代码后均不保留执行之前的内容,这是GCC在调度寄存器时所使用的依据。

输出部后面是输入部,输入约束的格式和输出约束相似,但不带'='号。如果一个输入约束要求使用寄存器,则GCC在预处理时就会为之分配一个寄存器,并插入必要的指令将操作数装入该寄存器。与输入部中说明的操作数结合的寄存器或操作数本身,在执行完嵌入的汇编代码后也不保留执行之前的内容。

有时在进行某些操作时,除了要用到进行数据输入和输出的寄存器外,还要使用多个寄存器来保存中间计算结果,这样就难免会破坏原有寄存器的内容。在GCC内联汇编格式中的最后一个部分中,可以对将产生副作用的寄存器进行说明,以便GCC能够采用相应的措施。

下面是一个内联汇编的简单例子:

例4.内联汇编


/* inline.c */

int main()
{
   int a = 10, b = 0;

   __asm__ __volatile__("movl %1, %%eax;\\n\\r"
                        "movl %%eax, %0;"
                        :"=r"(b)      /* 输出 */    
                        :"r"(a)       /* 输入 */
                        :"%eax");     /* 不受影响的寄存器 */
   
   printf("Result: %d, %d\\n", a, b);
}



上面的程序完成将变量a的值赋予变量b,有几点需要说明:

变量b是输出操作数,通过%0来引用,而变量a是输入操作数,通过%1来引用。
输入操作数和输出操作数都使用r进行约束,表示将变量a和变量b存储在寄存器中。输入约束和输出约束的不同点在于输出约束多一个约束修饰符'='。
在内联汇编语句中使用寄存器eax时,寄存器名前应该加两个'%',即%%eax。内联汇编中使用%0、%1等来标识变量,任何只带一个'%'的标识符都看成是操作数,而不是寄存器。
内联汇编语句的最后一个部分告诉GCC它将改变寄存器eax中的值,GCC在处理时不应使用该寄存器来存储任何其它的值。
由于变量b被指定成输出操作数,当内联汇编语句执行完毕后,它所保存的值将被更新。
在内联汇编中用到的操作数从输出部的第一个约束开始编号,序号从0开始,每个约束记数一次,指令部要引用这些操作数时,只需在序号前加上'%'作为前缀就可以了。需要注意的是,内联汇编语句的指令部在引用一个操作数时总是将其作为32位的长字使用,但实际情况可能需要的是字或字节,因此应该在约束中指明正确的限定符:

限定符                     意义
"m"、"v"、"o"              内存单元
"r"                        任何寄存器
"q"                        寄存器eax、ebx、ecx、edx之一
"i"、"h"                   直接操作数
"E"和"F"                   浮点数
"g"                        任意
"a"、"b"、"c"、"d"         分别表示寄存器eax、ebx、ecx和edx
"S"和"D"                   寄存器esi、edi
"I"                        常数(0至31)


八、小结

Linux操作系统是用C语言编写的,汇编只在必要的时候才被人们想到,但它却是减少代码尺寸和优化代码性能的一种非常重要的手段,特别是在与硬件直接交互的时候,汇编可以说是最佳的选择。Linux提供了非常优秀的工具来支持汇编程序的开发,使用GCC的内联汇编能够充分地发挥C语言和汇编语言各自的优点。

九、参考资料


在网站http://linuxassembly.org上可以找到大量的Linux汇编资源。
软件包binutils提供了as和ld等实用工具,其相关信息可以在网站http://sources.redhat.com/binutils/上找到。
NASM是Intel格式的汇编器,其相关信息可以在网站http://nasm.sourceforge.net上找到。
ALD是一个短小精悍的汇编调试器,其相关信息可以在网站http://dunx1.irt.drexel.edu/~psa22/ald.html上找到。
intel2gas是一个能够将Intel汇编格式转换成AT&T汇编格式的小工具,其相关信息可以在网站http://www.niksula.cs.hut.fi/~mtiihone/intel2gas/上找到。
IBM developerWorks上有一篇介绍GCC内联汇编的文章(http://www-900.ibm.com/developerworks/cn/linux/sdk/assemble/inline/index_eng.shtml)。


- 作者: dudumami 2005年03月10日, 星期四 16:50  回复(0) |  引用(0) 加入博采

学习linux心得(转)
学习Linux的心得

通过这几年来对Linux的学习,一步一步摸着石头过河,到现在才刚刚上岸。我能起的作用可能可以告诉你一些学习的方法,当你遇到困难的时候有个人在旁边给予帮助。说句心里话,对于Linux的掌握我还只能算是很粗的东西,因为看了很多大师们的东西以后,发现自已离他们真得还很远。我不是要打击你的意思,是告诉你实情。但也可以告诉你,就这一点现在还是比较好混口饭吃。对她了解掌握的越深,你会发现他几乎什么都能做,有了实现自已商业机会的平台。

学习Linux分好几种层次,我个人觉得可以用中学里学的课文《疱丁解牛》来对比。说白了也就是对事物的认识一般都是由表及里的过程,学习Linux也是一样的。以前总觉的学理的人只要会数理化就行了,很多老师们由于自身的素质问题也不由自主的暗示着我们这样做。现在觉得很后悔当初没有学好文,文太重要了,很多时候他真的可以指导我们的形为和思维方式。最近看的很多管理上的书,最终上升到都是哲学方面的问题,很多东西一追踪根源到上升到哲学上来了。以前小的时候不懂事,觉得学这些东西有什么用,现在成熟一些了才发现这些东西真的不是件简单的事情。学习Linux有很多东西,我建议你首先要注意大师们在哲学上的思路。Linux是由Unix产生的,最终他的实现及他基础上很多应用软件都是基于一个哲学理念"把任务分解成一小块一小块完成",可能现在你觉得这是件很简单的道理。但他不简单就在于所有有关Unix的东西都是基于这一理念的,看看我们常用的那些小工具吧,find,grep,gzip,tar,bash...等等,这个道理无不贯穿始终。现在IBM/MS等公司经常提到的WEB Service,实际上细想也是这样的,无非是把很多服务分散开来,然后有个总的业务逻辑来进行迅速的组合成一个新的服务,满足市场的迅速变化的需求。


结论1:首先要明白他的设计理念是缘自Unix的"把任务分解成一小块一小块完成",建议你可以看这本电子书<<The Art of Unix Programming>>。是由Eric Steven Raymond写的,在google上很容易找到。这本书编重于编程,可以根据自已的兴趣选相应章节。我也没有全看完,也在研习中:-)


从使用着手,自已装个系统。建议你从Redhat开始。现在Redhat的安装基本上都是学习MS的做法,很图形化了。只要按照图形的指示,很容易让一个系统跑起来。我觉得初学者应该从这个入手,这样会快很多。因为Linux上的软件包都是由不同的开发团队做的,所以一般人不知道如果安装,redhat公司做的事情就是把这些东西组织起来,然后把这些软件卖给大家。因为他对这一行非常的精通,所以卖软件是其次,卖服务是主业。现在redhat已停止出个人版的了,改成专做企业的了,版本发行最近有些变动。现在最后一个版本好象是RH9.1。

装好系统后从使用应用软件开始,建议你先学BASH SHELL编程。因为你将业想往Oracl DBA发展的话,精通SHELL可以在管理上给你带来很大的便利。同时对于理解Linux系统的启动和一些软件的安装很有帮助。比如说你可以很清楚的了解Linux的整个启动过程,继而知道为什么要做那一步做了这个动作,对你将来处理实际碰到的问题至少可以明白知道问题出在哪。象很多我们日常在Unix/Linux的管理和脚本都是分不开的,精通BASH是我们需要完成的第一步。我指的是精通,不是一般的了解,因为你只有精通了,用的时间越长,你会发现script可以帮我们很方便的干很多事情。这里有一本电子书提供参考,是我至今见到写BASH最全,最好的一本。名叫《Advanced Bash Scripting Guide》,你可以在www.linuxdoc.org里找到。

这里也提一下www.linuxdoc.org里的文章是你学习Linux的良师益友,里面的很多文章都是非常不错的教材。主要分为几类:1、FAQ 2、HOWTO 3、Guider 4、man手册。其中的Guider有几本都是非常不错的入门书,你基本上可以不用去买本,美国出的印刷书都奇贵,你把那里的几本书看完就差不多了。插一句,现在P2P很流行,我基本上都不买专业的书了,大部分都可以找到电子版的下载。里面的HOWTO主要讲的一些具体的问题如何处理,门类也很多很杂,你可以根据自已现在的需求去看相应的文章。有空的时候可以多看看,这里面有些很好的solution,说不定什么时候就可以用上。FAQ的文章不多,建议挑些重要的看下。对于man手册,一般系统上都有,不过那里应该是最近的,平时用的不多。

结论2:自已装个Redhat,学好shell。

你用Unix/Linux时候,熟悉一个编辑器是必不可少的。用两种建议选择Emacs&VIM。Emacs比较大些,要花些空间,功能具强,以前用过一段时间但没有深入下去,所以了解也是通过大师们文章中反复提到这个关键字和一些信息知道的,用好了功能具强,相当于一个MS下的IDE开发环境;vim比较小些,功能也很强,是vi的增强版,我现在主要是用她来编辑东西。vim支持多种语法,可以用来编脚本、C、Java,功能足够我们用,学好需要一定的时间实践。因为我们用Unix/Linux,免不了有时要用console来控制,如果用惯了MS的编辑的话,你会非常痛苦,建议如果以后想长时间使用unix/linux环境的话,学好一种这环境下的编辑器。vim也有windows的版本,开始用起来会觉得不方便,便很快你会发现用VIM可以解决很多MS下编辑器不能解决的问题,不信你可以试试看。icon_smile.gif

结论3:学会一种Unix/Linux下编辑器Emacs或Vim,建议Vim(因为我在用,可以给你些指导icon_smile.gif


学习Linux有几种常用的服务软件需要会用,1) apache(http) 2) bind(dns) 3) pro-ftp(ftp) 4)samba(file share) 。需要掌握这些软件的安装和简单配置,这些软件在网上都有很详细的文档。至少apache要会安装和管理,因为这是最基本的WEB服务,今天在哪里都能用到。对于这些常用服务软件根据自已兴趣和环境要求做出选择,不一定要很精,但要会,建议精通。

结论4:学会常用服务软件的安装和配置,至少掌握apache


在Linux中还有很多小实用工具也需要掌握,这对你今后诊断问题和做相应的系统管理有很大帮助。如配置网卡地址的工具ifconfig,查找文件的工具find,行编辑的命令sed、awk,网络状态检查netstat、ping、nc等。这些工具可以在实践中慢慢学习。

结论5:在实践中学习一些常用小工具,如ifconfig netstat route等


掌握一到两种Opensource的数据库,如现在流行的两种数据库MySQL和PostgreSQL。MySQL用的人多些也比较简单,速度又快,做一些简单的数据库应用很多都用他做backend databaes server。如果你自已想学Oracle的话,可以直接学Oracle。Oracle的东西多且全,基本上所用的功能都能在Oracle找到。但如果你想更深一层学习DB的话,你可以考虑学PostgreSQL,他有源程序而且支持事务处理(trans-action)、子查询(sub-select),过程(procedure)、自定义函数(function)、光标(cursor),支持了大部份SQL92和99的标准。他的文档也比较全。


结论6:建议掌握一种Opensource的DB,如MySQL


建议对TCP/IP协议层深入了解,我想你们上研究生时应该讲的很深。但我还是建议你再多复习相关的知识点,很多时候网络上的问题可以通对你对TCP/IP的了解找到问题所在。建议你买那个steven的书叫<<tcp/ip详解>>,同时经常在身边准备好那几种状态切换的图,劳记在心,出问题的时候就可以知道是哪边网络不通,还是应用程度出了什么问题了。

结论7:建议再深入了解TCP/IP protocol


建议除了bash外,再学多一种在Unix环境下的RAD脚本语言python。我也正在学python,通过这段时间的了解发现,这个东东有很多功能而且在开发上可以比c/c++提高至少5倍效率。有时候偏向做系统管理,但掌握一本这种编程形的脚本对工作帮助很大,可以迅速的处理一些问题。BASH是侧重于人机交复的脚本,有些事情用BASH是无法完成的。Python在很多Internet应用上有很多module&package,你可以很方便的使用已有的功能,比如说自已做个小的WEB SERVER,做测试工具,做数据库查询等等。


结论8:建议学下Python


好了,今天先罗嗦到这里,还有很多想法愧于文笔有限,改天再详谈。

水平有限,权做参考,希望对于你的学习有所帮助。

- 作者: dudumami 2005年03月10日, 星期四 16:46  回复(3) |  引用(0) 加入博采

转载: 嵌入式linux快速入门
以下是我初次学习嵌入式linux的笔记和体会,制作了软盘minicom应用,在此基础上也清楚了软盘Linux的实现,并利用busybox实际制作了软盘上的Linux系统。希望能给新手一些帮助和启发,同时也请高手批评指正。
yihui, eazi@163.com 2004年2月12日

一个典型的桌面Linux系统包括3个主要的软件层---linux内核、C库和应用程序代码。
内核是唯一可以完全控制硬件的层,内核驱动程序代表应用程序与硬件之间进行会话。内核之上是C库,负责把POSIX API转换为内核可以识别的形式,然后调用内核,从应用程序向内核传递参数。应用程序依靠驱动内核来完成特定的任务。
在设计嵌入式应用的时候,可以不按照这种层次,应用程序越过C库直接和内核会话,或者把应用和内核捆绑在一起,甚至可以把应用写为内核的一个线程,在内核中运行,虽然这样在移植上带来了困难,但考虑嵌入式系统对尺寸要求小的特点,是完全可行的。不过我们使用三层软件结构的模式来学习嵌入式linux将会是我们认识更清晰,简单可行并使应用具有弹性。

快速入门,最简单的建立嵌入式Linux应用的方法就是从我们使用的桌面Linux入手,安装一个喜爱的版本,把我们的某个应用作为初始化的一部分,框架就算完成了。当然,嵌入式linux应用远比我们的桌面版本功能简单专一,它也许就是一个用于足彩的终端机,或是一个数码音频播放器,这些系统除了使用嵌入式CPU外,仅仅再需要一个串口,网口等少量的输入输出接口就可以完成它们特定的应用了。在软件上,它可以按照三层的概念由内核装载器,定制的内核和较少的为特定任务设计的静态连接的应用程序组成。之所以使用静态连接的应用程序,是因为少量的静态连接程序所要的存储空间,比同样数量的动态连接的程序所占的空间小,这个平衡点需要我们在实际开发中去获取。也许你正在设计的是个PDA,它的应用程序较多,那么你很可能就要使用动态连接程序来减少存储空间。在你的/bin或者/sbin目录下,用厂列表看看bash,ifconfig,vi...,也许只用几十K,当你运行 ldd /bin/bash 时,你会看到它们都和好几个库文件相连。好了,这样看来,我们得把PC想像成一个嵌入式硬件平台,再重新制作一个特定功能的嵌入式linux。

再进行实际操作之前,先来搞清楚几个基础知识。
内核装载器Loader,它的作用是把内核从外部存储器,移动到内存中。它只作这个事情,一旦完成了调入内核的工作,Loader就跳转到内核位置开始执行。不同架构有不同的Loader,在x86结构的PC上,通常使用的loader有LILO,GRUB,syslinux,syslinux在嵌入式 linux中也同样工作。其他非x86架构的应用中,你必须使用专门的loader,或者自己编写loader来装入内核。也有不使用loader的情况,系统加电以后,内核直接从烧录有映象的Flash上开始执行。
内核,一旦内核开始执行,它将通过驱动程序初始化所有硬件,这可以从我们的pc机监视器的输出看出来,每个驱动程序都打印一些有关它的信息。初始化完成后,计算机就准备运行嵌入式应用。也许一个,也许是多个应用程序组成了嵌入式应用,但通常首先调用的是init(通过loader 向核心传入init=/program 可以定制首先运行的程序)。桌面linux中,init会读取/etc/inittab文件,来决定执行级别和哪些脚本和命令。嵌入式应用中,可以根据实际的情况决定是否使用标准的init执行方式,也许这个init是个静态程序,它能够完成我们的嵌入应用的特定任务,那完全不用考虑inittab了。
initrd文件系统,initrd以一种把内核从存储介质装入到内存的相同的机制来装入一个小型文件系统。这个文件系统最好是以压缩的方式存储在介质上的,解压缩到RAM盘上。通过使用initrd,包含有核心驱动和启动脚本的小文件系统,就可以直接从介质上和内核一起启动起来,内核届压缩这个文件系统,并执行这个文件系统上叫做/linuxrc的脚本文件,这个脚本通常会把启动过程中所需要的驱动程序装入。脚本退出以后,initrd文件系统也卸下了,启动过程进入真正初始化过程。对于嵌入式来讲,可以将需要的应用软件都运行在这个initrd文件系统上,只要/linxrc文件不结束,内核启动过程的其他部分就不会继续。
做个试验:
cp /boot/initrd-2.4.20.img /tmp
cd /tmp
mv initrd-2.4.2-.img initrd.img.gz
gunzip initrd.img.gz
mount -o loop initrd.img /mnt
cd /mnt
ls
cat linuxrc 可以看到里面执行了加载了两个模块的操作,你在启动linxu的时候会看见屏幕打印信息。

入门试验,制作一个简单的应用:我们使用一张软盘启动一台假象的只有一个串口,键盘输入,显示输出的x86架构的linux系统,执行的特定应用就是运行 minicom,通过串口拨号。需要软件: minicom-xx.src.tar.gz 和 syslinux-xx.tar.gz,xx代表版本号,开始之前,在主目录建立一个目录,来释放这两个软件包:
cd
mkdir -p project/minilinux
cd project/minilinux
tar zxvf minicom-xx.src.tar.gz
tar zxvf syslinux-xx.tar.gz

1、裁减linux内核(需要系统安装内核文件包)

配置内核的时候,我们需要选择这些:摸块编入内核,386处理器、物理内存off、支持ELF、标准PC软盘、支持RAM盘(4096)、支持 initial RAM disk (initrd)、虚你终端、虚拟终端控制台、标准串口、ext2文件系统、控制台驱动,VGA text console、DOS FAT、MSDOS文件系统,其他的都可以不要,这样内核编出来较小。
步骤:
cd /usr/src/linux
make mrproper
make xconfig
make dep && make bzImage
得到 /usr/src/linux/arch/i386/boot/目录的内核文件bzIamge。

2、编译一个静态的minicom ,把它作为将来的linuxrc
cd minicom-xx/src
vi Makefile
修改下面这行
minicom: $(minicom_OBJECTS) $(minicom_DEPENDENCIES)
rm -f minicom 下面的行加上 -static,连接为静态程序
(LINK) -static $(minicom_LDFLAGS) $(minicom_OBJECTS) $(minicom_LDADD) $(LIBS)

vi minicom.c
找到 if (real_uid==0 && dosetup==0 ) 删除这个判断条件语句,主要是用于权限判断的,因为这个嵌入应用不关注权限问题,否则会出错。
make
得到可执行程序,用ldd 检查一下是不是静态程序。

3、准备initrd压缩文件image.gz
dd if=/dev/zero of=image bs=1k count=4096
losetup /dev/loop0 image
mke2fs -m 0 /dev/loop0
mounmt -t ext2 /dev/loop0 /mnt/
mkdir -p /mnt/dev
mkdir -p /mnt/usr/share/terminfo/l/
cd /dev
cp -a consle null tty tty0 zero mem /mnt/dev
cp -P /usr/share/terminfo/l/linux /mnt/usr/share/terminfo/l/linux
cp ~/project/minilinux/mincom/src/minicom /mnt/linuxrc
umount /mnt
losetup -d /dev/loop0
sync
gzip -9 image

4、制作软盘引导,并拷贝文件 bzimage image.gz 到软盘

A.使用grub
fdformat /dev/fd0
mke2fs /dev/fd0
mount /mnt/fd0 /mnt/floppy
mkdir -p /mnt/floppy/boot/grub
cp /boot/grub/stage1 /boot/grub/stage2 /mnt/floppy/boot/grub
执行 grub,在软盘上创建引导
grub > root (fd0)
grub > setup (fd0)
grub > quit

cp /usr/src/linux/arch/i386/boot/bzImge /mnt/floppy
cp ~/porject/minilinux/image.gz /mnt/floppy

编辑 /mnt/floppy/boot/grub/grub.conf
default =0
timeout-=10
title minilinux
root (fd0)
kernel /bzImage
initrd /image.gz

卸下软盘
umount /mnt/floppy


B. 使用syslinux
fdformat /dev/fd0
mkfs.msdos /dev/fd0
mount -t msdos /dev/fd0 /mnt/floppy

cp /usr/src/linux/arch/i386/boot/bzImge /mnt/floppy
cp ~/porject/minilinux/image.gz /mnt/floppy

cp syslinux-xx/ldlinxu.sys /mnt/floppy
cat > /mnt/floppy/syslinux.cfg
LABEL linux
KERNEL bzimage
APPEND initrd=image.gz

umont /mnt/floppy
syslinux-xx/syslinux /dev/fd0
sync

5、用软盘启动计算机,如果幸运,minicom的运行画面出现在屏幕上。

到此,我们的单应用嵌入式linux做好了,但它还很简陋,没有什么实际用途,但通过这个实验,可以了解嵌入式系统的大致结构和开发过程。在进行实际的嵌入式开发时,通常要在PC机上借助嵌入式linux开发工具包,如:uclinux,bluecat等,对相应的硬件平台(目标机)进行软件编写编译,调试成功后,将内核及应用程序写入到目标机的存储器中,从而完成整个应用。


- 作者: dudumami 2005年03月10日, 星期四 16:40  回复(0) |  引用(0) 加入博采

自己的孩子自己带
迫于激烈的生存竞争,许多年轻爸爸妈妈忙于工作,而将孩子全权交给老人。孩子在老人的溺爱下,变得自私、任性、承受不了一点点挫折......为此,有关专家认为,"隔代教育"代替不了"亲子教育"。

  心理专家和育儿专家指出:父母对于孩子身心成长的意义比人们想像的重要得多。如果能更多地参与孩子的培育,对孩子也是一种更持久的幸福。

  儿童教育心理学家认为:父母自己带孩子在孩子个性心理发展上有着不可比拟的优势。母亲与孩子近10个月的共生生活造就了彼此心理上的无比亲密,"母子连心",这是父亲和任何看护人都无法取代的。只有母亲能本能地觉察孩子的需要和面临的危险;只有母亲才能本能地给孩子安全感和满足,孩子身心的健康成长尤其需要母亲不间断地关怀。而一岁半之后,父亲的作用又在于引导孩子独立探索外部世界,形成独立安全感。所以,不管你现在采用的是何种抚养方式,都应该更多地参与孩子的身心成长和教育。

  自己带孩子的优势


  ·现在的年轻父母都参加工作,文化程度比较高,接触面广。孩子在父母身边,能接触到许多在爷爷、奶奶身边接触不到的东西,这样得以培养孩子多方面的兴趣。 

  ·年轻人接受能力强,思想开放,虽说育儿经验少,但现在科学育儿方面的书籍很多,年轻人可以边学边育儿,并能对过去不科学的育儿方法加以辨别、改进。 

  ·年轻父母精力充沛、充满生气,经常带孩子出去活动,有利于孩子形成良好的性格,开阔视野,增长知识。 

  隔代教育大补救  隔代教育大补救

  生理发育——用新观念改造旧方法

  由于祖父母某些育儿观念相对陈旧,科学性比较欠缺,单凭个别的,表面的经验出发,抚育上对孩子过于纵容,导致孩子养成许多不良的健康习惯,影响了孩子的身体发育,而妈妈们则在抚育孩子上有较为科学的看法。

  看下面的观念对比:

  奶奶:多抱抱孩子,他更容易入睡,也很乖。

  导致结果:孩子只有在抱着时才容易睡着。

  奶奶:孩子睡前闹,给他块糖就好了。

  导致结果:孩子总要含着糖才能睡着,容易患龋齿。

  矫正版:

  孩子入睡前要把他平放在床上,抚养者可在身边陪伴。注意睡眠环境的安静、舒适,让孩子自然入睡。

  解决方案:父母尽可能参与育儿,以科学的育儿观念来促进孩子的健康成长,父母工作实在很忙,可以让祖父母多看一些育儿方面的书籍,并参加育儿培训。

  个性培养——教他做一个"小大人"

  祖父母对孩子偏向于依顺和纵容,不太重视孩子独立性的培养,看下面的对话情境,你就能从中看到日常生活中隔代抚养和父母自己带孩子对孩子性格培养的明显不同。

  隔代抚养情境:孩子(五岁):我要喝水!

  奶奶:好宝贝,杯子放得那么高,你自己拿不到,来,奶奶帮你倒上水,现在还太烫,我来吹一下再喝。你的手别碰杯子,奶奶喂你。

  结果:孩子依赖性强,处处都不想自己动手,自己解决问题,且习惯以自我为中心,不会关心别人。

  矫正版: 妈妈(或爸爸)——杯子放在柜子里,自己去取吧!嗯,开水要倒在凉杯里过一阵子再喝,真聪明,来,妈妈也口渴了,再给妈妈倒一杯水来。

  结果:孩子在妈妈的训练下,自己不但学会取杯子喝水,还能为妈妈倒水喝。这样就培养了他独立解决问题的能力,生活小事上自己做决定,还能为他人着想,避免自我中心。

  解决方案:父母适当地参与到对孩子的带养中,设置情境,在情境中培养孩子的独立性和自主性。情境可以设置为多个,如家庭情境、社会情境、人际互动情境等。在情境中充分接纳孩子,赞扬孩子,从侧面给他适当建议,帮助他纠正行为中的错误,不断提高孩子自己解决问题的能力。

  交际能力——新鲜刺激下的活力

  由于老年人大都喜欢安静而不喜欢运动与外出,而孩子的语言能力和交际能力依赖于不断地与外界接触,激发其兴趣,老人在给予孩子新鲜刺激,培养孩子认识事物、探究事物上的能力有限,还可能导致孩子视野狭小,缺乏应有的活力,不利于培养孩子开阔的胸怀和活泼、宽容的性格。这样长大的孩子,为人心胸狭小,不善与人交际,易产生交际恐惧症。

  隔代抚养情境:

  孩子:姥姥,电视上一闪一闪的是什么?好奇怪呀!姥姥:别吵,姥姥要睡觉,你安静一些,自己看吧。

  后果:对孩子智力刺激不足。孩子变得懒散,缺乏活力,缺乏交际。

  矫正版:妈妈:那是宇宙飞船在太空飞行,是人类很神奇的创举。(妈妈继续给孩子讲关于太空探秘的知识,孩子被迷住了。)

  结果:满足了孩子的求知欲,激发了他的活力和好奇心,交谈过程中语言能力得到培养。

  解决方案:父母注意不断给予孩子语言上、智力上的各种刺激,耐心地引导他们去探索外部世界,培养他们广阔的视野和良好的言语交际能力。

  安全感——连接亲子的心理脐带

  孩子成长的早期环境直接影响到他成年后的感情关系和婚姻关系,决定了他与别人相处的安全感。如果他从小没有形成良好的依恋关系,那么在他日后与他人建立亲密和信赖关系方面就会出现障碍,比如与人的疏离感,亲密焦虑,缺乏信任等等。孩子刚出生的时候第一本能反应就是寻找母亲的乳头,这是他与这个世界的第一个紧密的、安全的联系。一岁半之前,孩子需要最大程度地和母亲相处,以建立母婴依恋安全感,如果这个时候,母亲不能照顾孩子,那么这种安全感将很难建立,孩子心里会充满对失去爱的恐惧,导致他产生这样的想法:—连我的母亲都拒绝我、回避我,那么这整个世界都是不安全的。

  很多父母把幼儿"粘人"视为缺点,学前专家特别指出,低幼儿"粘人"不仅不是坏习惯,适当粘人的宝宝还直接有利于将来的沟通和交流。

  上海家庭教育研究会副会长、上海师范大学心理学系曹子芳教授指出,0至1岁半的宝宝多半会对父母产生依恋感。如果到了这一年龄的宝宝,还没有对家人产生依恋感的话,就会给宝宝未来的生活打上阴影。所以这段时间的宝宝最好自己带。当今社会,由于生活节奏加快,多数父母已经没有办法全天候养育、照顾孩子,很多家庭只能请保姆或者是祖父母来照料宝宝的生活起居,孩子和父母之间关系疏远了。很多父母还引以为自豪,认为自己生了个不会"粘人"、大大方方的好宝宝。

  殊不知,家庭才是最能够给每个孩子温暖和勇气的地方,而提供这些力量的就是婴幼儿和母亲之间温暖、亲密、连续不断的关系——适度的依恋(也就是"粘人现象")。这不仅可以促使幼儿找到满足感,而且还可以帮助幼儿享受愉悦感。适度的依恋有助于建立一个人的信赖度和自我信任感,将来能够成功地与伴侣、后代和睦相处。因此,专家提醒,如果在婴幼儿时期,宝宝还没有产生适度的"粘人性",将来就可能很难和别人沟通,影响他今后的社会生活和家庭生活。

  孩子是这个世界的小天使,每一对父母都有抚育他们的责任,除了在生活上的照顾外,有时候,心理上的育儿更加重要,这也关系到孩子日后基本心理素质的养成。父母适当参与带养孩子,是十分必要的。爸爸妈妈们,工作诚可贵,育儿价更高,尽可能抽出时间到孩子身边,陪伴孩子成长吧,这是我们能给他们的一生中最好的礼物。

- 作者: dudumami 2005年03月4日, 星期五 14:57  回复(1) |  引用(0) 加入博采