【Linux阅读笔记】LinuxC一站式编程2-数据类型、运算符与汇编基本

数据类型分析

浮点型

浮点数在不同平台上实现不同
有的处理器有浮点运算单元(Floating Point Unit,FPU),称为硬浮点(Hard-float)实现
有的处理器没有浮点运算单元,只能做整数运算,需要用整数运算来模拟浮点运算,称为软浮点(Soft-float)实现

在 x86 平台上,大多数编译器实现的 long double 型是 80 位
gcc 实现的 long double 型是 12 字节(96 位)


类型转换

C 中有转换级别机制,以下类型转换级别(Rank)越来越高:char、short、int、long、long long


运算符分析

移位问题

避免不同类型数值赋值操作;
因为 C 语言中不存在 8 位整数的二进制位运算,所有位运算执行之前都被提升为 int 类型

在一定的取值范围内
左移 1 位=乘以 2
右移 1 位=除以 2


异或运算特性

一个数和自己做异或的结果是 0

和 0 做异或保持原值不变,和 1 做异或得到原值的相反值

可用于奇偶校验:例如 a1 ^ a2 ^ a3 ^ … ^ an 的结果是 1,则表示 a1、a2、a3…an 之中 1 的个数为奇数个,否则为偶数个

x ^ x ^ y == y


计算机体系结构

CPU 的核心功能包括这部分

  1. 寄存器:特殊寄存器、通用寄存器
  2. 程序计数器 PC:特殊寄存器,保存着 CPU 取下一条指令的地址
  3. 指令译码器:负责解释从 CPU 中取出的指令对应段的含义(比如内存地址、寄存器编号等等)
  4. 算术逻辑单元 ALU:译码器转换的运算指令给 ALU 进行运算
  5. 地址与数据总线 Bus

内存映射 LO 定义
无论是在 CPU 外部接总线的设备还是在 CPU 内部接总线的设备都有各自的地址范围,都可以像访问内存一样访问


MMU 内存管理单元

MMU 工作原理
CPU 发出获取内存地址请求,此时传递虚拟地址 VA 给 MMU,MMU 将 VA 转换成物理地址 PA 给 CPU 外部的指定芯片引脚

如果 MMU 不工作,那么 CPU 发出的内存地址请求均为 PA,直接对应外部芯片引脚

MMU 管理一张虚拟页表,一一对应物理内存上的物理页表内容
每次 CPU 访问内存时,都会触发 MMU 的查表和地址转换操作


MMU 存在的意义

  • 内存保护机制。MMU 可以拦截不同用户组的请求,根据其拥有的权限选择是否拦截(不转换地址)或者放行(转换地址)
  • 有效避免内核空间和用户空间地址污染,使二者独立

汇编基本

最简汇编程序

汇编程序根据编译器的不同,使用 asm 或者 s 作为后缀;
首先要将汇编源文件使用汇编器翻译成机器指令,生成后缀为 o 的文件,然后再通过链接器编译成可执行文件

.section .data
.section .text
.globl _start

_start:
movl $1,%eax #this is the Linux kerneL command

movl $4,%ebx #this is the status number we wiLL

int $0x80   #this wakes up the kerneL to run

# 汇编中表示单行注释

汇编程序中以.开头的名称并不是指令的助记符,不会被翻译成机器指令,而是给汇编器一些特殊指示,称为汇编指示(Assembler Directive)伪操作(Pseudo-operation)

.section .text section 表示开始划分段的标志,text 表示后续的代码都属于 text 段

.globl 可理解为设置全局变量

_start 汇编程序入口点,必须被设置为全局变量
_start: 在这里开始写主入口程序


movl $1,%eax
movl 其实是 mov+l 的结合,l 表示该变量类型为 long
1 表示立即数 1 ( 1 表示立即数1( 1表示立即数1加任意数字都可以表示一个立即数)
%eax 表示寄存器 eax(所有寄存器都必须加%)

不难得出移位的格式为 movl [立即数],[欲保存到的寄存器]

int $0x80 软中断指令,可使程序故意产生一个异常导致程序终止运行;可以将其视为程序出口点


汇编语法分异

x86 汇编存在两种主流语法:

  1. AT&T 派:数据传送指令 mov 这样写 movl $1,%eax
  2. Lntel 派:数据传输指令这样写 mov eax,edx(寄存器不加%且存取位置互换)
  3. UNLX 平台一般采用 AT&T 语法

x86 寄存器

x86 通用寄存器:eax、ebx、ecx、edx、edi、esi

某些特殊场景下,他们会变得不那么“通用”,此时寄存器会有一个或者多个限制
(比如进行除法运算时)

x86 特殊寄存器:ebp、esp、eip、efLags
efLags 保存着计算过程中产生的标志位
ebp 和 esp 用于维护函数调用的栈帧


求最值汇编

# 定义数据存储段data
.section .data
# 类似于数组名
data_items:
# 定义数组类型,.long表示32位,.byte表示8位
.long 3,67,34,222,45,75,54,34,44,33,22,11,66,0

# 主程序段text
.section .text
# 程序入口点与全局变量
.globl _start
_start:
movl $0,%edi    #move 0 into the index register
movl data_items(,%edi,4), %eax # Load the first byte of data
movl %eax,%ebx   #since this is the first item,%eax is

# 循环开始,开头定义一个start_loop
start_loop:
cmpl $0,%eax  # 比较寄存器eax是否等于0,如果为0表示已到末尾,需要跳出循环
je loop_exit  # je即比较,如果上方代码相等,那么跳转到对应标志位

incl %edi   # edi寄存器移到下一位(即加载下一个数据)
movl data_items(,%edi,4), %eax
cmpl %ebx,%eax
jle start_loop # jle(jump if less than or equal)

movl %eax,%ebx
jmp start_loop # jmp是一个无条件跳转指令,类似c语言中的default

# 循环结束,结尾定义一个loop_exit
loop_exit:

movl$1,%eax
int $0x80

寻址方式

访问内存的三个方式:数组基地址、元素长度和下标

内存寻址指令的通用格式:ADDRESS_OR_OFFSET(%BASE_OR_OFFSET,%INDEX,MULTIPLIER)

几种主要的寻址方式

  • 直接寻址:只能用 ADDRESS_OR_OFFSET 寻址
  • 变址寻址:如 movI data_items (,%edi,4)中的%eax
  • 间接寻址:只使用 BASE_OR_OFFSET 寻址
  • 基址寻址:只使用 ADDRESS_OR_OFFSET 和 BASE_OR_OFFSET 寻址,便于访问结构体成员
  • 立即数寻址
  • 寄存器寻址

ELF 文件

UNIX 可执行文件均采用 ELF 格式,它包含以下三种类型

  • 可重定位的目标文件(Relocatable,或者 Object File)
  • 可执行文件(Executable)
  • 共享库(Shared Object,或者 Shared Library)

程序简易的汇编、链接、运行流程

  1. 编写汇编程序保存为 demo.s 文件
  2. 汇编器读取 demo.s,将源码中的.section 编译为目标文件的 Section
  3. 链接器将目标文件的 Section 汇总为 Segment,生成可执行文件 demo
  4. 加载器根据 Segment 信息加载运行程序!

猜你喜欢

转载自blog.csdn.net/delete_you/article/details/130146082