Unix的开创者之一丹尼斯-里奇,同时也是C语言的老祖宗。他把c语言引入了unix,从而开辟了一个崭新的时代。
C语言和unix的关系,真真称的上是鱼水情深,密不可分。毫不夸张的说,是c语言成就了unix,而同时也是
unix成就了c语言。
大家对这门语言自然是十分熟悉,但在这里我还是要多说几句。
首先,我们所分析的代码写于70年代初中期,其时c语言也刚刚成型,其语法与现在的c语言稍有不同,比如:
(1) +=、-=、/=等运算符的写法不同,记为=+、=-、=/等等;
(2) 看起来当时还没有long这一数据类型。因此,在记录一些可能会超出1个word(16 bits)的数据时,
往往会使用2个变量来进行存放。比如,对文件size,在struct inode中使用了两个变量:
5611: char i_size0; //文件size高8 bits
5612: char *i_size1; //文件size低16 bits
还有一种常见的情形是使用两个word来模拟一个32 bits数。unix甚至提供了一套函数来完成这类数
的加、减等算术操作。
(3) 现代c语言是个强类型语言,而当时的c语言类型没有现在这么强。对指针而言,尤其如此。而对指针
类型放松类型校验的结果是使指针的使用极其灵活高效,进行低层操作时显得得心应手。如下面的例子:
0164: #define PS 0177776 // 0177776即为PS寄存器的逻辑地址
//源码中有大量此类device register的定义,其特点是
//都位于第8个逻辑page(会被映射到高地址空间)
0175: struct { int integ; };
2070: s = PS->integ;
PS仅是一个常数地址,无需定义就可以按指针方式进行使用,而通过一个无名strut,PS获得了
指针类型——指向integer的指针。
最后来看一下函数的call和Return时通用寄存器的保存和恢复,这部分内容在书中的10.6小节,有简洁的说明,
请先翻到该小节,看看莱昂的描述。下面我举例说明一下函数调用期间栈的变化情况。为简单起见,我们使
用一个没有参数的函数来进行说明。假设有一个C语句定义的函数fun(),在调用此函数时:
(1) jsr pc, func
(2) func函数开头会调用jsr r5, csv
(3) csv代码如下:
1420: csv:
1421: mov r5,r0
1422: mov sp,r5
1423: mov r4,-(sp)
1424: mov r3,-(sp)
1425: mov r2,-(sp)
1426: jsr pc,(r0)
执行完毕后,栈图如下所示:
(4) 从func中返回时:
jsr ??, cret ??为某寄存器
1430: cret:
1431: mov r5,r1
1432: mov -(r1),r4 /恢复r4
1433: mov -(r1),r3 /恢复r3
1434: mov -(r1),r2 /恢复r2
1435: mov r5,sp
1436: mov (sp)+,r5 /恢复r5
1437: rts pc /return
博客地址: http://blog.csdn.net/cszhao1980