第一周
了解 Linux 的历史,Linux 与 Windows 的区别等入门知识。
1.Terminal(终端)
Linux 系统还提供了一个叫做终端模拟器的程序(Terminal),下面几个比较常见的终端模拟器,例如 gnome-terminal,kconsole,xterm,rxvt,kvt,nxterm 和 eterm,目前我们的实验中的终端程序是 xfce 桌面环境自带的 xfce-terminal。不过要注意的是这里所说的终端(Terminal)和控制台(Console)是有区别的。
在物理机系统上你
使用Tab
键来进行命令补全,Tab
键一般键盘是在字母Q
旁边,这个技巧给你带来的最大的好处就是当你忘记某个命令的全称时你可以只输入它的开头的一部分然后按下Tab
键就可以得到提示或者帮助完成:
可以通过使用[Ctrl]
+[Alt]
+[F1]~[F6]
进行切换,不过在我们的在线实验环境中可能无法切换,因为特殊功能按键会被你主机系统劫持。
Ctrl+c]
想想你有没有遇到这种情况,当你在 Linux 命令行中无意输入了一个不知道的命令,或者错误的使用了一个命令,导致在终端里出现了你无法预料的情况,比如,只有光标在闪烁无法继续输入命令,或者不停地在输出一大堆你不想要的结果。你想要立即停止并恢复到你可控的状态,那该怎么办呢。这时候你就可以使用Ctrl+c
键来强行终止当前程序(你可以放心它并不会使终端退出)。
3).学会使用通配符
也可能存在其他部分内容,但这些部分没有得到跨手册页的标准化。常见的例子包括:OPTIONS(选项),EXIT STATUS(退出状态),ENVIRONMENT(环境),BUGS(程序漏洞),FILES(文件),AUTHOR(作者),REPORTING BUGS(已知漏洞),HISTORY(历史)和COPYRIGHT(版权)。
通常 man 手册中的内容很多,你可能不太容易找到你想要的结果,不过幸运的是你可以在 man 中使用搜索,/<你要搜索的关键字>
,查找到后你可以使用n
键切换到下一个关键字所在处,shift+n
为上一个关键字所在处。使用Space
(空格键)翻页,Enter
(回车键)向下滚动一行,或者使用j
,k
(vim编辑器的移动键)进行向前向后滚动一行。按下h
键为显示使用帮助(因为man使用less作为阅读器,实为less
工具的帮助),按下q
退出。
六种模式:
二:进入vim模式
:set nu 显示行号
:set ai 自动缩行
:set ts=4 设置一个 TAB 键等于几个空格
移动光标
[[ 转到上一个位于第一列的"{"
]] 转到下一个位于第一列的"{"
{ 转到上一个空行
} 转到下一个空行
gd 转到当前光标所指的局部变量的定义
3.gcc
应该掌握的调试命令有:
第三周
无符号
基于传统二进制表示法,表示大于或者等于零的数字。
补码
表示有符号整数的最常见方式
浮点数
表示实数的科学计数法的以二为基数的版本。
2.信息存储
1.进制
二进制、八进制、十进制、十六进制(转换:以二进制作为中间变量)
十六进制:以0x或0X开头表示,字符A-F可大写可小写。
2.字
虚拟地址是以这样的一个字来编码的。
每台计算机都有一个字长,指明整数和指针数据的大小。
字长决定虚拟地址空间的最大大小,字长决定的最重要的系统参数就是虚拟地址空间的最大大小。对一个字长为w位的机器,虚拟地址的范围为0-2^(w-1),程序最多访问2^w个字节。
C语言支持整数和浮点数的多种数据格式
小端法——在存储器中按照从最低有效字节到最高有效字节的顺序存储对象。
大端法——从最高有效字节到最低有效字节的顺序存储。
(小端法:高对高,低对低。大端法:高对低,低对高。)
举例:变量x十六进制值为0x01234567,
寻址和字节顺序
小端法:最低有效字节在最前面,“高对高,低对低”,是大多数intel兼容机,包括IBM和Sun的个人intel兼容处理器的计算机使用的规则。
例如:变量x为int,位于地址0x100,十六进制值为0x01234567。
小端法:
地址: 0x100 0x101 0x102 0x103
67 45 23 0
在0x01234567中,高位字节为0x01,低位字节为0x67。
PS:字节顺序是网络编程的基础。
5.布尔代数
掩码表示的是设置为有效信号的集合。
|:或 &:与 ~:取反 ^:异或 确定一个位级表达式的结果的最好的方法就是:将十六进制的参数扩展成二进制表示并执行二进制运算,然后在转换为十六进制。 w 位级运算的常用算法:掩码运算。 掩码运算:掩码是一个位模式,表示从一个字中选出的位的集合。例如:位级运算x&0xFF生成一个有x的最低有效字节组成的值。表达式~0将生成一个全1的掩码,不管机器的字大小是多少。
逻辑运算符:||(或)、&&(与)、!(非) 逻辑运算认为所有非零的参数都表示TRUE,参数0表示FALSE。返回1或者0,分别表示结果为TRUE或FALSE。 逻辑运算和位级运算的区别是: 按位运算只有在特殊情况下,即参数被限制为0或者1时,才能与其对应的逻辑运算有相同的行为; 若第一个参数求值就能确定表达式的结果,那么逻辑运算符就不会对第二个参数求值。
左移k位:丢弃最高位的k位,右端补k个0 右移包括:逻辑右移、算数右移 逻辑右移:左端补k个0(常用于无符号数) 算数右移:左端补k个最高有效位的值(用于有符号数)
补码形式是最常见的有符号数的计算机表示方式 将字的最高有效位解释为负权 B2T(W)函数为:B2T(x) = -x(w-1)2^(w-1)+∑xi2^i(求和从i=0到i=w-2)
处理同样字长的有符号数和无符号数之间相互转换的一般规则:数值可能会改变,但是位模式不变。 c语言允许无符号数和有符号数之间的转换。转换的原则是底层的位表示不变。 当从无符号数转换为有符号数是,效果是应用函数U2T,从有符号数转化为无符号数时,应用函数T2U,其中w表示数据类型的位数。 负数和正数相等的情况:u=2147483648 =-2147483648 (当输出分别为无符号形式和有符号形式时)
零扩展:将无符号数转换为更大的数在表示的开头添加0 符号扩展:将一个补码数字转换为一个更大的数据类型
截断数字:不用额外的位来扩展一个数值,而是减少表示一个数字的位数。
无符号运算可以被视为一种模运算形式,无符号加法等同于计算和摸上2^w,可以通过简单的丢弃x+y的w+1位表示的最高位,来计算这个数值。 一个算数运算的溢出,是指完整的整数结果不能放到数据类型的字长限制中去。
两个数的w位补码之和与无符号之和有完全相同的位级表示。大多数计算机用相同的机器指令来执行无符号或者有符号加法。 有符号加法的结果z=x+y可分为4种情况: (1)-2^(w)≤z<-2^(w-1):两个负数相加得一个非负的结果。 (2)-2^(w-1)≤z<0:结果正常,z为负数 (3)0≤z<2^(w-1):结果正常,z为正数 (4)2^(w-1)≤z<2^(w):两个正数相加得一个负数的结果。
对于范围-2^(w-1)≤x<-2^(w-1)内的x,补码的非运算如下: (1)x=-2^(w-1):补码的非为-2^(w-1); (2)x>-2^(w-1):补码的非为-x。 求位级补码非的方法: (1)对每一位求补,再对结果加1;(2)建立在将位向量分为两部分的基础之上的。
两个数x、y相乘且x、y的位数为w,则结果的位数为2w。
同无符号乘法。 若为截断后的结果,则取结果的后w位作为计算结果。 注意:无符号运算和补码运算在“+”、“-”、“*”在位级上有相同的结果。
对于某个常数K的表达式x*K生成代码,编译器会将K的二进制表示表达为一组0或1的交替的序列: [(0…0)(1…1)(0…0)…(1…1)] 可以用以下两种形式来计算这些乘积的结果: A:(x<<n)+(x<<n-1)+……+(x<<m) B:(x<<n+1)-(x<<m) 注意:对于n为最高位的情况,B:-(x<<m)
设x/K,令K=2^n, 当x为正数时,计算 x>>n; 当x为负数时,将x加上偏置量,即加上2^n-1(即K-1),计算** (x+偏置量)>>n**。
计算机执行的“整数”运算实际上是一种模运算形式; 表示数字的有限字长限制来了可能的值的取值范围,运算结果可能溢出; 补码表示提供了一种即能表示负数也能表示正数的灵活方法,同时使用了与执行无符号算术相同的位级实现; c语言中的unsigned数据类型的使用也应当特别注意,比如,在书写整数常数和调用库函数的时候。
定点表示法:“.”为界(不能有效的表示很大的数)
十进制:小数点左边的数字的权是10的非负幂,得到整数值;右边的数字的权是10的负幂,得到小数值。
二进制:小数点左边的数字的权是2的非负幂,右边的数字的权是2的负幂。
符号:s决定这个数是负数(s = 1)还是正数(s = 0),而对于数值0的符号位解释作为特殊情况处理。
尾数:M是一个二进制小数,它的范围是1 ~ 2-ε,或者是0 ~ 1-ε。
阶码:E的作用是对浮点数据加权,这个权重是2的E次幂(可能是负数)。
一个单独的符号位s直接编码符号s。
k位的阶码字段exp = ek-1…e1e0编码阶码E。
n位小数字段frac = fn-1…f1f0编码尾数M,但是编码出来的值也依赖于阶码字段的值是否等于0。
C语言中的单精度浮点格式float 和双精度浮点格式double。
在float中,s、exp和frac字段分别为1位、k = 8 位和n = 23位,得到一个32位的表示;
在double中,s、exp和frac字段分别为1位、k = 11 位和n = 52位,得到一个64位的表示。
第四周
1、指令集体系结构(Instruction set architecture ISA)
它定义了处理器状态、指令的格式,以及每条指令对状态的影响。
IA32将程序的行为描述成好像每条指令时按顺序执行的,一条指令结束后,下一条再开始。(实际上处理器并发地执行许多指令,但是可以采取措施保证整体行为与ISA指定的顺序执行完全一致)
2、机器级程序使用的存储器地址是虚拟地址
提供的存储器模型看上去是一个非常大的字节数组。存储器系统的实际实现是将多个硬件存储器和操作系统软件组合起来。
程序存储器(program memory)包含:程序的可执行机器代码、操作系统需要的一些信息、栈、堆。程序存储器用虚拟地址来寻址(此虚拟地址不是机器级虚拟地址)。操作系统负责管理虚拟地址空间(程序级虚拟地址),将虚拟地址翻译成实际处理器存储器中的物理地址(机器级虚拟地址)。
p107:
gcc -S xxx.c -o xxx.s 获得汇编代码,也可以用objdump -d xxx 反汇编;
64位机器:gcc -m32 -S xxx.c
注意:MAC OS中没有objdump, 有个基本等价的命令otool Ubuntu中 gcc -S code.c (不带-O1)
p108:
二进制文件可以用od 命令查看,也可以用gdb的x命令查看。 我们可以使用 more或less命令结合管道查看,也可以使用输出重定向来查看 od code.o | more od code.o > code.txt
p109-p111:
两种格式:ATT格式和Intel格式 表中不同数据的汇编代码后缀
p112:
EAX、ECX、EDX、EBX:為ax,bx,cx,dx的延伸,各為32位元 ESI、EDI、ESP、EBP:為si,di,sp,bp的延伸,32位元
eax, ebx, ecx, edx, esi, edi, ebp, esp等都是X86 汇编语言中CPU上的通用寄存器的名称,是32位的寄存器。如果用C语言来解释,可以把这些寄存器当作变量看待。
比方说:add eax,-2 ; //可以认为是给变量eax加上-2这样的一个值。
这些32位寄存器有多种用途,但每一个都有“专长”,有各自的特别之处。
EAX 是"累加器"(accumulator), 它是很多加法乘法指令的缺省寄存器。
EBX 是"基地址"(base)寄存器, 在内存寻址时存放基地址。
ECX 是计数器(counter), 是重复(REP)前缀指令和LOOP指令的内定计数器。
EDX 则总是被用来放整数除法产生的余数。
ESI/EDI分别叫做"源/目标索引寄存器"(source/destination index),因为在很多字符串操作指令中, DS:ESI指向源串,而ES:EDI指向目标串.
EBP是"基址指针"(BASE POINTER), 它最经常被用作高级语言函数调用的"框架指针"(frame pointer). 在破解的时候,经常可以看见一个标准的函数起始代码: push ebp ;保存当前ebp mov ebp,esp ;EBP设为当前堆栈指针 sub esp, xxx ;预留xxx字节给函数临时变量. ... 这样一来,EBP 构成了该函数的一个框架, 在EBP上方分别是原来的EBP, 返回地址和参数. EBP下方则是临时变量. 函数返回时作 mov esp,ebp/pop ebp/ret 即可.
ESP 专门用作堆栈指针,被形象地称为栈顶指针,堆栈的顶部是地址小的区域,压入堆栈的数据越多,ESP也就越来越小。在32位平台上,ESP每次减少4字节。 esi edi可以用来操纵数组,esp ebp用来操纵栈帧。
p113:
一 、立即寻址方式(immediate addressing)
二 、寄存器寻址方式(register addressing)
三 、直接寻址方式(direct addressing)
四 、寄存器间接寻址方式(register indirect addressing)
五 、寄存器相对寻址方式(register relative addressing)
六 、基址变址寻址方式(based indexed addressing)
七 、相对基址变址寻址方式(relative based indexed addressing)
p114:
注意ATT格式中的方向,
不能从内存地址直接MOV到另一个内存地址,要用寄存器中转一下。能区分MOV,MOVS,MOVZ,掌握push,pop p115/p116:
栈帧与push pop; 注意栈顶元素的地址是所有栈中元素地址中最低的。 p117:
指针就是地址;局部变量保存在寄存器中。 p119:
结合表理解一下算术和逻辑运算, 注意目的操作数都是什么类型 注意减法 p123:
结合C语言理解一下控制部分,也就是分支(if/switch),循环语句(while, for)如何实现的。
p124:
条件码寄存器
注意leal不改变条件码寄存器 p125:
SET指令根据t=a-b的结果设置条件码 p127:
跳转指令(导致执行切换到程序中一个全新的位置,跳转的目的地通常用一个标号指明)
无条件跳转:JMP 可以是直接跳转也可以是间接跳转(写法是*后面加操作数指示符)
有条件跳转:根据条件码的某个组合,或者跳转或者继续执行下一条指令
p130/p131:
if-else 的汇编结构 p132/p133:语句的使用
三种语句:
条件分支:if--else
循环结构:do--while、while、for
switch语句
p149:
IA32通过栈来实现过程调用。掌握栈帧结构,注意函数参数的压栈顺序. p150/p151:
call/ret; 函数返回值存在%eax中 p174:
1.在栈帧之间切换
GDB中有很多针对调用堆栈的命令,都需要一个目标栈帧,例如打印局部变量值的命令。
frame args 将当前栈帧设置为args(编号或Address)指定的栈帧,并打印该栈帧的简要信息。
select-frame args 与frame args相同,但是不打印栈帧信息。
up n 向上回退n个栈帧(更外层),n默认为1.
down n 向下前进n个栈帧(更内层),n默认为1.
up-silently n 与up n相同,但是不打印信息。
down-silently n 与down n相同,但是不打印信息。
2.打印栈帧信息(不移动栈帧)
frame 打印当前栈帧的简要信息。
info frame 打印当前栈帧的详细信息。
info frame args 打印指定栈帧的详细信息。
info args 打印函数参数信息。
info locals 打印当前可访问的局部变量的信息。
3.打印调用堆栈
backtrace 打印全部栈帧的简要信息,按Ctrl-c可终止打印。
backtrace n 打印最内层的n个栈帧的简要信息。
backtrace -n 打印最外层的n个栈帧的简要信息。
backtrace full 打印全部栈帧的详细信息。
backtrace full n 打印最内层的n个栈帧的详细信息。
backtrace full -n 打印最外层的n个栈帧的详细信息。
4.一些配置项
set backtrace past-main on 对调用堆栈的打印可越过main函数。 set backtrace past-main off 对调用堆栈的打印止步于main函数。
bt/frame/up/down :关于栈帧的gdb命令
第五周
程序员可见状态
(1)8个程序寄存器,%eax,%ecx,%edx,%ebx,%esi,%edi,%esp,%ebp
(2)处理器的每个程序寄存器存储一个字
(3)寄存器%esp被入栈、出栈、调用和返回指令作为栈指针。在其他情况下,寄存器没有固定的含义或固定值。
(4)有三个一位的条件码:ZF,SF,OF,它们保存最近的算术或洛基指令所造成英雄的有关信息。
(5)程序计数器PC存放当前正在执行指令的地址。
(6)存储器:Y86程序用虚拟地址来引用存储器位置,硬件和操作系统软件联合起来将虚拟地址翻译成实际或物理地址
(7)状态码stat表明程序执行的总体状态,会指示是正常运行还是出现了某种异常。
六个基本阶段:取指;译码;执行;访存;写回;更新PC
第六周
局部性:空间、时间
步长增加,空间局部性下降;
循环有好的时间和空间局部性。循环体越小,循环迭代次数越多,局部性越好。
缓存不命中(替换策略):冷不命中、冲突不命中、容量不命中
高速缓存存储器结构(S,E,B,m)
收获: 这次国培对我来说无疑是一场及时雨,让我受益匪浅。娄老师的创设适合学生的教育,犹如一缕缕灿烂的阳光,让人温暖,使人感动,催人奋进,老师的教学设计使我茅塞顿开,讲授的课堂教学改革帮助我解决了在教学方面存在的困惑。更新了我教学思想的一些观念,使我更加深刻的了解到新课程的基本理念,让我学到了很多东西。在开始的时候对Linux完全不了解,但是再一次次试验和实践中,学会了基本使用vim等,学会了GCCGDB两种模式等等。不知不觉学期已过半,对这门课的情绪一直在变化。但是随着一周又一周过去,我渐渐习惯了这种每周都花固定时间来学习的生活。实话说大学以来我的学习能力下降了太多太多,积极性主动性什么的更是几乎消磨殆尽,而这种“使自主”学习方式,让我每天都想着还要学习还要学习,这种态度的转变我觉得是最大的收获。
不足:自己还是有一些懈怠,有些东西没有落实,反思自己,我觉得我最大的问题还是自制力差,容易分心,遇到问题容易烦躁然后思维就转移了不专注了。现在跟着老师的新方式学习有些改观,加上自己的克制比如去学习不带手机,可以说比之前好很多。我觉得连续学习两小时中第二个小时要比第一个小时效率高很多,在以后的学习中要继续克制继续克制,多利用第二个小时。
老师这种教学改革刚开始感觉很煎熬,但一旦习惯了才发现这样才是真正的事半功倍。如果不是这种每周任务制,大部分人到了期末都是恶补,不仅熬夜耗时间耗精力重点是还学不到东西。而现在这种方式大家的学习积极性都很高,主动抢题主动查实验,做的任务多得分就高,公平又高效。