正文之前
今天学的很尴尬,因为有事情,而且新认识了两个计算机学院的保研大佬,不得不感叹我找的导师之强,第一个去上交的,是被金老师推荐去的,听说是跟了目前亚洲第一人的一个做计算机系统的人,例外一个小大佬居然也是直接跟的金老师。。也就是说我们以后是同门。
前面随便问问计算机学院的情况:
- 学长:我有个高中同学在金老师手下念博士生,我帮你问问。。。;
- 学姐:我有个大学同学在金老师实验室读研究生,我给你推荐好友哈。。。;
- 大佬:金老师是我的助班和学业导师,他人很好的。。。;
- 小大佬:金老师跟我说还不急着选方向,(PS:“嗯?你也是金老师的研究生呀” ) 嗯,他也是我助班。。。(。。。。)。。。
正文
- 并行与指令:同步。
当不同的任务之间需要访问问一个位置的数据的时候,就会出现数据竞争的风险,这个时候急需要同步来处理,负责就会引起程序运行错误的结果。同步运行需要依赖于硬件提供的同步指令,可以由用户调用。主要是加锁和解锁的同步操作。要实现操作的原子性(不可被分割打断),需要由硬件对两个同时执行的交换操作(一种同步机制,通过交换原语实现)进行排序,一种可行的办法是:指令对,链接取数和条件存数。 关键就在于临时寄存器的特性!!这一点我也是写笔记才明白的!!!
again: addi $t0,$zero,1;
ll $t1,0($s1);
sc $t0,0($s1);
beq $t0,$zero,again;
add $s4,$zero,$t1
其中ll 和 sc 分别对应着链接取数和条件存数 这两个指令,$s1 中存放着我们的锁单元,对应着我们一种资源,当$t1 从锁单元中取出来值,如果有任何处理器插入改变了锁单元的值,指令都会将$t0 变为 0(诸位莫忘了 $t0是一个临时存储器,如果发生了别的处理器的操作,临时寄存器会被瞬间归零),那么这段指令就会重新执行。直到完全完成原子交换,$s1所指向的锁单元中的值与$s4完成 值的交换。
- 翻译并且执行程序
参照下面的图片:
链接器的工作分为三个步骤(这个我真不会,后面回头来看吧!):
- 将代码和数据模块象征性的放入内存
- 决定数据和指令标签的地址
- 修补内部和外部引用
加载器在UNIX系统中执行的工作步骤:
- 读取可执行文件头来确定代码段和数据段的大小
- 为正文和数据创建一个足够大的地址空间
- 将可执行文件中的指令和数据复制到内存中
- 把主程序的采纳数复制到栈顶
- 初始化机器寄存器,将栈顶指针指向的一个空位置
- 跳转到指令例程,将参数复制到参数寄存器并且调用程序的main函数,当main函数返回时,启动例程通过调用系统exit 终止程序。
- 以一个C交换程序作为例子
//C语言版本:
void swap(int v[],int k)
{
int temp;
temp = v[k];
v[k] = v[k+1];
v[k+1] = temp;
}
下面我们用以下常见的步骤将其手动翻译为汇编程序:
// 用$a0 $a1 来存储v的基址和k的值。 因为swap是叶过程(不会产生调用的过程),所以为temp分配唯一的临时寄存器$t0;
swap:
sll $t1,$a1,2; // 因为我们用字来存储数据,所以地址距离应该是4倍,所以要左移两位 ,等同与*4;
add $t1,$a0,$t1; // 把v[k]的位置传入进来 到$t0
lw $t0,0($t1); // 读取v[k]的值;
lw $t2,4($t1); // 读取v[k+1]的值;因为地址固定相差四位,所直接读取4($t1)即可
//交换两个值 用sw
sw $t2,0($t1);
sw $t0,4($t1);
//因为是叶过程,所以会被调用,就需要一个跳转调用者的返回指令
jr $ra;
- 以一个C排序程序(用冒泡法)作为例子
//C语言版本:
void sort(int v[],int n)
{
int i,j;
for(i=0;i<n;i+=1)
{
for(j=i-1;j>=0 && v[j]>v[j+1];j -= 1)
{
swap(v,j);
}
}
}
下面我们用以下常见的步骤将其手动翻译为汇编程序:
为sort 的两个参数分配寄存器为:$a0和$a1 为变量i,j 分配 $s0,$s1;首先,最外层的循环,初始化:
move $s0,$zero; // 其实这个是伪指令,是一种方便操作的方式,真实的代码应该是 add $s0,$zero,$zero;
然后在for中还有一个 i++的功能需要实现,那就是在末尾加上一个:
addi $s0,$s0,1;
然后第二个判断条件需要放在每一次循环的开头,如果i>=n 就会退出,否则就继续执行下去 采用小于则置位以及等于就跳转的命令,
for1tst: slt $t0,$s0,$a1; //如果i<n就把t0置位为1;否则为0 也就是跳出最外层循环。
beq $t0,$zero,exit1; //t0 等于0 跳转到退出。
综上,我们的第一层循环的总体结构就是:
move $s0,$zero;
for1tst: slt $t0,$s0,$a1;
beq $t0,$zero,exit1;
~~~~~~~body~~~~~~~~
addi $s0,$s0,1;
j for1tst;
exit1:
然后是第二层循环,类似的,先给定初始条件,然后准备好结束条件,同时对于swap要重新给定寄存器,或者是在进入swap之前把原来的被占用的寄存器的内容放到另外的寄存器,然后结束swap的时候在逆向的返回原来的值;
综合程序如下:(for中的对a0 a1 的引用换成了 s2(v的基址) s3(n) 方便读写)
//定义整个函数的基调:首先腾出四个地方来存放数据,当前这些寄存器可能内部有sort的调用者的数据,所以为了避免丢失,要把当前寄存器的值保存到堆栈中
sort:addi $sp,$sp,-20;
sw $ra,16($sp); //此处是将调用sort的调用者的位置保存到堆栈中;
sw $s3,12($sp);
sw $s2,8($sp);
sw $s1,4($sp);
sw $s0,0($sp);
// 在swap中要用到a0 a1 两个寄存器,所以先把其中的值保存起来比较好。
move $s2,$a0;
move $s3,$a1;
//对最外层for循环进行初始化,s0表示i值,s3表示n,t0 则是判定条件所需要的临时寄存器;
move $s0,$zero;
for1tst: slt $t0,$s0,$s3;
beq $t0,$zero,exit1;
//内部循环的内容,初始化s1表示j=i-1; 内层for循环,分别有两个判定条件判断是否进行内层循环,否则调到exit2 结束内层循环,满足条件就进入循环体,取出数值,然后t1表示v[k]的k,t2表示v[k]的位置,取出来v[k]和v[k+1],然后进行比较,如果v[k+1]<v[k] 就可以直接跳转到exit2,否则就执行swap程序
addi $s1,$s0,-1;
for2tst:slti $t0,$s1,0;
bne $t0,$zero,exit2;
sll $t1,$s1,2;
add $t2,$s2,$t1;
lw $t3,0($t2);
lw $t4,4($t2);
slt $t0,$t4,$t3;
beq $t0,$zero,exit2;
//执行swap前要先把需要的数据保存到指定的寄存器
move $a0,$s2;
move $a1,$s1;
jal swap;
//j=j-1然后继续进行内层的循环
addi $s1,$s0,-1;
j for2tst;
//如果一开始就结束了内层循环,那么就可以 i=i+1 后直接跳转到外层循环,进
exit2:addi $s0,$s0,1;
j for1tst;
//如果内层循环也已经结束了,那么sort函数也就可以返回数值给他的调用者了。并且,要对调用者的寄存器进行还原,不然会干扰程序的正常运作。
exit1:lw $s0,0($sp);
lw $s1,4($sp);
lw $s2,8($sp);
lw $s3,12($sp);
lw $ra,16($sp);
addi $sp,20;
// ra寄存器经过还原后指向调用sort的程序的下一指令,直接返回即可。
jr $ra;
//swap程序。
swap:
sll $t1,$a1,2;
add $t1,$a0,$t1;
lw $t0,0($t1);
lw $t2,4($t1);
sw $t2,0($t1);
sw $t0,4($t1);
jr $ra;
正文之后
今晚要进行推免的复试报名(其实对于本校的同学貌似就是水一波,但是是必须要走的流程 而且很紧急!)所以现在回到住处,写完文以后就要去找齐所有的证书,幸亏是前阵子统计加分的时候已经整理好了。所以直接拿着用就行了 但是!!为什么缺了一张四级证书!!我还要回去拿!!心疼