20145304 《信息安全系统设计基础》期中总结
Linux命令
command [options] [arguments]
man
man (选项) 命令
man -k
相当于编程时的搜索引擎,结合grep命令和管道,可以多关键字查找。 cheat
cheat命令是在GNU通用公共许可证下,为Linux命令行用户发行的交互式备忘单应用程序。它提供显示Linux命令使用案例,包括该命令所有的选项和简短但尚可理解的功能。
grep
管道是一种通信机制,通常用于进程间的通信(也可通过socket进行网络通信),它表现出来的形式就是将前面每一个进程的输出(stdout)直接作为下一个进程的输入(stdin)。
grep [命令选项]... 用于匹配的表达式 [文件]...
grep命令用于打印输出文本中匹配的模式串,它使用正则表达式作为模式匹配的条件。grep支持三种正则表达式引擎,分别用三个参数指定: -E POSIX扩展正则表达式,ERE -G POSIX基本正则表达式,BRE -P Perl正则表达式,PCRE - 用grep命令查找宏
grep -nr /usr/include
用户权限管理
- 查看用户
who am i
- sudo命令的两大前提:一是要知道当前登录用户的密码,二是当前用户必须在 sudo 用户组。
- 创建用户
sudo adduser 用户名
- 查看用户属于的用户组:
groups 用户名或 cat /etc/group | sort
- 将其它用户加入 sudo 用户组:
$ su 当前用户 //获得root权限$ groups 待添加用户$ sudo usermod -G sudo 待添加用户 //将用户添加到sudo用户组$ groups 待添加用户
- 删除用户
sudo deluser 待删除用户 --remove-home
Linux目录结构及文件基本操作
- FHS(英文:Filesystem Hierarchy Standard 中文:文件系统层次结构标准)。FHS 定义了两层规范,第一层是, / 下面的各个目录应该要放什么文件数据,例如 /etc 应该要放置设置文件,/bin 与 /sbin 则应该要放置可执行文件等等。第二层则是针对 /usr 及 /var 这两个目录的子目录来定义。例如 /var/log 放置系统登录文件、/usr/share 放置共享数据等等。
- 使用 cd 命令可以切换目录,在 Linux 里面使用 . 表示当前目录,.. 表示上一级目录,- 表示上一次所在目录,~ 通常表示当前用户的"home"目录。使用 pwd 命令可以获取当前所在路径(绝对路径)。
- 新建文件
touch 文件名
- 新建目录
mkdir 目录名
使用 -p 参数,同时创建父目录(如果不存在该父目录)mkdir -p father/son/grandson
- 复制
cp 文件名 指定目录
要成功复制目录需要加上-r或者-R参数,表示递归复制cp -r father family
- 删除文件
rm test
-f参数强制删除,要删除一个目录,需要加上-r或-R参数。 - 移动文件
mv 源目录文件 目的目录
- 重命名文件
mv 旧的文件名 新的文件名
正则表达式
- 基本语法:一个正则表达式通常被称为一个模式(pattern),为用来描述或者匹配一系列符合某个句法规则的字符串。 选择:|竖直分隔符表示选择,例如"boy|girl"可以匹配"boy"或者"girl" 数量限定: +表示前面的字符必须出现至少一次(1次或多次) ?表示前面的字符最多出现一次(0次或1次) ·代表前面的字符可以不出现,也可以出现一次或者多次(0次、或1次、或多次) 范围和优先级:()圆括号可以用来定义模式字符串的范围和优先级,这可以简单的理解为是否将括号内的模式串作为一个整体。
- 特殊字符
字符 描述\ 将下一个字符标记为一个特殊字符、或一个原义字符。^ 匹配输入字符串的开始位置。$ 匹配输入字符串的结束位置。{n} n是一个非负整数。匹配确定的n次。{n,} n是一个非负整数。至少匹配n次。{n,m} m和n均为非负整数,其中n<=m。最少匹配n次且最多匹配m次。* 匹配前面的子表达式零次或多次。+ 匹配前面的子表达式一次或多次。? 匹配前面的子表达式零次或一次。[^xyz] 排除型(negate)字符集合。匹配未列出的任意字符。[a-z] 字符范围。匹配指定范围内的任意字符。[^a-z] 排除型的字符范围。匹配任何不在指定范围内的任意字符。
Linux环境下编程基础
vim
- 保存文档:从普通模式输入:进入命令行模式,输入w回车,保存文档。输入:w 文件名/文件路径 可以将文档另存为其他文件名或存到其它路径下。
- 普通模式下输入Shift+zz即可保存退出vim。
- 游标移动
h 左l 右(小写L)j 下k 上w 移动到下一个单词b 移动到上一个单词
- 插入模式
i 在当前光标处进行编辑I 在行首插入A 在行末插入a 在光标后插入编辑o 在当前行后插入一个新行O 在当前行前插入一个新行cw 替换从光标所在位置后到一个单词结尾的字符
- 删除文本(命令前加数字,表示一次删除多行)
x 删除游标所在的字符X 删除游标所在前一个字符Delete 同xdd 删除整行dw 删除一个单词(不适用中文)d$或D 删除至行尾d^ 删除至行首dG 删除到文档结尾处d1G 删至文档首部
- 退出vim
:q! 强制退出,不保存:q 退出:wq! 强制保存并退出:w <文件路径> 另存为:saveas 文件路径 另存为:x 保存并退出:wq 保存并退出 文件路径>
gcc
- 编译过程
- 预处理:gcc –E hello.c –o hello.i; gcc –E调用cpp 生成中间文件- 编 译:gcc –S hello.i –o hello.s; gcc –S调用ccl 翻译成汇编文件- 汇 编:gcc –c hello.s –o hello.o; gcc -c 调用as 翻译成可重定位目标文件- 链 接:gcc hello.o –o hello ; gcc -o 调用ld** 创建可执行目标文件
-c 只编译不链接,生成目标文件.o-S 只编译不汇编,生成汇编代码-E 只进行预编译,不做其他处理-g 在可执行程序中包含标准调试信息-o file 将file文件指定为输出文件-v 打印出编译器内部编译各过程的命令行信息和编译器的版本-I dir 在头文件的搜索路径列表中添加dir目录
- 静态链接库的生成:
gcc -c 文件名.c
ar rcsv libxxx.a xxx.o
静态库的使用:gcc -o 文件名
文件名.c -L. -lxxx
,-L
在库文件的搜索路径列表中添加dir目录,l
在头文件的搜索路径列表中添加dir目录。 - 共享库的生成:
gcc -fPIC -c xxx.c
gcc -shared -o libxxx.so xxx.o
共享库的使用:gcc -o main main.c -L. -lxxx
。
gdb
- 使用GCC编译时要加“-g”参数。
- 基本命令
gdb programm(启动GDB)b 设断点(4种断点:行断点、函数断点、条件断点、临时断点)run 开始运行程序bt 打印函数调用堆栈p 查看变量值c 从当前断点继续运行到下一个断点n 单步运行s 单步运行quit 退出GDB
- 四种断点
行断点 b [行数或函数名] <条件表达式> 函数断点 b [函数名] <条件表达式> 条件断点 b [行数或函数名]临时断点 tbreak [行数或函数名] <条件表达式>条件表达式> 条件表达式> 条件表达式>
Makefile基本规则
- 一般写法
test(目标文件): prog.o code.o(依赖文件列表)tab(至少一个tab的位置) gcc prog.o code.o -o test(命令)
- Makefile还可以定义和使用宏(也称做变量),从而使其更加自动化,更加灵活,在Makefile中定义宏的格式为:
macroname = macrotext
使用宏的格式为:$(macroname)
信息的表示和处理
- 缓冲区溢出漏洞:人为的溢出是有一定企图的,攻击者写一个超过缓冲区长度的字符串,植入到缓冲区这时可能会出现两种结果:一是过长的字符串覆盖了相邻的存储单元,引起程序运行失败,严重的可导致系统崩溃;另一个结果就是利用这种漏洞可以执行任意指令,甚至可以取得系统root特级权限。
- 最低有效字节在最前面的方式称为小端法,最高有效字节在最前面的方式称为大端法。字节顺序是网络编程的基础,小端是“高对高、低对低”,大端与之相反。
- 逻辑运算符和对应的位运算之间的重要区别是,如果对第一个参数求值就能确定表达式的结果,那么就不会对第二个参数求值,避免运算结果的错误。
- 逻辑右移和算术右移。逻辑右移在左端补k个0,得到的结果是[0,…,0,xn-1,xn-2,…,xk]。算术右移是在左端补k个最高有效位的值,得到的结果是[xn-1,…,xn-1,xn-1,xn-2,…,xk]。
整数表示
- 无符号数的编码:假设一个整数数据类型有w位。我们可以将位向量写成x→,表示整个向量,或者写成[xw-1 ,xw-2,…,x0],表示向量中的每一位。把x→看做一个二进制表示的数,就获得了x→的无符号表示。无符号二进制有一个很重要的属性,就是每个介于0~2^w-1之间的整数都有唯一一个w为的值编码,函数为一个双射。
- 补码编码:最常见的有符号数的计算机表示方式就是补码形式。在这个定义中,将字的最高有效位解释为负权。所能表示的数值范围[-2^(w-1)~2^(w-1)-1],在可表示的范围内每个数字 都有一个唯一的w位的补码编码,函数为一个双射。
- 反码:除了最高有效位的权是-(2w-1-1)而不是-2w-1,它和补码是一样的。
- 原码:最高有效位是符号位用来确定剩下的位应该取负权还是正权。
截断数字:将一个w位的数假设我们不用额外的位来扩展一个数值,而是减少表示一个数字的位数。x=[xw-1 ,xw-2,…,x0]截断为一个k位的数字时,会丢弃高w-k位,得到一个位向量[xk-1 ,xk-2,…,x0],截断一个数字可能会改变他的值——溢出的一种形式。
整数运算
- 无符号运算可以被视为一种模运算形式。
- 无符号加法等价于计算和模上2w。可以通过简单的丢弃x+y的w+1位表示的最高位,来计算这个数值。
C语言中的无符号乘法被定义为产生w位的值,就是2w位的整数乘积的低w位表示的值。可以看作等价于计算乘积模2w。
浮点数
IEEE浮点标准用V = (-1)^s × M × 2^E的形式来表示一个数:- 符号: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…f1 f0编码尾数M,但是编码出来的值也依赖于阶码字段的值是否等于0。
程序的机器级表示
编译过程
- C预处理器插入宏和头文件:gcc -E xxx.c -o xxx.i
- 编译器产生源代码的汇编代码:gcc -S xxx.i -o xxx.s
- 汇编器化成二进制目标代码:gcc -c xxx.s -o xxx.o
- 链接器生成最终可执行文件:gcc xxx. -o xxx
用objdump -d xxx.o -o xxx.s 反汇编
寄存器
一个IA32中央处理单元(CPU)包含一组8个存储32位值的寄存器。用来存储整数数据和指针。局部变量保存在寄存器中。
%eax %ax (%ah %al) 通用寄存器%ecx %cx (%ch %cl) 通用寄存器%edx %dx (%dh %dl) 通用寄存器%ebx %bx (%bh %bl) 通用寄存器%esi %si 用来操纵数组%edi %di 用来操纵数组%esp %sp 操纵栈帧%ebp %bp 操纵栈帧
-leal,从存储器读数据到寄存器,而从存储器引用的过程实际上是将有效地址写入到目的操作数。目的操作数必须是一个寄存器。
寻址方式
一个立即数偏移Imm,一个基址寄存器Eb,一个变址寄存器Ei,一个比例因子s(必须为1,2,4,8)有效地址计算为:Imm(Eb,Ei,s) = Imm + R[Eb] + R[Ei]*s
条件码
CF:进位标志,最近操作使高位产生进位,用来检测无符号操作数的溢出ZF:零标志,最近操作得出的结果为0SF:符号标志,最近操作得到的结果为负数OF:溢出标志,最近操作导致一个补码溢出-正溢出或负溢出。
- leal不改变条件码寄存器
CMP与SUB的区别:CMP也是根据两个操作数之差设置条件码,但只设置条件码而不更新目标寄存器。
栈帧结构
- 为单个过程分配的栈叫做栈帧,寄存器%ebp为帧指针,而寄存器指针%esp为栈指针,程序执行时栈指针移动,大多数信息的访问都是相对于帧指针。
- 栈向低地址方向增长,而栈指针%esp指向栈顶元素。
- call:目标是指明被调用过程起始的指令地址,效果是将返回地址入栈,并跳转到被调用过程的起始处。
- ret:从栈中弹出地址,并跳转到这个位置。
函数返回值存在%eax中。
处理器体系结构
- 指令的字节级编码规则:高4位为代码部分,低四位为功能部分,功能值只有在一组相关指令共用一个代码时才有用。
- 8个程序寄存器当中,每个都有相应的0~7的寄存器标识符。程序寄存器存在CPU中的一个寄存器文件中,这个文件就是一个小的、以寄存器ID作为地址的随机访问存储器。
- 附加寄存器指示符字节:指定一个或者两个寄存器。
- 实现一个数字系统需要的三个组成部分:计算对位进行操作的函数的组合逻辑、存储位的存储器元素,以及控制存储器元素更新的时钟信号。
- 多路复用函数是用情况表达式来描述的。
[ select1 : expr_1 select2 : expr_2 ...... selectk : expr_k ]
- 集合关系:判断集合关系的通用格式是
iexpr in {iexpr1,iexpr2,....iexprk}(其中被测试和待匹配的值均为整数表达式。)
Y86指令集的计算原则:处理器从来不需要为了完成一条指令的执行而去读由该指令更新了的状态。
存储器层次结构
- SRAM与DRAM的对比
SRAM DRAM不需要刷新 以纳秒为周期刷新存取速度快 存取速度慢对光电噪声不敏感 光电因素易导致电压改变晶体管多密集度低 电容小,密集度高功耗贵代价高 功耗低
- 传统的DRAM:芯片中的单元(位)被分成了d个超单元,每个超单元都由w个DRAM单元组成, 一个d*w的DRAM共存储dw位信息。超单元被组织成一个r行c列的长方形阵列,rc=d。每个超单元的地址用(i,j)来表示(从零开始)。设计成二维矩阵是为了降低芯片上地址引脚的数量。
- 总线:数据流通过称为总线的共享电子电路在处理器和DRAM之间传送。是一组并行的导线,能携带地址、数据和控制信号。
- 总线事务:CPU和主存之间的数据传送的过程。读事务,从主存传数据到CPU;写事务,从CPU传数据到主存。
- I/O桥:将系统总线的电子信号翻译成存储器总线的电子信号。系统总线连接CPU和I/O桥,控制总线连接I/O桥和主存。
- 如果断电,DRAM和SRAM都会丢失信息,非易失性存储器——只读存储器:ROM。ROM是以他们能够被重编程的次数和对他们重编程的机制来区分的。
- 磁盘操作:磁盘用读/写头来读写存储在磁性表面的位,而读写头连接到一个传动臂 一端,通过移动转动臂将读写头定位在磁道上的机械运动称为寻道。磁盘以扇区大小的块来读写数据,对扇区的访问时间有三个主要的组成部分:寻道时间、旋转时间、传送时间。
固态硬盘是一种基于闪存的存储技术。一个硬盘包由一个或者多个闪存芯片和内存翻译层组成,闪存芯片替代旋转磁盘中的机械驱动器,而闪存翻译层将对逻辑块的请求翻译成对底层物理设备的访问。
局部性
- 局部性原理:一个编写良好的计算机程序倾向于引用邻近于其他最近引用过的数据项,或者最近引用过的数据项本身。有良好局部性的程序比局部性差的程序运行的更快,在硬件层引入高速缓存存储器就体现了局部性原理。
- 重复引用同一个变量的程序有良好的时间局部性
- 对于具有步长为k的引用模式的程序,步长越小,空间局部性越好。
对于取指令来说,循环具有良好的时间和空间局部性。循环体越小,迭代次数越多局部性越好。
存储器层次结构
- 高速缓存是一个小而快速的存储设备,作为存储在更大、更慢的设备中的数据对象的缓冲区域。每一层存储器被划分成连续的数据对象片,称为块,每个块都有唯一的对象和名字。数据总是以块大小为传送单元在第k层和第k+1层之间来回拷贝。
- 缓存命中:当程序需要第k+1层的某个数据对象d时,首先在当前存储的第k层的一个块中查找d,如果d刚好在第k层中,则称为缓存命中。
- 缓存不命中:如果k层中没有缓存数据d,则称为缓存不命中,此时要从k+1层取出包含d的块,可能会覆盖(替换/驱逐)现在的一个块(牺牲块)。决定该替换哪个快是缓存的替换策略来控制的。(例如,随机替换策略/LRU策略)种类:(1)强制性不命中/冷不命中:第k层缓存是空的(冷缓存),只是短暂的状态,不会在反复访问存储器使得缓存暖身之后的稳定状态出现。(2)冲突不命中:第k+1层的第i块,必须放置在第k层的块(i mod 4)中,这种限制性的放置策略引起冲突不命中。
- 利用时间局部性: 一旦一个数据在第一次不命中时被拷贝到缓存中,我们就会期望后面对该目标有一系列的访问命中。
利用空间局部性:块通常包含多个数据对象,我们通常期望后面对该块中其他对象的访问能够补偿不命中后拷贝该块的花费。
高速缓存存储器
高速缓存的结构用元组(S,E,B,m)来描述,高速缓存的大小C = S * E * B。
链接
与静态库链接
静态库:将所有相关的目标模块打包成一个单独的文件,称为静态库。在unix系统中,静态库以一种称为存档的特殊文件格式存放在磁盘中。 为了创建该库,使用AR工具:
ar rcs libvector.a addvec.o multvec.o
为了创建可执行文件,需要编译链接输入文件*.o 和libvector.a:
gcc -O2 -c .cgcc -static -o p2 .o ./libvector.a
加载可执行目标文件
./p
与动态库链接
共享库:一个目标模块,在运行时可以加载到任意的存储器地址,并和一个在存储器中的程序链接起来。这个过程称为动态链接,是由一个叫做动态链接器的程序来执行的。
创建动态库:gcc -shared -fPIC -o libvector.so addvec.c multvec.c
将动态库链接到程序中:
gcc -o p2 *.c ./libvector.so
自己的收获
之前的一些学习,都是怎么使用计算机来实现一些功能和算法,只知其然而不知其所以然,出现问题时不清楚因为什么,解决时常常感到无从下手,通过这两个月的学习,渐渐深入了解了计算机系统,对计算机的工作过程有了更深入的了解,在看书的过程中会常常联系到之前使用计算机编程出现问题时解决的方法,发现原理原来是这样,对新知识有了较深的印象,对旧知识有了较好的理解,看书的过程像是一个探索的过程,突然间便会解决掉困扰自己已久的问题。在这两个月里,我还掌握了许多工具的使用,例如vi,gcc和gdb等,熟练掌握后,这些工具会大大方便我们编程。
自己的不足
通过这两个月的学习,我觉得自己在解决问题方面存在些不足,出现问题时,解决能力较差,总是跳开问题重新尝试,或是放之不理。我认为出现这种问题的原因是知识储存量不够,出现问题时不知为何出现,当然也就不知怎样解决,还是要多多学习,多多动手。
课程建议和意见
希望老师能在课堂上讲解一些在测试中大家错的较多的题目,有时某个题目做错并不是这一周学习不认真,而是在学习过程中并为碰到这类问题或是碰到了没有注意,当某一道题错的人较多时,说明它具有价值,我们并不知道那些题错的人多,也就无法重点关注,希望老师能讲解一下错的较多的题目。