`
lovnet
  • 浏览: 6874079 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
文章分类
社区版块
存档分类
最新评论

深入系统底层--之--教你用0101写程序

阅读更多

准备你的行囊----建立环境

为了让大家更为轻松,除非迫不得已,我们尽量使用系统上已经安装的工具,在这一章里,下面两个外部工具是必须的

  • nasm: 作为汇编环境,官方网站http://www.nasm.us/
  • UltraEdit:作为16进制文本编辑器

同时,读者应该稍微具备的汇编知识,不用太多,知道下面这些指令的意义和用法即可

MOV 数据传送指令
ADD 加法指令
PUSH,POP 堆栈指令
CMP 比较指令
LEA 取地址指令
XOR 异或指令
所有的转移指令:JMP,JZ,JE

如果你还想进一步了解机器码的规范,可以下载 http://download.csdn.net/source/1103630,里面有Intel的文档,以及本文用到的操作码查询表

用0和1写程序

曾经有人发给我一张图片,说世界上"最牛程序员"的键盘,键盘上一共两个键,01,当时年少无知,崇拜到抓狂,今天就让我们当回"顶尖高手",用01直接写程序

请打开一个十六进制编辑器比如UltraEdit

把下面的二进制代码化为16进制输入进去(主要无法直接输入二进制代码)

1011 1000 0000 0001 0000 0000 0000 0101 0000 0001 0000 0000

十六进制为B8 01 00 05 01 00

将文件保存为test.com文件,恭喜你,你刚刚完成了一个伟大"壮举",你成功的让CPU计算出了1+1等于几,如果你兴匆匆的运行它,什么结果都看不到,那是因为为了保证代码简单,还没有告诉CPU输出结果的缘故,你愿意的话,可以运行cmd,切换到保存test.com的目录,通过执行debug test.com,来看看我们到底输入了什么

image

1011 1000 代表 MOV ax
0000 0001 0000 0000 代表1
0000 0101 代表 ADD ax
0000 0001 0000 0000 代表 0001h

全文加起来表示
MOV ax,01h
ADD ax,01h

可以看出,我们的代码对应了两条机器指令,每个指令分成两个部分,比如MOV ax,1的二进制代码,1011 1000 代表 MOV ax他指定了本条指令的操作,叫做指令操作码(Opcode),0000 0001 0000 0000 代表1,指定了操作的操作数,可以看出机器码是有自己固定的格式,只要掌握了这个格式,查询对应的操作码,应该就可以掌握机器语言了

当然,事情也有复杂的一面,同一条汇编指令其操作码可能根据寻址方式或寄存器或操作数的位数的变化发生变化,比如同样是MOV指令,MOV al,1 和MOV ax,1中Mov的操作码分别为B0(1011 0000)和B8(1011 1000),而MOV ax,[ES:100]操作码会变成26 A1(前面26是段超越前缀,现在不用仔细追究),Intel8086规定的MOV指令就有12种之多,而且操作码的长度还有可能不同,这些操作码都可以在表<>中对应的查到,不需要记忆,下面我们就来了解机器语言指令的格式

自己设计机器语言指令格式

在阅读Intel公司的实现前,为了不让您陷入一堆的解释和说明中迷惘无助,我们先来热热身,做点有趣的事情---思考一下如果让你自己来设计机器语言指令的格式,那么你会做出怎样的设计,下面是我的设计思路

首先汇编代码和机器代码是对应的,所以让我们来看看一条典型x86汇编指令:

MOV ax,1

这条指令由三个部分组成:指令,目的操作数,源操作数

指令为Mov,目的操作数ax,源操作数1,

ADD bx,2

指令为Add,目的操作数bx,源操作数2

相对应的我们可以考虑把机器指令格式也分成三个部分:指令码,目的操作数,原操作数

由于寄存器的数目是有限的,我们可以列个寄存器机器码指令表,这样代码中的寄存器就可以被替换为如下的机器代码,比如

  • 000 AX
  • 001 CX
  • 010 DX
  • 011 BX
  • 100 SP
  • 101 BP
  • 110 SI
  • 111 DI

    然后我们再列一个指令码表,比如

    MOV=00000000
    ADD=00000001
    AND=00000010
    .
    .
    .

    则MOV ax,1就可以变成 00000000 00000000 00000001(ax是000)

    但是这样简单清晰的三个部分会出现一些问题mov bx,0,和mov bx,ax就有可能混淆了,因为ax的代码是000,和立即数0相同

    所以我们需要一个标志位来确定是那种操作数,操作数有下面5种可能

    目的操作数和原操作数的大小就比较难了,因为操作数可能是

    1)一个立即数 比如1

    2)一个寄存器 ax,bx,cx,dx

    3)一个内存地址 [StringLable]

    4)一个由一个或多个寄存器组成的内存地址

    [ebx],[ebx+esi],[es:ebx+esi]

    5)一个由一个或多个寄存器再加上一个偏移量组成的内存地址

    [ebx+esi]

    显然我们需要两个标志字段,每个5个值,(每个操作数一个)来标定自己是哪种操作数,每个标志字段只要3位就够了,我把这两个标志字段放到一个字节里,放在两个操作数前面

    格式一:

    指令码 保留2位|标志1|标志2| 操作数1 操作数2
    Mov ax,1 00000000 00|001|000 00000000 00000001

    标志的意义

    000:立即数
    001:寄存器
    010:内存地址
    011:多个寄存器
    100:多个寄存器加偏移量

    问题又出来了,当标志位为100,这时,操作数应该是多个寄存器+偏移量,假设每个寄存器占3位,两个就是6位,留给我们的偏移量的空间只有两位,也就是说偏移量最大只有3,这显然是不够的,所以我们必须加上一个字节表示偏移量,而当不需要偏移量的时候,这两个字段可以不存在,也就是说表格变成了

    格式二:

    指令码 00|标志1|标志2 操作数1
    偏移量 00|操作数2
    bbb|iii
    偏移量
    Mov ax,[bp+si+5] 00000000 00|001|100 00000000 00|101|110 00000110

    怎么样,有点像样子了吧,固定长度8位的指令码可能有256种指令,我想最基本的操作,AND,OR,XOR,ADD,SHR,SHL等等不会太多,而其他的操作都可以由这些操作组合而成,比如减法是补码的加法,乘法是重复相加等

    似乎大部分问题都已经解决了,但是稍微熟悉x86汇编的朋友就会知道,不可能有任何指令的两个操作数都是内存,也就是永远不会出现
    MOV [dx+di],[ex+si]这样的语句,要想实现这样的移动我们必须要把源操作数移动到一个寄存器里,然后再从寄存器里移动到目的地

    反应在我们的设计上,我们就会发现两个偏移量是多余的,任何情况下最多会有一个被使用到,所以表格可以修改成这样

    格式三:

    指令码 00|标志1|标志2 偏移量 操作数1
    操作数2
    00|bbb|iii
    MOV ax,[bp+si+5] 00000000 00|001|100 00000110 00000000 00|101|110
    MOV ax,bx 00000000 00|001|001 00000000 00000011

    其实看看上表的第二条语句,我们就会发现一个很重大的问题,那就是空间浪费,第二行中所有黑体的部分都是被浪费掉的空间,浪费了12位,总共才32位的代码,居然就浪费了12位,心疼啊,而且看看标志字段,占了三位,总共可以表示8个标志,确只用了5个,我们能不能想办法把这些空间利用起来呢?

    我们重新仔细考虑第二个字节,也就是标志字节,把最高位的两位利用起来,称作寄存器标志,他的值如下表

    00:操作数中没有寄存器

    01:操作数的后一个为寄存器

    10:操作数的前一个为寄存器

    11:两个操作数都是寄存器

    如果此位指明某操作数为寄存器,则后面的标志位直接为寄存器值,如果为00,则后面的操作数只可能为 (内存,立即数) 形式,这样MOV ax,bx的机器码就变成了下面的样子

    格式四:

    指令码 寄存器标志|标志1|标志2 偏移量 操作数1
    操作数2
    00|bbb|iii
    MOV ax,bx 00000000 11|000|011

    好了,指令系统的雏形已经出来了,虽然和Intel的实现有很多不同,并且本身还有各种问题,比如依然有浪费空间的情况,功能也不太健全,不过基本体现了指令格式的特点:

    • 分成几个字段表示不同意义
    • 尽量短小精干
    • 不能浪费任何一位

    下面让我们来看看Intel公司的实现方法

    让书写机器码像填表一样简单

    从上面的叙述,我们已经大概能看出点门道,每条指令分为几个部分,表示不同的含义.Intel规定,机器指令都可以被表示成六个部分,Prefix,Opcode,ModR/M,SIB,Displacement,Immediate,除了Opcode部分是必须的外,其他部分都有可能不存在

    好像有点复杂不是?不要着急,我们稍作解释就可以把书写机器指令变得像填写表格一样简单

    下面我们把几条命令按照六个部分进行分割,填写到这张表里,后面会解释六个部分的含义

    Prefix
    前缀
    0-4个前缀,每个1字节
    可选
    Opcode
    操作码
    1-2字节
    一定存在
    ModR/M
    寻址与寄存器
    1个字节
    可选
    SIB
    内存寻址模式
    一个字节
    可选
    Displayment
    偏移量
    1,2或4个字节
    可选
    Immeidate
    立即数
    1,2或4个字节
    可选
    oo|rrr|mmm cc|iii|bbb
    MOV ax,1 1011 1000 0001 0000
    ADD ax,1 0000 0101 0001 0000
    MOV ax,[ES:0100h] 0010 0110(26h代表es的段超越前缀) 1010 0001 0000 0000
    0001 0000
    mov ax,[ebx+esi*2+1] 0110 0111
    (67h,代表使用了32位
    1000 1011 01 000 100 01 110 011 0000 0001
    mov [ebx+esi*2+1],01h 67 1100 0111 01 000 100 01 110 011 0000 0001 0000 00001


    只要会填这个表,我们就可以写出所有的机器代码.

    可以看到,Intel的格式中并没有明确的标出两个操作数,而是把偏移量和立即数单独拿了出来,而且同一条指令的操作码会根据寻址方式的不同而变化,不像我们的设计,MOV就是MOV,所有的MOV指令都对应同样的操作码,Prefix部分也是我们的设计所没有的

    下面简单的解释下这六个部分,每个部分的具体含义和使用,后面的例子里会逐步阐述

    prefix:

    指令前缀,为了一些特殊的定义或者操作而存在,只有10个可能的值,可以在下表里面查到,我们大致了解下就是了
    • 锁(Lock)和重复前缀:
    锁前缀用于多CPU环境中对共享存储的排他访问。重复前缀用于字符串的重复操作,他可以获得比软件循环方法更快的速度。
    — F0H—LOCK 前缀.
    — F2H—REPNE/REPNZ 前缀.
    — F3H—REP 前缀
    — F3H—REPE/REPZ prefix (used only with string instructions).
    • Segment override:
    根据指令的定义和程序的上下文,一条指令所使用的段寄存器名称可以不出现在指令格式中,这称为段缺省规则。当要求一条指令不按缺省规则使用某个段寄存器时,必须以段取代前缀明确指明此段寄存器。
    — 2EH—CS 段前缀
    — 36H—SS 段前缀.
    — 3EH—DS 段前缀.
    — 26H—ES 段前缀.
    — 64H—FS 段前缀.
    — 65H—GS 段前缀.
    • 操作大小前缀 66H 和 地址长度前缀 67H

    Opcode:

    操作码,这个操作码指定了具体的操作,他的值可以在下表查到,注意查表时候要根据操作类型,操作数类型和寻址方式来查询,比如Mov指令有12种操作操作码,我们需要根据操作数的类型,比如Mov bx,1,的两个操作数一个是寄存器,一个是立即数,即Reg,Imm,查下表,应为1011wrrr

    MemOfs,Acc 1010001w
    Acc,MemOfs 1010000w
    Reg,Imm 1011wrrr
    Mem,Imm 1100011woo000mmm
    Reg,Reg 1000101woorrrmmm
    Reg,Mem 1000101woorrrmmm
    Mem,Reg 1000100woorrrmmm
    Reg16,Seg 10001100oosssmmm
    Seg,Reg16 10001110oosssmmm
    Mem16,Seg 10001100oosssmmm
    Seg,Mem16 10001110oosssmmm
    Reg32,CRn 000011110010000011sssrrr
    CRn,Reg32 000011110010001011sssrrr
    Reg32,DRn 000011110010000111sssrrr
    DRn,Reg32 000011110010001111sssrrr
    Reg32,TRn 000011110010010011sssrrr
    TRn,Reg32 000011110010011011sssrrr

    表中rrr,w,mmm,oo都可以看做几个变量, 会根据寄存器,和寻址方式的变化而变化,如果使用4位寄存器,比如al,ah,bl,bh等,则其值为0,否则为1,表<>可以查到,注意所查的结果中已经包含了后面的ModR/M字节

    ModR/M和SIB:

    这两个字节共同决定了寻址方式,ModR/M包含三个部分oo|rrr|mmm:这三个部分联合表示了寻址方式,oo指示了寻址模式,rrr:指明所用寄存器,注意使用<>查询得到的结果里已经包含ModR/M字节,而SIB是辅助的寻址方式确定位,也包含三个部分

    • ss:放大倍数
    • iii:变址寄存器
    • bbb:基址寄存器

    比如如果用到这样的地址[ebp+5*esi],则ebp为基址寄存器,esi为变址寄存器,5为放大倍数

    Displayment偏移量位:寻址方式中的偏移量,如[ebp+5]中的5

    Immediate:立即数,操作数中的立即数

    一起练练手:人肉翻译汇编代码

    一) mov bx,cx

    查询其操作码为1000 100w,由于使用16位寄存器,则w=1 得到100010001即16进制的89H

    ModR/M 包含三个部分oo|rrr|mmm:这三个部分联合表示了寻址方式,这里由于没有内存寻址,查表得,oo=11,rrr和mmm各表示一个寄存器,那么问题来了:哪个表示目的寄存器bx,哪个表示源寄存器cx呢?翻文档太累了,不如用nasm汇编一下这条指令瞧瞧.得到的ModR/M字节为对应寄存器代码可以看出来,rrr表示的是源寄存器bx,则这一个字节为:11 001 011,即16进制CBH

    由于这条语句没有内存寻址,SIB列为空,也没有偏移量列Displayment,这条语句也没有立即数作为操作数,所以Immediate列为空

    至于Prefix列,我们稍微看下Prefix的说明和他的值表就能知道,Prefix列只有少数的几种情况才能出现,比如段超越啊,16位/32位切换啊,锁定啊,像mov bx,cx这样普通的语句自然也没有Prefix列

    所以我们可以得到mov bx,cx的最终代码为

    Prefix
    Opcode
    ModR/M
    oo|rrr|mmm
    SIB
    ss|iii|bbb
    Displayment
    Immeidate
    mov bx,cx 100010001 11 001 011
    mov cx,bx
    mov cl,bl

    既然已经掌握了mov bx,cx,那么mov cx,bx呢? mov cl,bl呢?大家自己想想

    如果觉得上面例子还是太简单了,毕竟6列只用了2列,那么我们就来挑战一个有点难度的怎么样

    二) mov [ebx+esi+1],dword 00h

    word是nasm的关键字,表明存入内存的操作数是一个双字,在内存中占32位,即4个字节

    查询Opcode,得1100011w,w=1,即C7

    现在来看ModR/M,这里会有些变化了,我们要仔细分析我们的内存寻址方式ebx+esi+1,有一个8位的偏移量1,所以oo=01,后面的rrr和 mmm该指明用于寻址的两个寄存器,ebp和esi,查询rrr表,应该分别是011,110,则rrr=011,mmm=110,但是我偏偏不这样作, 我设置rrr为000(EAX),mmm为100(ESP),于是代码变为了01000100,44h

    奇怪?明明是ebx+esi,怎么偏偏让你给变成了eax+esp了?

    其实在查询mmm的时候,我们不应该查询rrr表,应该查询iii表,iii表是专门查询变址寄存器号码的,rrr表和iii表基本上完全相同,只是 rrr表中100代表ESP,而iii表中呢.....no index....,这不是表示没有变址寄存器,而是表示设置两个寄存器的工作交给后面的SIB来做,44h可以看做是个特殊的数字,这个数字就表明寻址方式所用的寄存器会让SIB位来完成.

    上面的做法不是我别出心裁,其实如果你用nasm编译这句话,也会得到这个结果,让SIB来设置内存寻址,我想至少有两个好处,

    一是可以更加灵活一些,毕竟人家SIB有整整一个字节专门来作这件事情,比如如果寻址模式位改为ebx+esi*2+1,SIB里专门有两位ss,表示这个倍数,而ModR/M里呢,对不起,没地方放了

    二是可以让汇编编译器简单一些:统一成一种格式方便处理

    ok,那么如果我们严格按照寄存器查表的结果(ebx=011,esi=110)能不能运行呢,大家自己去试试吧

    SIB
    ss:没有倍数,ss=00
    iii:刚才查过了esi=110
    bbb:ebx=011
    合起来是00110011即33

    后面是8位的偏移量,01h,最后是立即数00h,注意这里是个双字,所以占4个字节

    填在表里

    Prefix
    Opcode
    ModR/M
    oo|rrr|mmm
    SIB
    ss|iii|bbb
    Displayment
    Immeidate
    mov [ebx+esi+1],dword 00h 67,66 C7 44 33 01 0000

    你可能用nasm汇编了一下这条语句,发现前面多了个67,66,恭喜你,67和66正是Prefix,由于你是在16位环境下汇编的,所以如果某条指令使用到32位的数据和地址,指令前面就会出现前缀,67表示使用了32位地址,66表示使用了32位数据.消除的方法是在文件头上加上[BITS 32]

    推荐一个好的机器码入门<老罗的OPCODE教程:http://www.luocong.com/learningopcode.htm>,x86 OPCODE规范下载<>

    让人迷惑的倒置 -LittleEndian

    参见上面的代码,MOV到ax的操作数为16位二进制的一,即0001h(h表示16进制)可是从这里看上去,是0100h,这是为什么呢?

    其实这是著名的Little Endian存储格式捣的鬼,Little Endian的意思是高位在高地址,低位在低地址,比如0100 0011 0010 0001这个二进制数(十六进制为4321h),在内存里类似

    位置 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15
    1 0 0 0 0 1 0 0 1 1 0 0 0 0 1 0

    显示的时候,显示程序一般都以一个字节为整体显示这个数,即先解析处0-7位,为数字21h,显示在前面,然后解析8-16位,为数据43h,显示在后面,则变为了21h 43h,如果显示程序能按照字为整体解析并显示,就能没有这个倒装了,但是显示是不会知道你到底需要怎么显示的,比如你可以定义一个32位数据,也可能定义64位数据,即使是按照16位,也仍然会有倒装发生,所以现在一般显示程序都简单按照字节显示

    除了LittleEndian反过来当然也有BigEndian,这种存储格式就和咱平时的数字理解习惯没有冲突了

    LittleEndian 是Intel x86(8086/8088,80286,80x86,PentiumX)系列CPU所采用的格式,而BigEndian是Motorola的 PowerPC系列CPU所采用的标准,网络传输也采用BigEndian,二者各有优缺点,有兴趣的读者可以参考1980年的著名论文<On Holy Wars and a Plea for Peace>

    别看LittleEndian这个是个细节,却绊倒了不少初学者的腿,比如你刚打开Windbg,想尝试利用调试工具修改某个游戏角色的体力值,从 157110修改为100000000,157110的16进制为265B6,而你在内存里怎么都找不到02 65 B6这个序列,那就是LittleEndian搞的鬼

  • 据Jargon File记载,endian这个词来源于Jonathan Swift在1726年写的讽刺小说 "Gulliver's Travels"(《格利佛游记》)。该小说在描述Gulliver畅游小人国时碰到了如下的一个场景。在小人国里的小人因为非常小(身高6英寸)所以总是碰到一些意想不到的问题。有一次因为对水煮蛋该从大的一端(Big-End)剥开还是小的一端(Little-End)剥开的争论而引发了一场战争,并形成了两支截然对立的队伍:支持从Big- End剥开的人Swift就称作Big-Endians而支持从Little-End剥开的人就称作Little-Endians……(后缀ian表明的就是支持某种观点的人:-)。Endian这个词由此而来。
    1980年,Danny Cohen在其著名的论文"On Holy Wars and a Plea for Peace"中为了平息一场关于在消息中字节该以什么样的顺序进行传送的争论而引用了该词。该文中,Cohen非常形象贴切地把支持从一个消息序列的 MSB开始传送的那伙人叫做Big-Endians,支持从LSB开始传送的相对应地叫做Little-Endians。此后Endian这个词便随着这篇论文而被广为采用。

    思考:指令的起止

    既然每条指令都可能不一样常,我们的CPU怎么知道每条指令从哪里开始,到哪里结束?

    要知道变长指令的起止,系统就必须自己知道各个指令的长度,可以说系统内部有个登记簿,登记了每个指令的长度.

    程序执行的时候,系统会把eip指向的指令加载到cpu,cpu会尝试翻译指令,这样系统会知道这条指令的长度,比如长度为6,则将eip增加6,指向下一条语句.如何正确计算指令长度本身是采用CISC(复杂指令集)计算机特有的问题,因为使用RISC(精简指令集)的 cpu,他的指令长度是固定的,让指令变长的优势在于可以节省空间,也方便以后的扩展,缺点是cpu实现会比较复杂

  • 输出结果

    也许你觉得虽然cpu已经执行了我们的工作,但是由于看不到结果,不能满足我们小小的虚荣心,那么下面我们就告诉系统,让他把结果展示在屏幕上

    打开刚才建立的test.com,在刚才的程序后面附加上下面这段

    04 30 88 C2 B4 02 CD 21 E9 FD FF

    程序变为:

    B8 01 00 05 01 00 04 30 88 C2 B4 02 CD 21 E9 FD FF

    保存运行一下看看是不是输出了结果

    感觉好多了吧,至少看见了自己劳动的结晶,后面附加的那段机器码是调用了Dos的int 21中断输出了一个字符,我们直接给出他对应的汇编代码

    mov ax,1
    add ax,1
    add al,'0' ;数字到ascii的粗糙转换
    mov dl,al ;-----|
    mov ah,02h;-----|--调用中断
    int 21h ;-----|
    jmp $ ;保证程序不会立即退出,好让我们看到结果

    image

    从上面的图上我们可以清晰的看到机器码和汇编指令的对应关系,不再赘述

    add al,'0',是把结果转化成ascii,'0'的值为30h,2+30h=32h,是'2'这个字符的ascii值,当然这是个非常粗糙的转换,一旦数字大过9,就会输出奇怪的结果,这样作是为了机器码尽量简单,方便大家输入

    通过上面的二进制编码与汇编代码的对比,我们大概能知道汇编和机器指令是一一对应的,但是由于机器指令实在是太不方便人类记忆,写起来也非常繁琐,所以需要汇编语言,也就是说汇编语言实际上是机器语言的助记符号

    总结

  • 我们会算1+1了

  • 分享到:
    评论

    相关推荐

      _IBM--PC 汇编语言程序设计实验教程

      在本教程中,你将学习如何编写、调试和优化汇编语言程序,这对于理解计算机底层工作原理、提高程序性能以及解决特定硬件问题具有重要意义。 汇编语言的基础知识包括以下几个方面: 1. **指令集架构**:IBM-PC采用...

      android 底层系统开发资料

      这份“android 底层系统开发资料”涵盖了这一领域的核心分析和平台介绍,对于深入理解Android系统的运行机制至关重要。下面将详细探讨这些知识点。 一、Android系统结构 Android系统是一个基于Linux内核的开源操作...

      TI LM3S系列底层程序

      总之,“TI LM3S系列底层程序”涵盖了嵌入式系统开发的关键环节,从硬件接口的驱动编写到系统级的程序设计,是学习和掌握LM3S5749及其相关型号的重要参考资料。通过深入研究这些内容,开发者可以提升其在嵌入式领域...

      吴章金 - 智能手机底层系统优化的演进——从M9到PRO5

      在智能手机领域,系统优化是一个持续而深入的课题,随着硬件性能的提升和软件需求的增加,对于智能手机底层系统优化的需求也在不断增长。在本次演讲中,吴章金先生主要介绍了从M9到PRO5智能手机的底层系统优化的演进...

      android教程(底层)

      这个教程旨在帮助开发者深入理解Android系统的底层工作原理,从而更好地进行应用开发和系统优化。 Android系统由以下几个层次构成: 1. **Application(应用程序层)**:这是用户直接接触的部分,包含Android内置...

      清华大学计算机教程之-数据结构-程序设计-程序基础

      而“清华大学计算机教程之-数据结构”可能是包含课程内容的PDF文档,涵盖理论讲解、实例演示和习题解答,通过逐步学习和实践,学生可以系统地掌握数据结构的原理和应用。 总的来说,数据结构和程序设计是构建高效...

      46-001-T-烽火hg680-ka-mv300处理器-强刷固件带教程 当贝桌面纯净版安卓系统

      标题中的“46-001-T”可能是一种型号或者版本编号,“烽火hg680-ka-mv300处理器”...同时,这也是一份学习和实践Android系统刷机技术的资源,对于想要深入理解Android系统和设备底层操作的IT爱好者来说具有很高的价值。

      算王安装算量ET199写锁工具包含底层数据及配套驱动

      《算王安装算量ET199写锁工具——深入解析与应用指南》 在工程领域,特别是建筑行业的工程量计算中,精确、高效的算量软件是不可或缺的工具。"算王安装算量"就是这样一款专业软件,它以其强大的功能和易用性深受...

      eye-U010及M1-U010读写器演示程序及开发包

      这个开发包包含了必要的驱动程序、API接口、示例程序以及详细的文档资料,以确保用户能够顺利地理解并使用这两款读写器。 1. **读卡器介绍**: - eye-U010和M1-U010是明华澳汉公司推出的智能卡读写设备,它们支持...

      Xilinx公司xdma驱动下的底层读写DLL封装

      在本文中,我们将深入探讨Xilinx公司的xdma驱动下的底层读写DLL封装技术,这是针对PCI Express(PCIE)开发中的一个关键环节。Xilinx的xdma IP核是用于实现高性能PCIE接口的一种解决方案,而将底层读写操作封装成DLL...

      IBM-PC汇编程序设计教程

      《IBM-PC汇编程序设计教程》是一本深入解析IBM-PC平台汇编语言编程的教程,适合初学者和有经验的程序员进一步提升技能。该教程以PPT形式呈现,内容涵盖8个主要章节,旨在全面讲解汇编语言的基础概念、指令系统、程序...

      汇编程序设计教程 2011-11-20

      《汇编程序设计教程》是针对计算机编程领域中基础但至关重要的汇编语言进行深入讲解的教程。汇编语言是一种低级编程语言,它与计算机硬件的指令集紧密相关,是程序员直接控制计算机硬件的手段之一。本教程适用于对...

      Linux设备驱动程序学习教程-170页-pdf版

      总之,这份"Linux设备驱动程序学习教程"是学习Linux系统底层编程和硬件交互的理想资料,通过170页的详尽内容,可以系统性地掌握设备驱动开发的各个方面,为成为一名合格的Linux系统开发者奠定坚实基础。

      分布式RPC系统框架-Dubbo(2.7)教程(4.31G)

      这种方式大大降低了分布式系统中的通信复杂性,使得开发者可以更专注于业务逻辑,而无需关心底层网络通信细节。Dubbo作为一个优秀的RPC框架,它提供了服务注册、服务发现、负载均衡、容错机制等核心功能。 在Dubbo...

      中文版-深入理解计算机系统讲义 第三章 程序的机器级表示

      《深入理解计算机系统》第三章主要探讨的是程序的机器级表示,这是一门涉及底层计算机运作原理的关键领域。本章内容被分为五个部分,分别涵盖了基础、控制、数据、过程和高级主题,这些主题旨在帮助读者理解计算机...

      cpp-两千行的教程代码教你如何构建自己的深度学习系统

      总之,"cpp-两千行的教程代码教你如何构建自己的深度学习系统"是一次宝贵的学习机会,它将带你深入了解深度学习的底层工作原理,以及如何用C++实现一个高效的深度学习框架。通过这个项目,你不仅可以提升编程技能,...

      G广联达-ET199写锁163数据底层加驱动

      《G广联达-ET199写锁163数据底层加驱动》是一个针对建筑行业信息化管理的专业技术教程,其核心内容涉及到了广联达软件的ET199写锁技术和163数据处理,以及相关的底层驱动程序开发。在深入理解这个主题之前,我们需要...

      C语言程序设计--期刊管理系统

      《C语言程序设计--期刊管理系统》是一份详细的教学资料,主要针对使用C语言进行程序设计,特别是构建一个期刊管理系统的实践教程。C语言是计算机科学中的基础编程语言,以其高效、灵活和对底层硬件的控制力强而著称...

    Global site tag (gtag.js) - Google Analytics