Archive for 十一月, 2009

关于进程的疑问集

Posted on 十一月 29th, 2009 in 备忘 | 11 Comments »

  1. 如何在有限的空间(如内存和硬盘,可以将其看作一个大数组)内储存不定长度不定生命周期的个体(如进程或者文件)?
  2. 如何限制一个进程在运行时无法访问或修改其他进程的数据?
  3. 如何实现共享库或者系统调用?
  4. 能否把进程的调度、连续对象(字节、页)的分配抽象为纯粹的最优问题?

最近看几本linux内核的书,貌似开始弄明白了点门道。通过分页机制实现一个虚拟地址空间貌似就可以解决上述的问题。不过现在只了解其机制,而不解其策略。纸上谈兵显然还差的远。

大约去年这时候曾经问过FlowerCode一些关于分页的东西,当时fc说分页有点像文件系统,现在才越想越觉得有道理。后知后觉了一些,呵呵 :)

mips汇编入门

Posted on 十一月 3rd, 2009 in 笔记 | 19 Comments »

据说若要深入学习MIPS开发的话,《MIPS处理器设计透视》这书是必不可少的。不过若只是学习MIPS汇编,这书可能就不大合适了。汇编语言还是隐藏了CPU的很多细节,而这本书里讲的貌似就是这部分,在对汇编有所了解之后再来阅读可能要更好。

学习函数式语言的时候总是满足于看书,理解下语法语义即可,真正写的代码则少的可怜,不过确实“改变了编程的看法”,目的也算达到了。汇编就不行了,看书不够,一定得动手。所以学MIPS需要一个模拟器。pcspim应该是比较标准的了,不过感觉Mars可能要更好用(准确地说,Mars非常非常好用)。

MIPS是以优雅著称,据说即使是其竞争对手也如此认为。RISC么,32个寄存器,指令长度都一样。其中的指令大约这么三种形式:

j 1000
li $1, 10
add $1, $2,$3

差异就是各个参数的长度不同。如add指令的三个参数都只有两个位宽(0~255),每个参数表示一个寄存器。如果把指令看作函数,那参数就可以看作是有类型的。而MIPS的汇编器是很强大的(听说可以进行窥孔优化),像add $t0, $0, 10这样的指令会被汇编器翻译成addi $t0,$0,10。汇编器处理前后指令的对比可以在Mars中显示出来。

记几个helloworld吧,

求3的阶乘:

li $t0, 0
li $t1, 1
if_1:
add $t0, $t0, 1
mul $t1, $t1, $t0
bne $t0, 3, if_1

在Mars下可以看到寄存器的变化,最后$t1寄存器的值是6。

mips汇编的分支(branch)指令分b系(bne,beq,bgt等等)和j系(j, jr等),差别就是b系指令的跳转都是有条件的,而且地址在参数中指明,而j系的跳转都是无条件的。j系指令的地址长度更长,寻址范围要更大,所以远程跳转都是j。

输出Helloworld:

.text
.globl main
 
main:
li $v0, 4                     # just the print syscall in SPIM
la $a0, str
syscall
 
.data
str:
.asciiz "hello world"

这应该算个比较完整的汇编程序了。程序的可执行代码都是在.text段,数据在.data段。.globl指明程序的入口地址,那个:str指代的就是这段字符串的地址。字符串么,就是数组。数组不就是指针么。

其中这个syscall会与操作系统的不同而有差异。系统调用的号码由$v0指明,参数在$a系的寄存器中传递,返回值放回到$v0。这里调用的是spim实现的4号系统调用,即print string。

定义一个函数f_add,它可以将两个数相加:

.text
.globl main
 
f_add:
add $v0, $a0, $a1
jr $ra
 
main:
li $a0, 1
li $a1, 2
jal f_add
 
add $t0, $v0, $0
 
li $v0, 1
add $a0, $0, $t0
syscall

在使用jal指令的时候,它会把发生跳转的地址记录在$ra寄存器中。这样函数在结尾的时候就可以用jr返回原先的位置了。

使用寄存器传递参数的好处貌似就是约定了函数的调用规范,兼容性要更好。例如x86下的BASIC和C在参数传递时压栈的顺序貌似就是相反的。