
莱昂氏unix源代码分析导读
文章平均质量分 80
即使到今天,《莱昂氏unix源代码分析》仍是最好的内核源码分析书籍之一,它以短短9000多行代码实现了一个复杂的操作系统,其代码质量之高令人叹为观止。希望本专栏能为读者的读码过程提供一点帮助。
cszhao1980
这个作者很懒,什么都没留下…
展开
-
(莱昂氏unix源代码分析导读-51) 交互式终端
1 DL11/KL11终端有很多种终端可供选择使用,在一台计算机上可能同时连接若干种不同类型的终端。在我们的模型里,使用DL11 / KL 11终端。 一个DL11/KL11终端拥有4个设备寄存器,分为接收和发送两组,可用如下结构表示:8016: struct klregs { 8017: int klrcsr; //接收状态寄存器8018: i原创 2013-09-21 09:05:07 · 4997 阅读 · 4 评论 -
(莱昂氏unix源代码分析导读-50)LP11行式打印机
LP11有两个设备寄存器:状态寄存器(lpsr)和数据缓冲寄存器(lpbuf),可通过以下结构进行访问:8812: #define LPADDR 01775148823: struct {8824: int lpsr;8825: int lpbuf;8826: }; (1). LPADDR.lpsr:状态寄存器i. 第15位(最高位):出错标记;原创 2013-08-17 13:16:42 · 3888 阅读 · 0 评论 -
(莱昂氏unix源代码分析导读-49) 字符缓冲区
同块设备一样,对字符设备的输入输出也是通过缓冲区来进行的。使用缓冲区有个额外的好处,即以缓冲区为界,函数可分为高低两个层次。低层函数负责与实际设备交互,而高层函数只与缓冲区打交道,只对缓冲区存取数据,这样可以蔽掉掉底层的许多细节。 对于字符缓冲区,有两个最重要结构,即cblock和clist。前者是缓冲区本身,后者则用作字符链表(队列)的头结点。莱昂在第23章中详细介绍了这两个原创 2013-08-02 12:28:14 · 3915 阅读 · 0 评论 -
(莱昂氏unix源代码分析导读-48) 字符设备
我们终于到达了本书的最后一章,它涉及的是慢速、面向字符外部设备的输入和输出。同块设备一样,unix v6使用一个“设备switch” struct来进行设备操控。原创 2013-07-15 12:42:34 · 3916 阅读 · 0 评论 -
(莱昂氏unix源代码分析导读-47) exec
by cszhao1980现在,我们已经储备了足够的知识,该吹响向EXEC sys call冲锋的号角了。exec是系统中最重要也是最复杂的系统调用之一,它的作用是执行指定的“可执行文件”。一般说来,exec与fork配合使用,fork生成一个新进程,而exec是新进程执行其应该执行的代码。 莱昂对exec有着比较详细的介绍,但很不幸,这些代码理解起来仍然困难重重。所以,我要在原创 2013-06-30 08:54:28 · 4985 阅读 · 0 评论 -
(莱昂氏unix源代码分析导读-46)权限、管道
by cszhao1980 1. 文件与权限控制进程u结构中,身份相关的信息有:0420: char u_uid; /* effective user id */0421: char u_gid; /* effective group id */0422: char u_ruid; /* real user id */0423: char u_rgid; /* rea原创 2013-06-15 21:10:21 · 4006 阅读 · 0 评论 -
(莱昂氏unix源代码分析导读-45) 文件与“资源”
by cszhao1980我们已经知道文件会占用很多资源,如磁盘inode资源、盘块资源,访问时还要占用inode数组资源,等等。 除此之外,unix v6还使用file数组来记录整个系统中被open开的文件,file数组的定义如下:5507: struct file5508: {5509: char f_flag;5510: char f_co原创 2013-05-29 19:33:28 · 4084 阅读 · 0 评论 -
(莱昂氏unix源代码分析导读-44) 文件系统资源
by cszhao1980一个设备被mount进系统后,就被称为一个文件系统。它有两类资源:(1) 磁盘inode资源;(2) 普通盘块资源。1. 磁盘inode资源对于inode资源,unix v6采用了一种很简单的管理方法。即在超级块中维护一个free inode资源数组(max 100 entry),存放可用的inode资源(inode原创 2013-05-05 08:20:34 · 4023 阅读 · 0 评论 -
(莱昂氏unix源代码分析导读-43) 文件系统的mount
By cszhao1980当一个设备被mount进系统,就会在“mount表”中占据一个表项,mount表的定义如下:0272: struct mount0273: {0274: int m_dev /* device mounted */0275: int *m_bufp; /* pointer to superblock */0276:原创 2013-04-02 09:13:05 · 3862 阅读 · 0 评论 -
(莱昂氏unix源代码分析导读-42) 硬链接
by cszhao1980熟悉unix/linux的同学都听说过硬链接的概念,老实说,在阅读源码之前,对硬链接的理解总是模模糊糊的。现在,我们已经了解了inode、目录,是时候对硬链接有个清楚的了解了。 我们知道,新建一个文件时,需要申请很多资源,如:(1)申请磁盘盘块用于写文件的内容;(2)申请inode资源,存放文件inode;(3)在父目录文件中添加一目录项,原创 2013-03-25 11:15:27 · 4122 阅读 · 0 评论 -
(莱昂氏unix源代码分析导读-41)文件系统树状结构的形成
By cszhao1980我们现在对inode有了一定的了解,正如前面所说,inode记录的信息比较靠近文件的物理存储。那末,文件系统的树状结构是如何实现的呢? 答案就是“目录”。首先需要强调的是,目录也是一种文件,它也拥有与普通文件相同的inode结构。所不同的是其文件内容——逻辑上,我们可以把目录文件的内容看作一个“目录项数组”,它由若干“目录项”构成,一个目录原创 2013-02-25 12:11:17 · 4500 阅读 · 0 评论 -
(莱昂氏unix源代码分析导读-40)inode“指向文件的内容”的读写
by cszhao1980前面已经说过,inode指向文件的内容是通过i_addr[]数组来组织和记录的。本章讨论一下文件内容的读写。在进行文件读写时,使用了进程u空间的若干变量,先介绍如下: 0418: char u_segflg; /* flag for IO; user or kernel space */0425:原创 2013-01-18 13:33:08 · 3895 阅读 · 0 评论 -
(莱昂氏unix源代码分析导读-39)inode“资源”的获取和释放
by cszhao1980iget()函数用于获取inode资源,它有2个参数,设备号和inode id。前面说过,通过这两个参数会唯一确定一个inode。简单的说,该函数的作用就是将指定的磁盘inode读入内存inode数组,并Lock该项(即会设置 ILOCK flag),它返回一个指向该inode数组项的指针。 事实上,iget()做的更多一些,它首先检查内存inod原创 2012-12-26 12:55:18 · 4239 阅读 · 0 评论 -
(莱昂氏unix源代码分析导读-38)文件物理存放位置与inode
by cszhao1980文件的物理位置指文件存放:(1)哪个设备;(2)该设备的哪些块。 Inode使用i_addr[8]数组来记录文件的物理块号:(1)对“小文件”(占用的块数块),i_addr数组内直接存放文件占用的物理块号; 如文件占用6块,则使用i_addr[0] ~ i_addr[5]存放这6个块号 (2)当文件占用的块数超过8块(此时,原创 2012-12-18 12:32:18 · 4564 阅读 · 0 评论 -
(莱昂氏unix源代码分析导读-37) 文件系统与inode
by cszhao1980前面所讲的各项内容,进程管理也好,中断处理也罢,都静静的工作在幕后,普通用户甚至根本感觉不到他们的存在。但文件系统不同,它工作在前台,用户或多或少都有一定的感性及理性认识——这给我们的读码带来了巨大的好处。在这里我假设大家对unix文件管理有一定的认识,比如,知道文件系统的树状层次结构,知道文件的访问控制,知道绝对路径与相对路径,知道如何将一个原创 2012-12-10 12:34:00 · 4479 阅读 · 0 评论 -
(莱昂氏unix源代码分析导读-36) 缓存管理(下)
by cszhao1980理解了上述内容,下面的这些程序就不难理解了。首先是函数brelse(buf bp),该函数将传入的缓存归还到AV队列中,函数采用尾插法,即缓存会插到AV队列的队尾——这样做显然有助于提高“延迟写”技术的效率。莱昂特别指出,brelse没有清理B_DONE标志,这一点非常重要,读完本章后大家就会明白。 接下来是binit()函数,完成初始化:原创 2012-11-19 10:52:49 · 4168 阅读 · 0 评论 -
(莱昂氏unix源代码分析导读-35)缓存管理(上)
by cszhao1980系统定义了NBUF个缓存区域,每个514个字节:4720: char buffers[NBUF][514]; 【注】:514个字节稍稍大于一个物理盘块的size,多出的2个byte的用途不明。 而“缓存头”数组buf[NBUF]的每个entry对应一个缓存区域,其b_addr被设置为对应的缓存区的首地址,如:&buffers[1]。对于缓存的使原创 2012-11-12 12:57:39 · 4216 阅读 · 0 评论 -
(莱昂氏unix源代码分析导读-34)You are not expected to understand this
By cszhao1980本章将探讨unix v6代码中最微妙的部分,即著名的注释:“ You are not expected to understand this”。 2178: swtch()2179: {2180: static struct proc *p;2181: register i, n;2182: register struc原创 2012-10-31 13:22:51 · 7835 阅读 · 0 评论 -
(莱昂氏unix源代码分析导读-33) swap函数
By cszhao1980是了解swap函数的时候了,它有四个参数:(1)blkno:磁盘块号;(2)coreaddr:物理内存block号;(3)count:读写字节数;(4)rdflg:读写标志。 swap函数尽力使用device independent的方法来实现功能:(1)它通过swapdev在块设备表中查表来操控swap设备;(2)启动设备时,通过块设原创 2012-10-15 11:43:02 · 4012 阅读 · 0 评论 -
(莱昂氏unix源代码分析导读-32) RK磁盘驱动
by cszhao1980别紧张,RK磁盘是一种非常简单设备——这一点从其代码量中也可以看出。首先,它由一个控制器外加 1~8个devices组成,这8个devices编号为0~7,缓冲头的b_dev的minor号记录的就是该device编号,为简单起见,我们不考虑多于8个device的情况——对RK磁盘来说,b_dev的minor部分就是0~7,而major部分为0。原创 2012-09-24 11:48:34 · 2950 阅读 · 0 评论 -
(莱昂氏unix源代码分析导读-31) “缓存头”初探
By cszhao1980struct buf结构,又被称为“缓存头”结构,而buf[NBUF]数组被称为“缓存头”数组。顾名思义,“缓存头”结构应该是用于缓存处理的,事实上,它确实有这个功能——“缓存头”数组的每个entry可与系统的一个缓存区域对应,用于操控一个缓存区域。 4520: struct buf4521: {4522: int b_flags;原创 2012-09-17 15:05:59 · 2554 阅读 · 0 评论 -
(莱昂氏unix源代码分析导读-30) device
By cszhao1980我们现在必须放下身段看一看低层的IO操作了,复杂繁琐的外设。 PDP-11/40拥有两种外设:(1) Block device (2) Character device 简单说来,Block device以block来单位操作数据,而character device则以character为单位操作数据。我原创 2012-09-10 12:23:25 · 2679 阅读 · 0 评论 -
(莱昂氏unix源代码分析导读-29) swap in/out (下)
by cszhao1980最后,看一下我们的老朋友sched(),上次看到它还是在系统初启时,#0进程在sched()函数中调用sleep(&runout ,…)睡眠,从而让出cpu,切换至#1进程。 sched()函数是个黑洞,它内部是个死循环,永远也不会退出(除非出错)。也就是说#0进程将陷入在sched()中,而sched()用来进行调度,自此#0进程就蜕变为调度原创 2012-09-03 11:48:42 · 4073 阅读 · 0 评论 -
(莱昂氏unix源代码分析导读-28) swap in/out (中)
by cszhao1980换出时,使用函数xswap(),它有三个参数,前两个参数很容易理解:(1)p——指向进程表项;(2)ff—— free flag,如非0,则free core空间; 但第三个参数就难以琢磨了:(3)os—— old size。是什么意思呢?既然p->p_size记录了进程swap image的size,这个os又是作什么用的。为了理解这原创 2012-08-27 12:28:36 · 2779 阅读 · 0 评论 -
(莱昂氏unix源代码分析导读-27) Swap in/out (上)
By cszhao1980Swap in/out指的是进程在物理内存(core空间)和磁盘交换文件间的双向移动过程,进程在active状态时,其segment必然被swap in内存空间(core空间),而一旦处于非活动状态就有可能被swap out到磁盘交换文件中。换进换出的过程必然涉及到磁盘io——这可看作是比较低层的操作,因此,这部分内容可以分为两部分:高层的模型原创 2012-08-20 12:06:29 · 3286 阅读 · 0 评论 -
(莱昂氏unix源代码分析导读-26) trace
by:cszhao1980trace是unix提供的一种是父进程可以跟踪子进程进展的手段,子进程被跟踪时,当子进程收到signal后,会进入“暂停(SSTOP)”状态,使父进程有机会进行干预。子进程的暂停是在issig()函数中实现的: 3997: if(n = p->p_sig) {3998: if (p->p_flag&STRC) { /进程处原创 2012-08-14 12:03:43 · 2460 阅读 · 0 评论 -
(莱昂氏unix源代码分析导读-25) signal(下)
By cszhao1980下面来看一下信号的处理过程。首先,进程需要不时的检查自己是否收到signal,此时需要调用issig()函数,该函数会返回进程此时收到的signal的signal type(如果没有signal,或忽略此signal,则返回0):3991: issig()3992: {3993: register n;3994: registe原创 2012-08-06 12:02:49 · 2658 阅读 · 1 评论 -
(莱昂氏unix源代码分析导读-24)signal(上)
by cszhao1980signal更确切的称呼应该是soft interruption,顾名思义,就是一种能够通过软件手段达到类似interruption目的的方法。 Unix最多支持NSIG(20)种软中断,进程的u中有u.signal[NSIG]数组,记录每种软中断的处理方法。u_signal〔n〕的值当#n中断发生时原创 2012-07-30 12:54:12 · 2610 阅读 · 0 评论 -
(莱昂氏unix源代码分析导读-23) 若干系统调用的实现
By cszhao1980 System call是提供给user进程的接口,使其可以主动进入内核,完成一些特殊的操作。 多数的sys call的实现很简单,莱昂留给大家分析,我在这里多说原创 2012-07-23 12:02:01 · 2983 阅读 · 0 评论 -
(莱昂氏unix源代码分析导读-22) trap函数(2693)
莱昂的分析比较清晰。在这里我只说明几个容易让人感到迷惑的问题。 首先是trap函数的长长的参数列表,同clock函数一样,这些参数来自于入口处的设置,回头看一下栈图,这些参数的容易理解了。 函数开头就检查进程的“前状态”——绝大多数情况下,应该是user态。核态的陷入在大多数情况下都是一个错误,会引起panic的调用,导致系统down掉。 在“2701: u.u_a原创 2012-07-16 11:58:51 · 3150 阅读 · 0 评论 -
(莱昂氏unix源代码分析导读-21)时钟中断处理
时钟中断是系统中最重要的中断,每个时钟滴答都会产生时钟中断,它的中断矢量为(0100)或(0103)。 0533: . = 100^.0534: kwlp; br60535: kwlp; br6 0569: .globl _clock0570: kwlp: jsr r0,call; _clock 显然,时钟中断会通过call例程调用_原创 2012-07-02 12:00:18 · 10886 阅读 · 5 评论 -
(莱昂氏unix源代码分析导读-20)中断、陷入的入口和出口
陷入处理程序的入口都为“trap”(这里是指一个汇编程序,而非PDP11指令);中断则不同。他们有不同的入口,如:525 .=60^. 当前地址设置为60 octal526 klin; br4 /(中断矢量地址为60,电传打字机输入的中断处理程序)527 klou; br4原创 2012-06-25 12:10:30 · 3698 阅读 · 1 评论 -
(莱昂氏unix源代码分析导读-19)再谈进程swtch
我们已经涉及到了部分进程切换的概念,在本章中,我们会从更一般的意义上考察进程切换的行为。首先,进程切换(也称作context switch)一定是在内核中完成的。比如,以下为发生进程切换的最常见的情况:(1) active进程因等待某资源阻塞,自动让出cpu;(2) 进程时间片用完; 情况1中,进程会通过系统调用进入内核,在内核态让出cpu;而情况2的检查是在原创 2012-06-18 12:02:24 · 4955 阅读 · 0 评论 -
(莱昂氏unix源代码分析导读-18) 再谈中断与陷入
从产生原因看,中断和陷入也有巨大的差别。 硬件中断由外部事件造成,属于异步事件,往往与当前进程毫无关系;陷入则不同,它常常都是同步的(如除0错),并与当前进程上下文相关。 除此之外,陷入还用来实现系统调用——内核为user进程提供了大量的服务,这些服务就是通过系统调用来访问的。PDP11提供了trap指令来使user进程进入内核,进行系统调用。 由于系统调用的存在,原创 2012-06-11 12:00:03 · 4187 阅读 · 0 评论 -
(莱昂氏unix源代码分析导读-17)系统初启(10)
小结本章的内容到此结束。由于我们跳过了一些专题,启动代码中仍有一些未解之谜,但它的神秘面纱已经揭开,这是一个好的开始。 最后,给出fuibyte(0)的分析结果:0814 _fuibyte: 0815 _fubyte:0816 mov 2(sp),r1 /参数(输入地址)--->r10817 bic $1,r1原创 2012-06-05 11:59:51 · 3866 阅读 · 0 评论 -
(莱昂氏unix源代码分析导读-16)系统初启(9)
回到start 现在,继续#1进程之旅。main()1627 if(newproc()) { 1628 expand(USIZE+1); /调整进程“私有空间”大小,暂不考虑换出情况的话程序/较简单,读者自行分析1629 estabur(0, 1, 0,原创 2012-06-04 12:02:44 · 3892 阅读 · 0 评论 -
(莱昂氏unix源代码分析导读-15) 系统初启(8)
进程user态下的分段 User态中将进程空间分为text、data、stack segment三部分。 estabur(nt, nd, ns,sep)根据各个segment的大小,为各段分配page,参数如下:(1) nt—— text segment的长度(block)(2) nd—— data segment的长度(block)(3) ns—— stack seg原创 2012-06-01 12:05:28 · 4142 阅读 · 0 评论 -
(莱昂氏unix源代码分析导读-14)系统初启(7)
#1进程 现在,是#1进程的天下了。回到main函数:1627 if(newproc()) { /返回值为1,要执行以下的语句1628 expand(USIZE+1); /扩展进程私有空间,增大1个block 1629 estabur(0, 1, 0, 0); 1630原创 2012-05-31 12:12:54 · 4027 阅读 · 0 评论 -
(莱昂氏unix源代码分析导读-13)系统初启(6)
#0进程的switch 我们已经了解了进程的switch原理,接下来,我们继续跟踪#0进程,看发生了什么。 1940: sched()1941: {……1951: goto loop;……1957: loop:1958: spl6();原创 2012-05-30 12:40:25 · 4655 阅读 · 2 评论 -
(莱昂氏unix源代码分析导读-12)系统初启(5)
进程switch现在让我们考虑一个问题,如何确定当前Active的进程是哪个呢?要知道,进程并不独享某段代码,我们不可能通过当前执行的代码来确定哪个进程处于Active状态。 显然,应该通过进程私有的东西来确定——而进程所私有的只有一样,即Kernel page 6(即ppda所在的Page)。我们通过KISA6来确定Active进程,即KISA6指向哪个进程的“私有空间”,则原创 2012-05-29 12:21:20 · 4573 阅读 · 0 评论