fishcrazy 发表于 2003-1-22 00:10:45

内核阅读日记[1.18]

文件prf.c

此文件包含以下函数:
printf()        panic()
printn()        prdev()
putchar()        deverror()

它们存在如下的调用关系:

panicdeverror
|      |
|    predev
|      |
   \    /
   printf
   |
   printn
   |
   putchar

function printf() 给操作系统提供了一种向系统控制台终端发送消息的方法;直接、不带缓存;
在系统初启、硬件出错、系统突然崩溃时使用.

/*
* Scaled down version of C Library printf.
* Only %s %l %d (==%l) %o are recognized.
* Used to print diasnostic information
* directly on console tty.
* Since it is not interrupt driven,
* all sysytem activities are pretty much
* suppended.
* printf should not be used for chit-chat.
*/
printf(fmt, x1, x2, x3, x4, x5, x6, x7, x8, x9, xa, xb, xc)
/* 只用到了fmt和x1两个参数 */
char fmt[]; /* 这个定义类似于 char *fmt; 不同的是 char fmt[]; 中,fmt[]的值是不可以改变的 */
{
   register char *s;
   register *adx, c;
   
   adx = &x1; /* x1占用的是栈单元,编译时不能对它求值,所以让adx指向x1 */
loop:                                                /* while(1) { */
   while((c = *fmt++) != '%') {
            if(c == '\0')
               return;
            putchar(c);
   }
   c = *fmt++; /* 判断格式 */
   if(c == 'd' || c == 'l' || c == 'o')
            printn(*adx, c == 'o'? 8:10);/* 八进制 或 十进制 */
   if(c == 's') {
            s = *adx;
            while(c = *s++)
                      putchar(c);
   }
   adx++;
   goto loop;                                    /* } */
}
/* ------------------------------ */



printn() 按照所要求的顺序产生相应数字字符

/*
* Print an unsigned integer in base b
*/

printn(n, b)
{
   register a;
   
   if(a = ldiv(n, b))
          printn(a, b);
   putchar(lrem(n, b) + '0');
}

/* ------------------------------ */
.globl_ldiv
_ldiv
   clrr0
   mov2(sp), r1
   div4(sp), r0
   rtspc
/* 似乎是对ldiv的定义 */

.globl_lrem
_lrem
   clrr0
   mov2(sp), r1
   div4(sp), r0
   movr1, r0
   rtspc
/* ------------------------------ */


因为这个系统是在PDP11计算机上运行的,所以
处理器包含了一组16位的寄存器
r0、r1、r2、r3、r4、r5、r6、r7 是通用寄存器
其中前六个可以作为累加器、地址指针或变址寄存器
一般来说:
r0、r1在表达式求值时用做临时累加器;在函数返回时存放返回值;在函数调用的某些情况下可用来传递实参。
r2、r3和r4在函数执行时可用做局部变量;在函数调用入口处存储这些寄存器的值,在退出时恢复它们的值。
r5用作函数激活记录动态链的链首指针,该动态链存放在当前栈上。r5被成为“环境指针”。
r6(也称sp)用作栈指针
r7(也称pc)程序计数器,即指令地址寄存器。

汇编指令:
adc 将C位的内容加至目的操作数
add 将源操作数加至目的操作数
ash 按移位记数将指定寄存器的内容左移相应次数
ashc 功能与ash相同,但是涉及两个寄存器
asl 所有位左移1位,第0位装入0,第15位装入C
asr 所有位右移1位,第15位保持原值,第0位装入C
beq 等于转移
bge 大于或等于转移
bhi 高于转移
bhis 高于或相同转移
bic 若源操作数的位非0,则将目的操作数中的相应位清0
bis 对源操作数和目的操作数执行或操作,将结果存入目的操作数
bit 对源操作数和目的操作数执行逻辑与操作,设置条件码
ble 大于或等于转移
blo 低于转移
bne 不等于转移
br 转移到.-128和.+127之间的一个单元,.代表当前地址
clc 清除C
clr 目的操作数清0
cmp 比较源操作数和目的操作数。设置条件码
dec 将目的操作数减1
div 存在rn和r(n+1)中的32位二进制补码整数被除以源操作数,商在rn中,余数在r(n+1)中
inc 将目的操作数加1
jmp 跳转到目的操作数
jsr 跳转到子程序
mtpi 从当前栈弹出一个字,将其存入当前地址空间中的指定字节中
mov 将源操作数复制到目的操作数
mfpi 将前地址空间中指定字的值压入当前栈

motor98 发表于 2003-1-22 02:54:07

这是C语言的库, 还是linux内核? ;)

fishcrazy 发表于 2003-1-22 15:53:23

Linux有两种运行态:核心态和用户态。这里的printf和putchar是在核心

态下运行,类似于C程序调用的库函数printf和putchar,但是却不能等

同;后者是在用户态下运行的,位于/lib/libc.a中

motor98 发表于 2003-1-23 03:18:01

什么意思呀? 你是说这段printf代码是被静态地编译在vmlinuz中吗?

至于用户态和核心态, UNIX操作系统是有用户态和核心态, 但这只是操作系统的保护机制.用户在用户态下, 通过UNIX系统调用, 就可以进入核心态. 在核心态下, 操作系统可以替这个用户执行很多操作, 如果这些都要被编译进内核的话, 内核会变得非常臃肿.  

不过你说的也有可能, 很多以前的操作系统都不是'微内核'结构, 所以都很庞大, 难道LINUX也是这样?

flyaway 发表于 2003-1-23 08:58:36

非常高兴有人已经开始了!
希望能够将分析的代码在内核中的位置列出来,
使别人能更快地定位,否则会让很多人只见树木而不见森林!
谢谢!

fishcrazy 发表于 2003-1-23 18:10:24

我手上的这份源码只有10000行,你说是大还是小呢?

这些代码都是核心代码,是常驻内存的部分。

printf()是由系统控制台调用,向终端输出系统信息时使用的,

包括初启等都有用到,所以它是在启动时编译一次就OK了

motor98 发表于 2003-1-28 04:09:19

我没有你的这份源码, 但首先, 一个操作系统是不是微内核结构不是由他的行数决定的. 我说的是操作系统的体系结构. 听说过CMU的MACH吗?
其次, 长驻内存不一定是操作系统内核, 因此这点并不能支持你的观点;)
另外, 我不明白你说的"启动时编译一次"是什么意思, 难道操作系统boot时还要先编译吗?那么我的操作系统如果没有编译器怎么办? ;)

jackzheng2002 发表于 2003-2-15 11:24:08

:lol:

motor98 发表于 2003-2-18 03:34:11

最近看了一些linux kernel方面的文章, 发现linux确实和别的unix不太一样. 简单说, 确实, 象控制台输出这样的function是编译在内核中的.

但是, 它的源代码应该是属于glibc函数库的, 所以, 研究linux内核的话, 应该可以将其忽略啦. ;)

noble_shi 发表于 2003-2-18 16:34:48

这些应该是linux C函数库把

吴文官 发表于 2003-2-18 18:31:35

你所读的应该不是Linux的内核,是Unix的内核,Linux好象没有运行在PDP11上过。

这也不是C函数库,这是系统启动时在标准输出上输出启动信息的方式 。

motor98 发表于 2003-2-21 03:06:08

这就对了, 我觉得这是glibc的源码, 因为glibc应该在PDP11上有移植.
linux在它的vmlinuz(核心)中用了glibc的源码, 应该是这样吧.

motor98 发表于 2003-2-21 03:09:27

当然啦, linux_i386应该用的是glibc_i386.

stephenhu 发表于 2003-3-20 11:05:08

确实是unix代码

你所读的应该不是Linux的内核,是Unix的内核,Linux好象没有运行在PDP11上过。

这也不是C函数库,这是系统启动时在标准输出上输出启动信息的方式 。
unix6,很好,很经典的系统内核,不过有些过时,但作为源码阅读还是很能感觉到它的精致的。

fishcrazy 发表于 2003-3-30 10:14:35

谢谢大家的提醒,UNIX和Linux虽然不是一样,但它在很多方面都相类似,难道内核代码

的差别会很大吗?
页: [1] 2
查看完整版本: 内核阅读日记[1.18]