关于分析今天长安保定百度爱问爱第2章代码初识 本章首先从较高层次

时间:2019-05-15 09:15:01下载本文作者:会员上传
简介:写写帮文库小编为你整理了多篇相关的《关于分析今天长安保定百度爱问爱第2章代码初识 本章首先从较高层次》,但愿对你工作学习有帮助,当然你在写写帮文库还可以找到更多《关于分析今天长安保定百度爱问爱第2章代码初识 本章首先从较高层次》。

第一篇:关于分析今天长安保定百度爱问爱第2章代码初识 本章首先从较高层次

的的范德萨的地方爱的规格爱你啊好文章第2章 代 码 初 识

本章首先从较高层次介绍Linux内核源程序的概况,这些都是大家关心的一些基本特点。随后将简要介绍一些实际代码。最后介绍如何编译内核。2.1 Linux内核源程序的部分特点

在过去的一段时期,Linux内核同时使用C语言和汇编语言来实现。这两种语言需要一定的平衡:C语言编写的代码移植性较好、易于维护,而汇编语言编写的程序则速度较快。一般只有在速度是关键因素或者一些因平台相关特性而产生的特殊要求(例如直接和内存管理硬件进行通讯)时才使用汇编语言。

正如实际中所做的,即使内核并未使用C++的对象特性,部分内核也可以在g++(GNU的C++编译器)下进行编译。同其他面向对象的编程语言相比较,相对而言C++的开销是较低的,但是对于内核开发人员来说,这已经是太多了。

内核开发人员不断发展编程风格,形成了Linux代码独有的特色。本节将讨论其中的一些问题。

2.1.1 gcc特性的使用

Linux内核被设计为必须使用GNU的C编译器gcc来编译,而不是任何一种C编译器都可以使用。内核代码有时要使用gcc特性,本书将陆续介绍其中的一部分。一些gcc特有代码只是简单地使用gcc语言扩展,例如允许在C(不只是C++)中使用inline关键字指示内联函数。也就是说,代码中被调用的函数在每次函数调用时都会被扩充,因而就可以节约实际函数调用的开销。

一般情况下,代码的编写方式比较复杂。因为对于某些类型的输入,gcc能够产生比其他输入效率更高的执行代码。从理论上讲,编译器可以优化具有相同功能的两种对等的方法,并且得到相同的结果。因此,代码的编写方式是无关紧要的。但在实际上,用某种方法编写所产生的代码要比用另外一些方法编写所产生的代码执行速度快许多。内核开发人员知道怎样才能产生更高效的执行代码,这不断地在他们编写的代码中反映出来。例如,考虑内核中经常使用的goto语句—为了提高速度,内核中经常大量使用这种一般要避免使用的语句。在本书中所包含的不到40 000行代码中,一共有500多条goto语句,大约是每80行一个。除汇编文件外,精确的统计数字是接近每72行一个goto语句。公平地说,这是选择偏向的结果:比例如此高的原因之一是本书中涉及的是内核源程序的核心,在这里速度比其他因素都需要优先考虑。整个内核的比例大概是每260行一个goto语句。然而,这仍然是我不再使用Basic进行编程以来见过的使用goto频率最高的地方。

代码必需受特定编译器限制的特性不仅与普通应用程序的开发有很大不同,而且也不同于大多数内核的开发。大多数的开发人员使用C语言编写代码来保持较高的可移植性,即使在编写操作系统时也是如此。这样做的优点是显而易见的,最为重要的一点是一旦出现更好的编译器,程序员们可以随时进行更换。

内核对于gcc特性的完全依赖使得内核向新的编译器上移植更加困难。最近Linus对这一问题在有关内核的邮件列表上表明了自己的观点:“记住,编译器只是一个工具。”这是对依赖于gcc特性的一个很好的基本思想的表述:编译器只是为了完成工作。如果通过遵守标准还不能达到工作要求,那么就不是工作要求有问题,而是对于标准的依赖有问题。

在大多数情况下,这种观点是不能被人所接受的。通常情况下,为了保证和程序语言标准的一致,开发人员可能需要牺牲某些特性、速度或者其他相关因素。其他的选择可能会为后期开发造成很大的麻烦。

但是,在这种特定的情况下,Linus是正确的。Linux内核是一个特例,因为其执行速度要比向其他编译器的可移植性远为重要。如果设计目标是编写一个可移植性好而不要求快速运行的内核,或者是编写一个任何人都可以使用自己喜欢的编译器进行编译的内核,那么结论就可能会有所不同了;而这些恰好不是Linux的设计目标。实际上,gcc几乎可以为所有能够运行Linux的CPU生成代码,因此,对于gcc的依赖并不是可移植性的严重障碍。在第3章中我们将对内核设计目标进行详细说明。2.1.2 内核代码习惯用语

内核代码中使用了一些显著的习惯用语,本节将介绍常用的几个。当通读源代码时,真正重要的问题并不在这些习惯用语本身,而是这种类型的习惯用语的确存在,而且是不断被使用和发展的。如果你需要编写内核代码,你应该注意到内核中所使用的习惯用语,并把这些习惯用语应用到你的代码中。当通读本书(或者代码)时,看看你还能找到多少习惯用语。为了讨论这些习惯用语,我们首先需要对它们进行命名。为了便于讨论,笔者创造了这些名字。而在实际中,大家不一定非要参考这些用语,它们只是对内核工作方式的描述而已。一个普通的习惯用语,笔者称之为“资源获取”(resource acquisition idiom)。在这个用语中,一个函数必须实现一系列资源的获取,包括内存、锁等等(这些资源的类型未必相同)。只有成功地获取当前所需要的资源之后,才能处理后面的资源请求。最后,该函数还必须释放所有已经获取的资源,而不必考虑没有获取的资源。

我采用“错误变量”这一用语(error variable idiom)来辅助说明资源获取用语,它使用一个临时变量来记录函数的期望返回值。当然,相当多的函数都能实现这个功能。但是错误变量的不同点在于它通常是用来处理由于速度的因素而变得非常复杂的流程控制中的问题。错误变量有两个典型的值,0(表示成功)和负数(表示有错)。

这两个用语结合使用,我们就可以十分自然地得到符合模式的代码如下:

(注意变量err是使用错误变量的一个明确实例,同样,诸如out之类的标号则指明了资源获取用语的使用。)

如果执行到标号out2,则都已经获取了r1和r2资源,而且也都需要进行释放。如果执行到标号out1(不管是顺序执行还是使用goto语句进行跳转到),则r2资源是无效的(也可能刚被释放),但是r1资源却是有效的,而且必需在此将其释放。同理,如果标号out能被执行,则r1和r2资源都无效,err所返回的是错误或成功标志。

在这个简单的例子中,对err的一些赋值是没有必要的。在实践中,实际代码必须遵守这种模式。这样做的原因主要在于同一行中可能包含有多种测试,而这些测试应该返回相同的错误代码,因此对错误变量统一赋值要比多次赋值更为简单。虽然在这个例子中对于这种属性的必要性并不非常迫切,但是我还是倾向于保留这种特点。有关的实际应用可以参考sys_shmctl(第21654行),在第9章中还将详细介绍这个例子。2.1.3 减少#if和#ifdef的使用

现在的Linux内核已经移植到不同的平台上,但是我们还必须解决移植过程中所出现的问题。大部分支持各种不同平台的代码由于包含许多预处理代码而已经变得非常不规范,例如: 这个例子试图实现操作系统的可移植性,虽然Linux关注的焦点很明显是实现代码在各种CPU上的可移植性,但是二者的基本原理是一致的。对于这类问题来说,预处理器是一种错误的解决方式。这些杂乱的问题使得代码晦涩难懂。更为糟糕的是,增加对新平台的支持有可能要求重新遍历这些杂乱分布的低质量代码段(实际上你很难能找到这类代码段的全部)。与现有方式不同的是,Linux一般通过简单函数(或者是宏)调用来抽象出不同平台间的差异。内核的移植可以通过实现适合于相应平台的函数(或宏)来实现。这样不仅使代码的主体简单易懂,而且在移植的过程中还可以比较容易地自动检测出你没有注意到的内容:如引用未声明函数时会出现链接错误。有时用预处理器来支持不同的体系结构,但这种方式并不常用,而相对于代码风格的变化就更是微不足道了。

顺便说一下,我们可以注意到这种解决方法和使用用户对象(或者C语言中充满函数指针的struct结构)来代替离散的switch语句处理不同类型的方法十分相似。在某些层次上,这些问题和解决方法是统一的。

可移植性的问题并不仅限于平台和CPU的移植,编译器也是一个重要的问题。此处为了简化,假设Linux只使用gcc来编译。由于Linux只使用同一个编译器,所以就没有必要使用#if块(或者#ifdef块)来选择不同的编译器。

内核代码主要使用#ifdef来区分需要编译或不需要编译的部分,从而对不同的结构提供支持。例如,代码经常测试SMP宏是否定义过,从而决定是否支持SMP机。2.2 代码样例

了解Linux代码风格最好的方法就是实际研究一下它的部分代码。即使你不完全理解本节所讨论代码的细节也无关紧要,毕竟本节的主要目的不是理解代码,一些读者可以只对本节进行浏览。本节的主要目的是让读者对Linux代码进行初步了解,为今后的工作提供必要基础。该讨论将涉及部分广泛使用的内核代码。2.2.1 printk printk(25836行)是内核内部消息日志记录函数。在出现诸如内核检测到其数据结构出现不一致的事件时,内核会使用printk把相关信息打印到系统控制台上。对于printk的调用一般分为如下几类:

?紧急事件(emergency)—例如,panic函数(25563行)多次使用了printk。当内核检测到发生不可恢复的内部错误时就会调用panic函数,然后尽其所能地安全关闭计算机。这个函数中调用printk以提示用户系统将要关闭。

?调试—从3816行开始的#ifdef块使用printk来打印SMP逻辑单元(box)中每一个处理器的相关配置信息,但是此过程只有在使用SMP_DEBUG标志编译代码的情况下才能够被执行。?普通信息—例如,当机器启动时,内核必须估计系统速度以确保设备驱动程序能够忙等待(busy-wait)一个精确的极短周期。计算这种估计值的函数名为calibrate_delay(19654行),它既在19661行使用printk声明马上开始计算,又在19693行报告计算结果。另外,在第4章将详细的介绍calibrate_delay函数。

如果你已经浏览过这些参照行,你可能已经注意到printk和printf的参数十分类似:一个格式化字符串,后跟零个或者多个参数加入字符串中。格式化字符串可能是以一组“”开始,这里的N是从0到7的数字,包括0和7在内。数字区分了消息的日志等级(log level),只有当日志等级高于当前控制台定义的日志等级(console_loglevel,25650行)时,才会打印消息。root可以通过适当减小控制台的日志等级来过滤不是很紧急的消息。如果内核在格式化字符串中检测不到日志等级序列,那么就会一直打印消息(实际上,日志等级序列并不一定要在格式化字符串中出现,可以在格式化文本中查找到它的代码)。

从14946行开始的#define块说明了这些特殊序列,这些定义可以帮助调用者正确区分对printk的调用。简单地说,我称日志等级0到4为“紧急事件”,等级5到等级6为“普通信息”,等级7自然就是我所说的“调试”(这种分类方法并不意味着其他更好的分类方法没有用处,而只是目前我们还不关心它而已)。在上面讨论的基础上,我们研究一下代码本身。printk 25836:参数fmt是printf类型的格式化字符串。如果你对“...”部分的内容不熟悉,那就 需要参阅一本好的C语言参考书(在其索引中查找“变参函数,variadic function”)。另外,在安装的GNU/Linux中的stdarg帮助里也包含了一个有关变参函数的简明描述,在这儿只需要敲入“man stdarg”就可以看到。简单地说,“...”部分提示编译器fmt后面可能紧跟着数量不定的任何类型的参数。由于这些参数在编译的时候还没有类型和名字,内核使用由三个宏va_start、va_arg和va_end组成的特殊组及一个特殊类型—va_list对它们进行处理。25842:msg_level记录了当前消息的日志等级。它是静态的,这看起来可能会有些奇怪—为什么下一次对printk的调用需要记录日志等级呢?问题的答案是只有打印出新行(n)或者赋给一个新的日志等级序列以后,当前消息才会结束。这样,通过在包含消息结束的新行里调用printk,就保证了在多个短期冲突的情况下,调用者只打印唯一一个长消息。

25845:在SMP逻辑单元中,内核可能试图从不同的CPU向控制台同时打印信息(有时在单处理机(UP)逻辑单元中也会发生同样问题,但由于中断还未被覆盖掉,所以问题也并不十分明显)。如果不进行任何协同的话,结果就将处于完全无法让人了解的杂乱无章的状态,每个消息的各个部分都和其他消息的各个部分混杂交织在一起。

相反,内核使用旋转锁(spin-lock)来控制对控制台的访问。旋转锁将在第10章进行深入介绍。

如果你对flags 在传送给spin_lock_irqsave之前为什么不对它初始化感到疑惑,请不要担心:spin_lock_irqsave(对于不同的版本请分别参看12614行,12637行,12716行和12837行)是一个宏,而不是一个函数。该宏实际上是将值写入flags中,而不是从flags中读出值(在25895行中,存储在flags中的信息被spin_unlock_irqrestore回读,请参看12616行,12639行,12728行和12841行)。

25846:初始化变量args,该变量代表printk参数中的“...”部分。25848:调用内核自身的vsprintf(为节省空间而省略)实现。该函数的功能与标准vsprintf函数非常相似,向buf中写入格式化文本(25634行)并返回写入字符串的长度(长度不包括最后一位终止字符0字节)。很快,你将可以看到为什么这种机制会忽略buf的前三个字符。(正如25847行的注释中所述)我们应该注意到在这里并没有采取严格的措施来保证缓冲器不会过载。这里系统假定1024个字符长度的buf已经足够使用(参阅25634行)。如果内核在这里能够使用vsnprintf函数的话,情况就会好许多。然而,vsnprintf还有另外一个参数限制了它能够写入缓冲器的字符长度。

25849:计算buf中最近使用的元素,调用va_end终止对“...”参数的处理。

25851:开始格式化消息的循环。其中存在一个内部循环能够处理更多内容(这一点随后就能看到),因此,每次内循环开始,都开始一个新的打印行。由于通常情况下printk只用于打印单行,所以在每次调用中,这种循环通常只执行一次。

25853:如果预先不知道消息的日志等级,printk会检查当前行是否以日志等级序列开头。25860:如果不是,buf中开始未使用的三个字符就能够起作用了(第一次以后的每次循环,都会覆盖部分消息文本,但是这样并不会引起问题,因为这里的文本只是前面行中的一部分,它们已经被打印过,而且以后也不再需要了)。这样,就可以将日志等级插入buf中。25866:此处有如下属性:p指向日志等级序列(消息文本紧随其后),msg指向消息文本—请注意25852行和25865行中对msg的赋值。

由于已知p用来指示日志等级序列的开头—该日志等级序列可能是由函数自身所创建的,日志等级可以从p中抽出并存到msg_level中。25868:没有检测到新行,清空line_feed标志。

25869:这是前面谈到过的内循环,循环将运行到本行结束(也就是检测到新行标志)或者缓冲器的末尾为止。

25870:除了将消息打印到控制台之外,printk还能够记录最近打印的长度为LOG_ BUF_LEN的字符组(LOG_BUF_LEN为16K,请参看25632行)。如果在控制台打开之前,内核就已经调用printk,则显然不能在控制台上正确打印消息,但是这些消息将被尽可能地存储到log_buf中(25656行)。当控制台打开以后,缓存在log_buf中的数据就可以转储并在控制台上打印出来,请参看25988行。

log_buf是一个循环缓冲器,log_start和log_size变量(25657行和25646行)分别记录当前缓冲器的开始位置和长度。本行中的按位与(AND)操作实际上是快速求模(%)运算,它的正确性依赖于LOG_BUF_LEN的值是2的幂。25872:保存变量跟踪记录循环日志的值。显然,日志大小会不断增长,直至达到LOG_BUF_LEN的值为止。此后,log_size将保持不变,而插入新字符将导致log_start的增长。

25878:请注意logged_chars(25658行)记录从机器启动之后由printk写入的所有字符的长度,它在每次循环中都会被更新,而不是在循环结束后才改变一次。基于同样的道理,log_start和log_size的处理方式也是一样。这实际上是一种优化的时机,本书将在结束对函数的介绍之后再对它进行详细讨论。

25879:消息被分为若干行,这当然要使用新行标志符来进行分割。一旦内核检测到新行标志符,就写入一个完整行,从而内循环的执行也可以提前终止。25884:在这里我们先不考虑内部循环是否会提前退出,从msg到p的字符序列是专门提供给控制台使用的(这种字符序列我称之为行,但是不要忘了,这里的行可能并不意味着新行终止,因为buf也许还没有终止)。如果该行的日志等级高于系统控制台定义的日志等级,而且当前又有控制台可供打印,那么就能够正确打印该行。(记住,printk可能在所有控制台打开之前就已经被调用过了。)

如果在该消息块中没有发现日志等级序列,并且在前面的printk调用中也没有对msg_level赋值,那么本行中的msg_level就是-1。由于console_loglevel总不小于1(除非root通过sysctl接口锁定),于是总是可以打印这些行。

25886:本行应该能够被打印。printk通过遍历打开的控制台驱动链表告知每一个控制台驱动去打印当前行设备驱动在本书的讨论范围之外,因此,控制台驱动代码则并不包含在内)。25888:请注意这里消息文本的开头使用的是msg而不是p,这样就在没有日志等级序列的情况下写入消息了。然而,日志等级序列已经被存储到log_buf缓冲器中了。这样就使后来能够访问log_buf以获取消息日志等级的代码(请参看25998行),不会再产生显示混乱信息序列的现象。

25892:如果内层for循环发现一新行,那么buf中的剩余字符(如果有的话)将被认为是新的消息,因此msg_level会被重置。但是无论怎样,外层循环都会持续到buf清空为止。25895:释放在25845行获取的控制台锁(console lock)。

25896:唤醒等待被写入控制台日志的所有进程。注意即使没有文本被实际写入任何控制台,这个过程也仍然会发生。这样处理是正确的,因为无论是否要往控制台中写入文本,等待进程实际上都是在等待从log_buf中读出信息。在25748行,进程被转入休眠状态以等待log_buf的活动。在休眠、唤醒和等待队列中所使用的机制将在下一节中进行讨论。25897:返回日志中写入的字符长度。

如果对于每个字符的处理工作都能减少一点,那么从25869行开始的for循环就执行得更快一点。当循环存在时,我们可以通过只在循环退出时将logged_chars更新一次来稍微提高运行速度。然而我们还可以通过其他努力来提高速度。由于我们可以预知消息的长度,因此log_size和log_start可以到最后再增长。让我们来实验一下这样能否提高速度,下面是一段经过理想优化的代码:

请注意循环通常只需要执行一次,只有在log_buf末尾写入信息需要折行时才会多次执行。因而log_size和log_buf只需要更新一次(或者当写入需要换行时是两次)。

这时速度的确提高了,但是有两个原因使我们并不能这样做。首先,内核可能有自己特有的memcpy函数,我们必须确保对memcpy的调用不会再次进入对printk的调用(有一部分内核移植版定义了自己特有的速度较快的memcpy函数版本,因此所有的移植都要在这一点上保持一致)。如果memecpy调用printk来报告失败,那么就有可能触发无限循环。

然而在这一点上也并不是真的无药可救。使用这种解决方案的最大问题在于该内核循环的形式中也要留意新行标志符,因此使用memcpy将整个消息拷贝到log_buf中是不正确的:如果此处存在新行,我们将无法对其进行处理。

我们可以试验一个一箭双雕的办法。下面这种替代的尝试虽然可能比前面那种初步解决方法速度要慢,但是它保持了内核版本的语意:

(请注意gcc的优化器十分灵敏,它足以能检测到循环内部的表达式log_buf+LOG_BUF_LEN并没有改变,因此在上面的循环中试图手工加速计算是没有任何效果的。)

不幸的是,这种方法并不能比现在的内核版本在速度上快许多,而且那样会使得代码晦涩难懂(如果你编写过更新log_size和log_start的代码,你就能清楚地了解这一点)。你可以自己决定这种折衷是否值得。然而无论怎样,我们学到了一些东西,通常,不管成功与否,改进内核代码都可以加深你对内核工作原理的理解。2.2.2 等待队列

前一节我们曾简要的提到进程(也就是正在运行的程序)可以转入休眠状态以等待某个特定事件,当该事件发生时这些进程能够被再次唤醒。内核实现这一功能的技术要点是把等待队列(wait queue)和每一个事件联系起来。需要等待事件的进程在转入休眠状态后插入到队列中。当事件发生之后,内核遍历相应队列,唤醒休眠的任务让它投入运行状态。任务负责将自己从等待队列中清除。

等待队列的功能强大得令人吃惊,它们被广泛应用于整个内核中。更重要的是,实现等待队列的代码量并不大。1.wait_queue结构

18662:简单的数据结构就是等待队列节点,它包含两个元素: ?task—指向struct task_struct结构的指针,它代表一个进程。从16325行开始的struct task_struct结构将在第7章中进行介绍。

?next—指向队列中下一节点的指针。因而,等待队列实际上是一个单链表。

通常,我们用指向等待队列队首的指针来表示等待队列。例如,printk使用的等待队列log_wait(25647行)。2.wait_event 16840:通过使用这个宏,内核代码能够使当前执行的进程在等待队列wq中等待直至给定condition(可能是任何的表达式)得到满足。

16842:如果条件已经为真,当前进程显然也就无需等待了。16844:否则,进程必须等待给定条件转变为真。这可以通过调用__wait_event来实现(16824行),我们将在下一节介绍它。由于__wait_event已经同wait_event分离,已知条件为假的部分内核代码可以直接调用__wait_queue,而不用通过宏来进行冗余的(特别是在这些情况下)测试,实际上也没有代码会真正这样处理。更为重要的是,如果条件已经为真,wait_event会跳过将进程插入等待队列的代码。

注意wait_event的主体是用一个比较特殊的结构封闭起来的:

奇怪的是,这个小技巧并没有得到应有的重视。这里的主要思路是使被封闭的代码能够像一个单句一样使用。考虑下面这个宏,该宏的目的是如果p是一个非空指针,则调用free: 除非你在如下所述的情况下使用FREE1,否则所有调用都是正确有效的: FREE1经扩展以后,else就和错误的if(FREE1的if)联系在一起。有些程序员通过如下途径解决这种问题:

这两种方法都不尽人意,程序员在调用宏以后自然而然使用的分号会把扩展信息弄乱。以FREE2为例,在宏展开之后,为了使编译器能更准确地识别,我们还需要进行一定的缩进调节,最终代码如下所示:

这样就会引起语法错误—else和任何一个if都不匹配。FREE3从本质上讲也存在同样的问题。而且在研究问题产生原因的同时,就能够明白为什么宏体里是否包含if是无关紧要的。不管宏体内部内容如何,只要使用一组括号来指定宏体,就会碰到相同的问题。

引入do/while(0)技巧能够克服前面所出现的所有问题,现在我们可以编写FREE4。

将FREE4和其他宏一样插入相同代码之后,宏展开后其代码如下所示(为清晰起见,我们再次调整了缩进格式):

这段代码当然可以正确执行。编译器能够优化这个伪循环,舍弃循环控制,因此执行代码并没有速度的损失,我们也从而得到了能够实现理想功能的宏。

虽然这是一个可以接受的解决方案,但是我们不能不提到的是编写函数要比编写宏好得多。不过如果你不能提供函数调用所需的开销,那么就需要使用内联函数。这种情况虽然在内核中经常出现,但是在其他地方就要少得多。(不可否认,当使用C++、gcc或者任何实现了将要出现的修正版ISO标准C的编译器时,这种方案只是一种选择,就是最后为C增加内联函数。)

3.__wait_event 16824:__wait_event使当前进程在等待队列wq中等待,直至condition为真。16829:通过调用add_wait_queue(16791行),局部变量__wait可以被链接到队列上。注意__wait是在堆栈中而不是在内核堆中分配空间,这是内核中常用的一种技巧。在宏运行结束之前,__wait就已经被从等待队列中移走了,因此等待队列中指向它的指针总是有效的。16830:重复分配CPU给另一个进程直至条件满足,这一点将在下面几节中讨论。16831:进程被置为TASK_UNINTERRUPTIBLE状态(16190行)。这意味着进程处于休眠状态,不应被唤醒,即使是信号也不能打断该进程的休眠。信号在第6章中介绍,而进程状态则在第7章中介绍。

16832:如果条件已经满足,则可以退出循环。

请注意如果在第一次循环时条件就已经满足,那么前面一行的赋值就浪费了(因为在循环结束之后进程状态会立刻被再次赋值)。__wait_event假定宏开始执行时条件还没有得到满足。而且,这种对进程状态变量state的延迟赋值也并没有什么害处。在某些特殊情况下,这种方法还十分有益。例如当__wait_event开始执行时条件为假,但是在执行到16832行时就为真了。这种变化只有在为有关进程状态的代码计算condition变量值时才会出现问题。但是在代码中这种情况我没有发现。

16834:调用schedule(26686行,在第7章中讨论)将CPU转移给另一个进程。直到进程再次获得CPU时,对schedule的调用才会返回。这种情况只有当等待队列中的进程被唤醒时才会发生。

16836:进程已经退出了,因此条件必定已经得到了满足。进程重置TASK_RUNNING的状态(16188行),使其适合CPU运行。

16837:通过调用remove_wait_queue(16814行)将进程从等待队列中移去。wait_event_interruptible和__wait_event_interruptible(分别参见16868行和16847)基本上与wait_event和__wait_event相同,但不同的是它们允许休眠的进程可以被信号中断。信号将在第6章中介绍。

请注意wait_event是被如下结构所包含的。

和do/while(0)技巧一样,这样可以使被封闭起来的代码能够像一个单元一样运行。这样的封闭代码就是一个独立的表达式,而不是一个独立的语句。也就是说,它可以求值以供其他更复杂的表达式使用。发生这种情况的原因主要在于一些不可移植的gcc特有代码的存在。通过使用这类技巧,一个程序块中的最后一个表达式的值将定义为整个程序块的最终值。当在表达式中使用wait_event_interruptible时,执行宏体后赋__ret的值为宏体的值(参见16873行)。对于有Lisp背景知识的程序员来说,这是个很常见的概念。但是如果你仅仅了解一点C和其他一些相关的过程性程序设计语言,你可能就会觉得比较奇怪。__wake_up 26829:该函数用来唤醒等待队列中正在休眠的进程。它由wake_up和wake_up_ interruptible调用(请分别参见16612行和16614行)。这些宏提供mode参数,只有状态满足mode所包含的状态之一的进程才可能被唤醒。

26833:正如将在第10章中详细讨论的那样,锁(lock)是用来限制对资源的访问,这在SMP逻辑单元中尤其重要,因为在这种情况下当一个CPU在修改某数据结构时,另一个CPU可能正在从该数据结构中读取数据,或者也有可能两个CPU同时对同一个数据结构进行修改,等等。在这种情况下,受保护的资源显然是等待队列。非常有趣的是所有的等待队列都使用同一个锁来保护。虽然这种方法要比为每一个等待队列定义一个新锁简单得多,但是这就意味着SMP逻辑单元可能经常会发现自己正在等待一个实际上并不必须的锁。

26838:本段代码遍历非空队列,为队列中正确状态的每一个进程调用wake_up_process(26356行)。如前所述,进程(队列节点)在此可能并没有从队列中移走。这在很大程度上是由于即使队列中的进程正在被唤醒,它仍然可能希望继续存在于等待队列中,这一点正如我们在__wait_event中发现的问题一样。2.2.3 内核模块

整个内核并不需要同时装入内存。应该确认,为保证系统能够正常运行,一些特定的内核必须总是驻留在内存中,例如,进程调度代码就必须常驻内存。但是内核其他部分,例如大部分的设备驱动就应该仅在内核需要的时候才装载,而在其他情况下则无需占用内存。

举例来说,只有在内核真正和CD-ROM通讯时才需要使用完成内核与CD-ROM通讯的设备驱动程序,因此内核可以被设置为在和设备通讯之前才装载相应代码。内核完成和设备的通讯之后可以将这部分代码丢弃。也就是说,一旦代码不再需要,就可以从内存中移走。系统运行过程中可以增减的这部分内核称为内核模块。内核模块的优点是可以简化内核自身的开发。假设你购买了一个新的高速CD-ROM驱动器,但是现有的CD-ROM驱动程序并不支持该设备。你自然就希望增加对这种高速模式的支持以提高系统光驱设备的性能。如果作为内核模块来编译驱动程序,你的工作将会方便得多:编译驱动程序、加载到内核、测试、卸载驱动程序、修改驱动程序、再次加载驱动程序到内核、测试,如此周而复始。如果你的驱动程序是直接编辑在内核中的,那么你就必须重新编译整个内核并且在每次修改驱动程序之后重新启动机器。这样慢得很多。

自然,你也必须留意内核模块。对于指明其他内核模块在磁盘上的驻留位置的那些模块,一定不能从内存中卸载,否则,内核将只能通过访问磁盘来装载处理磁盘访问的内核模块,这是不可能实现的。这也是我们要选择把部分内核作为模块编译还是直接编译进内核使其常驻内存的又一个原因。知道自己系统的设置方式,因而也就可以选择正确使用的方式(如果为了确保安全,可以简单的忽略内核模块系统的优点,而把所有的内容都编译到内核里面)。内核模块会带来一些速度上的损失,这是因为一些必需的代码现在并不在RAM中,必需要从磁盘读入。但是整个系统的性能通常会有所提高,这主要是因为通过丢弃暂时不使用的模块可以释放出额外的RAM供应用程序使用。如果这部分内存被内核所占用,应用程序将只能更加频繁地进行磁盘交换,而这种磁盘交换会显著地降低应用程序的性能(磁盘交换将在第8章中讨论)。

内核模块还会带来因复杂度的增加所造成的开销,这是因为在系统运行的过程中,移进移出部分内核需要额外的代码。然而,复杂度的开销是可以管理的。通过使用外部程序来代理一些必需的工作还可以更进一步降低复杂度的开销(更为确切的说法是,这样做不是减少了复杂度的开销,而是把复杂度的开销重新分配了一下)。这是对内核模块原理的一个小小的扩展:即使是内核的支持模块,对于内核来说也只是外部的、部分可用的,只有在需要的时候才被装入内存。

通常用于这种目的程序称为modprobe。有关的modprobe代码超出了本书的范围,但是在Linux的每个发行版本中都包含有它。本节的剩余部分将讨论同modprobe协同工作,以装载内核模块的内核代码。1.request_module 24432:作为函数说明之前的注释,request_module是一个函数。内核的其他模块在需要装载其他内核模块的时候,都必须调用这个函数。就像内核处理其他工作一样,这种调用也是为当前运行的进程进行的。从进程的角度来看,这种调用的请求通常是隐含的—正在执行进程其他请求的内核可能会发现,必须调入一个模块才能够完成该请求。例如,请参见10070行,这里是一些将在第7章中讨论的代码。

24446:以内核中的一个独立进程的形式执行exec_modprobe函数(24384行)。这并不能只通过函数的简单调用实现,因为exec_modprobe要继续调用exec来执行一个程序。因此,对函数exec_modprobe的简单调用将永远不会有返回。

这和使用fork以准备exec调用十分类似,你可以认为kernel_thread对内核来说就是较低版本的fork,虽然两者有很大不同。fork是从指定函数开始执行新的进程,而不是从调用者的当前位置开始运行。正如fork一样,kernel_thread返回的值是新进程的进程号。24448:和fork一样,从kernel_thread返回的负值表示内部错误。

24455:正如函数中论述的一样,大部分的信号将因当前进程而被暂时阻塞。

24462:等待exec_modprobe执行完毕,同时指出所需要的模块是已经成功装入内存,还是装载失败了。

24465:结束运行,恢复信号。如果exec_modprobe返回错误代码,则打印错误消息。2.exec_modprobe 24384:exec_modprobe运行为内核增加内核模块的程序。这里的模块名是一个void*的指针,而不是char*的指针。原因简单说来就是kernel_thread 产生的函数通常都使用void*指针参数。

24386:设置modprobe的参数列表和环境。modprobe_path(24363行)用来定位modprobe程序的位置。它可以通过内核的sysctl特性来修改,这一点将在第11章中介绍(参见30388行)。这意味着root可以动态选择不同于/sbin/modprobe的程序来运行,以适应当modprobe被安装到其他地方或者使用修改过的modprobe替换掉了原有的modprobe之类的情况。24400:(正如代码中描述的一样)出于安全性考虑,丢弃所有挂起的信号和信号句柄(handl-ers)。这里最重要的部分是对flush_signal_handlers的调用(28041行),它使用内核默认的信号句柄代替所有用户定义的信号句柄。如果在此时有信号被传送到内核,它将获得默认响应—通常是忽略信号或杀死进程。但是不管怎样都不会引起安全风险。由于该函数从触发它的进程中分离出来(如前所述),所以,不管原始进程在此处是否改变其原来分配的信号,句柄都不会产生任何影响。

24405:关闭调用进程打开的所有文件。最重要的是,这意味着modprobe程序不再从调用进程中继承标准输入输出和标准错误。这很有可能会引起安全漏洞(这可能是在替代modprobe的程序中引起的问题,但是modprobe本身实际上并不关心这个差异)。

24413:modprobe程序作为root运行,它拥有root所拥有的所有权限。和整个内核中其他地方一样,请注意root使用用户ID号0的假定在这里已经被写入程序。用户ID号和权能系统(capability system,在接下来的几行中会用到)将在第7章中介绍。

24421:试图执行modprobe程序。如果尝试失败,内核将使用printk打印错误消息并返回错误代码。这里是可能产生printk的缓冲器过载的地点之一。module_name的长度并没有明确限制,就我们对该调用的看法而言,它可能长达一百万个字符。为防止printk缓冲器过载,你必需遍历所有对于该函数的调用(实际上是对request_module的调用),以保证每个调用者使用足够短的、不会为printk造成麻烦的模块名。

24427:当execve成功执行时,它不会返回任何结果,因此本处是不可能执行到的。但是编译器却并不知道这一点,因此,此处使用了return语句以保证gcc不出错。

对于内核的进一步讨论将超出本章的既定范围,因此在这个问题上我们到此为止。然而本书中也包括了其他必需的内核代码。在读完第4章和第5章之后,也许你会希望再次仔细研读一下这部分内容。有关这个问题的两个文件是include/linux/module.h(从15529行开始)和/kernel/module.c(从24476行开始)。和sys_create_module(24586行)、sys_init_module(24637行)、sys_delete_module(24860行)和sys_query_module(25148行)四个函数需要特别注意一样,struct module(15581行)也要特别引起注意。这些函数实现了modprobe及insmod、lsmod和rmmod所使用的系统调用,以完成模块的装载、定位和卸载。

内核触发直接回调内核程序的现象看起来很令人奇怪。但是,实际上进行的工作不止于此。例如,modprobe必须实际访问磁盘以搜寻要装载的模块。而且更为重要的一点是,这种方法赋予root对内核模块系统更多的控制能力。这主要是因为root也可以运行modprobe及相关程序。因此,root既可以手工装载、查询、卸载模块,也可以由内核自动完成。2.3 配置与编译内核

你可能仅仅研读、欣赏而并不修改Linux内核源代码。但是,更普遍的情况是,用户有强烈的愿望去改进内核代码并完成相应的测试,这样我们就需要知道如何重建内核。本节就是要告诉你如何实现这一点,而最终则归结于如何把你所做的修改发行给别人,以使得每个人都能从你的工作中受益。2.3.1 配置内核

编译内核的第一步就是配置内核,这是增加或者减少对内核特性的支持及修改内核的一些特性的必要步骤。例如,你可以要求内核为自己的声卡指定一个不同的DMA通道。如果内核配置和你的需要相同,那么你可以直接跳过本节,否则请继续阅读以下内容。为了完成内核的配置,请先切换到root用户,然后转入如下内核源程序目录: cd /usr/src/linux 接着敲入如下命令组: make config make menuconfig make xconfig 这三条命令都可以用来配置内核,但它们发挥作用的方式各不相同:

?make config—三种方法中最简单也是最枯燥的一种。但是最基本的一点是,它可以适应任何情况。通过为每一个内核支持的特性向用户提问的方式来决定在内核中需要包含哪些特性。对于大多数问题,你只要回答y(yes,把该特性编译进内核中)、m(作为模块编译)或者n(no,根本不对该特性提供支持)。在决定之前用户应该考虑清楚,因为这个过程是不可逆的。如果你在该过程中犯了错误,就只能按Ctrl+C退出。你也可以敲入?以获取帮助。图2-1显示了这种方法在X终端上运行的情况。图2-1 运行中的make config 幸运的是,这种方法还有一些智能。例如,如果你对SCSI支持回答no,那么系统就不会再询问你有关SCSI的细节问题了。而且你可以只按回车键以接受默认的选择,也就是当前的设置(因此,如果当前内核将对于SCSI的支持编译进了内核,在这个问题上按回车键就意味着继续把对SCSI的支持编译进内核中)。即使是这样,大部分用户还是宁愿使用另外的两种方法。

?make menuconfig—一种基于终端的配置机制,用户拥有通过移动光标来进行浏览等功能。图2-2显示了在X终端上运行的make menuconfig。虽然在控制台上显示的是彩色,但是在终端上的显示仍然相当单调。使用menuconfig必须要有相应的ncurses类库。图2-2 运行中的make menuconfig ?make xconfig—这是我最喜欢的一种配置方式。只有你能够在X server上用root用户身份运行X应用程序时,这种配置方式才可以使用(有些偏执的用户就不愿意使用这种方式)。你还必须拥有Tcl窗口系统,这实际上还意味着你必须拥有Tcl、Tk以及一个正在运行的X安装程序。作为补偿,用户获得的是更漂亮的、基于X系统的以及和menuconfig功能相同的配置方法。图2-3显示了在这种方法运行过程中打开“可装载模块支持(Loadable module support)”子窗口的情况。

图2-3 运行中的make xconfig 如上所述,这三种方法都实现了相同的功能:它们都生成在构建内核时使用的.config文件。而唯一的区别在于创建这个文件时的难易程度不同。2.3.2 构建内核

构建内核要做的工作要比配置内核所做的工作少得多。虽然有几种方式都能实现这一功能,但是选择哪一种依赖于你希望怎样对系统进行设置。长期以来,我已经形成了如下的习惯。虽然这种习惯比我所必须要做的略微多一些,但是它包含了所有基本的问题。首先,如果你还不在内核源程序目录中,请先再次转入这一目录: cd /usr/src/linux 现在,切换到root用户,使用下面显示的命令生成内核。现在在shell中敲入下面的命令,注意make命令因为空间关系分成了两行,但实际上这在shell输入时是一个只有一行的命令: make dep clean zlilo boot modules modules_install 当给出了如上多个目标时,除非前面所有的目标都成功了,否则make能够知道没有必要继续尝试下面的目标。因此,如果make能够运行结束,成功退出,那么这就意味着所有的目标都正确构建了。现在你可以重新启动机器以运行新的内核。2.3.3 备份的重要性

当修改(fooling)内核时,你必须准备一个能够启动的备用内核。实现该目的的一种方式是通过配置Linux加载程序(LILO)以允许用户选择启动的内核映象,其中之一是从没有修改过的内核的备份(我总是这样做的)。

如果你比较有耐心,那么你就可以使用zdisk目标而不使用zlilo目标;它可以把能够启动的内核映象写入软盘中。这样你就可以通过在启动时插入软盘的方式启动你的测试内核;如果没有插入软盘,则启动正常的内核。

但是请注意:内核模块并没有被装载到软盘中,它们实际上是装在硬盘中的(除非你愿意承担更多的麻烦)。因此,如果你弄乱了内核模块,即使是zdisk目标也救不了你。实际上,上面提到的这两种方法都存在这个问题。虽然有比较好的解决方法可用,但是最简单的方法(也就是我所使用的方法)是把备份内核作为严格独立的内核来编译,而不使用可装载模块的支持。通过这种方法,即使我弄乱了内核而不得不使用备份启动系统,那么不管问题是实验性内核不正确还是内核模块的原因都无关紧要。不管怎样,在备份的内核中已经有我需要的所有东西了。

由于用户所做的修改可能导致系统的崩溃,如损坏磁盘上的数据等等,并不仅仅只是打乱设备驱动程序或文件系统,在测试新内核之前,备份系统的最新数据也是一个英明的决策(虽然设备驱动程序的开发不是本书的主题,但是必需指出的是,设备驱动程序的缺陷可能会引起系统的物理损坏。例如显示器是不能备份的,而且因价格昂贵而不易替换)。作为一个潜在的内核黑客,你的最佳投资(当然是读过本书以后)是一个磁带驱动器和充足的磁带。2.3.4 发布你的改进

下面是有关发布你所做修改的一些基本规则:

?检查最新发行版本,确保你所处理的不是已经解决了的问题。

?遵守Linux 内核代码编写的风格。简要的说就是8字符缩进以及K&R括号风格(if,else,for,while,switch或者do后面同一行中紧跟着开括号)。在内核源程序目录下面的文档编写和代码风格文件给出了完整的规则,不过我们已经介绍了其中的关键部分。注意本书中包含的源程序代码为节省空间而进行了大量的重新编辑,在该过程中我可能打破了其中的一些规则。

?独立发行相对无关的修改。这样,只想使用你所做的某部分修改的人就可以十分方便地获得想要的东西,而不用一次检验所有的修改内容。

?让使用你所做修改的用户清楚他们可以从你的修改中获取什么。同样,你也应该给出这些问题的可信度。你是15min之前才匆匆完成你的修改,甚至还没有时间对它们进行编译,还是已经在你和你的朋友的系统中已长期稳定地运行过这个修改?

假设现在你已经准备好发行自己的修改版本了,那么要做的第一步是建立一个说明你所做的修改的文件。你可以使用diff程序自动创建这个文件。结果或者被称为diffs,或者在Linux中更普遍的被称为补丁(patch)。

发布的过程十分简单。假设原来没有修改过的源程序代码在linux-2.2.5目录下,而你修改过的源程序代码在linux-my目录下,那么只要进行如下的简单工作就可以了(只有在链接不存在的情况下才需要执行ln):

现在,输出文件my.patch包含了其他用户应用这个修改程序时所需要的一切内容。(警告:如上所述,两个源程序间的所有差别都会包含在这个补丁文件中。diff不能区分修改部分之间的关系,所以就把它们都罗列了出来。)如果补丁文件相对较小,你可以使用邮件直接发往内核邮件列表。如果补丁很大,那么就需要通过FTP或者Web站点发布。这时发给邮件列表的信件中就只需要包含一个URL。Linux内核邮件列表的常见问题解答(FAQ)文件位于http://www.xiexiebang.com。

阿道夫 的 范德萨 啊订单式

第二篇:关于分析今天长安保定百度爱问爱学位论文写作方法学位论文.

的的范德萨的地方爱的规格爱你啊好文章梁慧星:学位论文写作方法(2):

学位论文的结构

三、学位论文的结构

答辩委员会成员或委员会外的专家对硕士、博士论文作鉴定、写评语,有一个内容是就论文结构表态。一篇合格的学位论文,要求结构合理。肯定的评语是:本文结构合理、逻辑严谨、层次清晰。什么叫“结构合理”?结构合理就是指论文的“层次清晰”、“逻辑严密”。这就要求了解论文的一般结构,这里注重讲硕士论文、博士论文的结构。

学术论文的结构:

目录

序言

导论

本论

结论

参考文献目录

后记

上述结构中,导论、本论、结论三部分构成论文的本体;目录、序言、参考文献目录和后记,是附属部分。最重要的当然是本体。一篇完整的学位论文,其本体由导论、本论、结论三部分构成。有没有特殊的,有特殊的,所谓特殊,无非是在一般结构基础上省略了其中的某个部分,或者省略结论,或者省略导论,但无论如何不能省略本论。如果以重要性为标准进行划分,则导论和结论属于组成部分,本论属于本质部分。例如一个人,头和躯干是本质部分,四肢是组成部分。没有手臂,甚至手脚都没有,不影响人这个事物的存在,仍然是人;但没有头和躯干,光有四肢就不成其为人。同理,省略了导论、结论,不影响学术论文的本质,但学术论文的完整性大大受到损害,专家作鉴定会写上一句:结构不完整。当然,学术论文不可能没有本论,假设没有本论,就不成其为学术论文。

可见,本论部分特别重要,答辩委员会成员评价学位论文结构合理不合理,注重的是本论部分。

下面对各部分作简要说明:

(一)导论

导论起什么作用?导论的作用在于引起读者的阅读兴趣。读者拿到一篇学术论文,通常好多万字、二三十万字,是否值得花费宝贵的时间,光看题目还难以判断,总是首先读导论,希望从导论的内容判断本文是否有阅读价值,是否值得花费时间阅读。

导论的内容,通常是交待课题,本文究竟要研究一个什么课题,这个课题的产生背景,说明作者为什么要研究这个课题,它有什么理论意义和现实意义。如果是博士论文,通常还要交待所采用的研究方法,交待论文的大体结构。

实例1:

博士论文:合同自由与公序良俗

第一章 导论

(一)选题背景及意义

(二)研究状况和文献综述

(三)研究方法和主要内容

实例2:

博士论文:国际货物贸易中的补贴与反补贴法律问题研究

导论

(一)本文研究的目的和意义

(二)本文研究范围

(三)素材选取与研究方法

(四)体例安排

(二)结论

学术论文应当有结论,是学术研究的规律性决定的。学术研究是一个过程,有其始端和终端。导论是始端,结论是终端。结论表明一项科学研究的结束。同时,一项研究当有其研究结果。最终得到一个什么研究结果,应当在结论部分作出概括。如果还有遗留问题没有解决,也应在结论部分指出。

从学位论文答辩的角度讲,论文要经专家鉴定,写出评语。答辩委员会成员也要审读论文,写出评语。考虑到一篇博士学位论文通常二十多万字,甚至三十多万字,专家教授通常不可能一口气读完。总是读几页,放下了,又读几页,有什么事情又放下了。经过好多次才断续读完,读到末尾,前面的内容已经模糊、记不清了。不可否认,有的评定人因时间关系不可能读完全文,阅读了导论部分,翻阅、选读几个章节,然后就写评语。如果有一个结论,概括本文的研究结果、作者的基本学术见解、本研究结果的理论意义和实践价值,对于审定人作出总的评价有莫大的帮助。这对于论文最后能否通过答辩,有极重大的意义。切不可掉以轻心!

实例:

博士论文:合同自由与公序良俗

第六章 结论

(一)总结

(二)论文的基本观点

(三)论文的主要创新点

(四)论文的局限和不足

有的学位论文以立法建议代替结论,这大抵属于制度型选题,研究某一项法律制度,研究最后得到的基本学术见解或结论,表现为建议我国立法机关制定某一法律或对现行法作修改,并形成了立法或修改的基本设想甚至条文草案。另外,也有以结束语代替结论的。以立法建议代替结论,以结束语代替结论,不等于没有结论。省略结论,影响论文结构的完整性,完整性是合理性的一个方面,因此没有结论将影响论文结构的合理性。

(三)本论

本论是一篇学位论文的本质部分,没有本论就不成其为一篇论文。就象没有头和躯干不能成其为人一样。本论的内容是研究过程的反映,应当写什么,自然不用我在这里说。这里只是介绍本论部分的结构安排。评价一篇论文的结构是否合理,主要是针对本论部分的结构是否合理。

1、本论部分的结构

大体上有五种:

总分结构

三分结构

四分结构

编章结构

章节结构

总分结构,实际是分为两个部分,称为总论与分论。实际上,是哲学上的“一般”与“特殊”、“共性”与“个性”的关系。有关本课题的一般理论、共同理论,在总论部分;然后分别研究本课题内部各特殊部分或特殊问题,叫做分论。实际是“二分结构”:总论、分论。然后总论再分为若干部分(章),分论再分若干部分(章)。如果将总论、分论作为两编,每编下分若干章,这就是“二分结构”加“编章结构”。有的博士论文,在总论、分论之前再设绪论,研究本课题的前提性的问题,作为另一个部分,称为绪论编,包括若干章。这样就变成“三分结构”加“编章结构”。可以说几乎所有的选题,都有总论与分论的划分,都可采用“二分结构”加“编章结构”。但是,如果属于一般理论、共同理论的内容太少,不足以再分为若干章,就会出现这样的情况:总论编只有一章,分论编包括若干章,显得不协调、不成比例。因此可不设编,直接采用“章节结构”,第一章实际是总论,从第二章起实际是分论。下面举一些实例。

采“四分结构”的实例:

蒋新苗的博士论文:国际收养法律制度研究 第一编 导论,包括第一、二章;

第二编 国际收养中的国际私法问题,包括第三、四章;

第三编 国际收养法的统一化进程,包括第五、六章;

第四编 中国与国际收养法统一化进程,包括第七、八、九章。

须说明的是,第一编导论,内容实际是绪论。绪论是本论的一部,导论不是本论的一部。本文省略了结论。

采“三分结构”的实例1:

傅静坤的博士后论文:契约冲突法论

第一部分 契约冲突法的基本原则和规范,包括第一、二章;

第二部分 统一国际契约实体法与统一国际契约冲突法,包括第三、四章;

第三部分 区际契约冲突法,包括第五章。

(本文省略了结论)

采“三分结构”的实例2:

沈涓的博士论文:中国区际冲突法研究

第一编 中国区际冲突法的历史与现状,包括第一至三章;

第二编 中国区际冲突法的方法与规则,包括第四至五章;

第三编 中国区际法律关系冲突的调整,包括第六至十一章。

(本文省略结论)

2、总分结构

这种结构最为常见,通常先划分为总论与分论两大部分,然后各部分再分若干章;或设总论编、分论编,然后各编再分若干章;或不设编,总论作为第一章,分论分为若干章。实际是总分结构加编章结构。多数博士论文、硕士论文采用这样的结构。

实例:

肖厚国的博士论文:物权变动研究

(导论)

第一章 物权变动的基本理论

第二章 物权变动的立法主义

(一)第三章 物权变动的立法主义

(二)第四章 不动产物权变动的公示

(一)第五章 不动产物权变动的公示

(二)第六章 动产物权变动

第七章 善意取得 第八章 取得时效

(结束语)

本论实际上分为“总论”与“分论”两大部分,“总论”再分为“物权变动的基本理论”(第一章)与“物权变动的立法主义”(第二、三章)两部分;“分论”分为“积极的物权变动”与“消极的物权变动”两部分,其中“积极的物权变动”,再分为“不动产物权变动”(第四、五章)与“动产物权变动”(第六章)两部分;“消极的物权变动”,再分为“善意取得”(第七章)与“取得时效”(第八章)两部分。属于典型的“总分结构+章节结构”。

不合理的结构:

如果本论分为两大块,下面不再划分章节,或者本论仅有两章,属于单纯的二分结构,应当认为结构不合理。为什么这样的结构不合理?首先是不合习惯。其次是美学上的考虑。一篇论文,前面一个序言,后面一个结语,本论部分就两章,第一章、第二章。如果序言、结语都省略了,一篇论文就两块,第一部分,第二部分,或者第一章、第二章,这不好看。要进一步追问为什么?也难以回答。可能是太呆板。下面举实例。

实例1:

硕士论文题目:作者精神权利性质探讨

第一部分 概述

第二部分 作者精神权利性质探讨

论文前面没有导言,后面没有结论,本论就两部分,很不合理。

实例2:

一篇硕士论文

序言

第一章

第二章

结语

前有一个序言,后有一个简短的结语,中间本论部分就两章,属于结构不合理。

3、关于切题

关于本论的结构,无论采取哪一种结构模式,其共同的要求是:紧扣题目,亦即我们平常所谓“切题”。这主要从每部分的标题来体现。

从上引博士论文的结构,我们看到,每一个标题,都紧扣住题目。如物权变动研究一文,第一至第六章的标题都有“物权变动”一语,第七、八两章的标题虽然没有“物权变动”四个字,但“善意取得”和“取得时效”是物权变动的具体形式。可见,所谓切题,所谓紧扣题目,往往通过在本论各部分标题中“反复出现”论文题目中的“关键词语”来体现。反之,如果本论各标题与论文题目无关,找不到论文题目的关键词语,我们就会觉得不切题,没有紧扣题目。下面举例。

实例1:

博士论文题目:论私法对国际法的影响

第一章 万民法与国际法

第二章 人或主体

第三章 领土主权与所有权

第四章 条约与契约

评论:

在各部分标题中,没有出现论文题目中的关键词语“私法”和“国际法”。从各部分的标题,读者很难理解该部分内容与题目间是否有密切的关联。这就是没有紧扣题目,或者说不切题。

实例2:

博士论文题目:现代商人法研究

其本论分三章:

第二章 现代商人法产生和发展的历史过程

第三章 现代商人法的适用及其法律效力

第四章 现代商人法与冲突法及国际仲裁法的完善和发展

评论:

题目中的关键词语是“现代商人法”,我们看到本论部分每一个标题都重复“现代商人法”一语,使读者觉得各部分内容与题目的关系非常紧密,扣得很紧,这就叫“切题”。

4、小结

从上述本论部分的结构安排,我们可以看到,博士论文采用总分结构加编章结构,或者采用章节结构的最常见,这类结构安排系以“章”为单位,硕士论文也以章节结构最常见,也有的用“部分”为单位,“部分”下面以一、二、三、四为序。是否可以作出这样一个判断:

无论以“章”或“部分”为单位,本论部分所划分的单位至少应在三个以上,例如不少于三章或三个部分。否则,就叫结构不合理。

5、逻辑关系 以上仅指对结构安排的形式要求。在此基础上,还有对逻辑性的要求。指本论部分的结构安排要具有一定的逻辑关系。大体有下面三种逻辑关系:

第一种 总分关系

第二种 并立关系

第三种 递进关系

第一种:总分关系

关于本课题的一般性问题、一般理论、基本理论、基本原则的内容,属于总论。特殊问题、特殊理论、具体制度、具体问题、构成条件、实际运用等内容,属于分论。总论与分论之间,是一般与个别、普遍与特殊的关系,通常总论部分应当在前,分论部分应当在后。无论采用总分结构或者编章结构、章节结构,都要求总论与分论有清晰的界限,不能混淆,总论内容写完再安排分论,分论部分不能插入总论的内容,不能颠倒顺序,一般不能先分论后总论,应当先总论后分论。

其规则是:先总、后分。

第二种:并列关系

如果采用总分结构,总论部分与分论部分,已具有并列的意义,总论下面的各章、分论下面的各章,也可以是并列的关系,即各章的内容应当处在同一层次。采用编章结构,各编的内容可以是并列的关系,例如绪论编、总论编、分论编,编下面的各章可以是并列关系。

其规则是:位阶同一

实例1:

肖厚国的博士论文:物权变动研究

第一章 物权变动的基本理论

第二章 物权变动的立法主义

(一)第三章 物权变动的立法主义

(二)第四章 不动产物权变动的公示

(一)第五章 不动产物权变动的公示

(二)第六章 动产物权变动

第七章 善意取得

第八章 取得时效

其结构属于典型的并列关系,而且是多层次的并列关系。第一个层次是总论(第1、2、3章)与分论(第4、5、6、7、8章)的并列;第二个层次是总论下面物权变动的基本理论(第1章)与物权变动的立法主义(第2、3章)的并列,分论下面积极的物权变动(第4、5、6章)与消极的物权变动(第7、8章)的并列;第三个层次是不动产物权变动(第4、5章)、动产物权变动(第6章)、善意取得(第7章)、取得时效(第8章)的并列。

实例2:

杨松的博士论文:国际货币基金协定研究

本论分五章:

第四章 国际收支平衡的法律制度研究

第五章 国际储备的法律制度研究

第六章 汇兑安排国际法律制度研究

第七章 外汇管制法律问题研究

第八章 基金协定的监督与磋商机制研究

其结构也属于典型的并列关系。

第三种:递进关系

采编章结构,在一编之下的各章可以是递进关系。采章节结构,各章之间也可以是递进关系。章下面的节,也可以是递进关系。就一篇博士论文而言,可能各编之间是并列关系,各编下面的章是递进关系,或者总论编下面的各章是并列关系,分论编下面各章是递进关系。或者采总分结构不设编,总论仅一章,从第二章开始是分论,分论各章是递进关系。至于章下面的各节之间的关系,当然可以某些章下面的各节之间是并列关系,某些章下面的各节之间是递进关系。

这里有一个要求,某编下面的各章,或者某章下面的各节,如果采递进关系,就一定是递进关系,不能混淆。不能出现这样的情况,一编有五章,其中一、二、三、五章显然是递进关系,中间第四章与各章不构成递进关系,或者一章下面若干节,其中几节似乎是递进关系,中间又有几节似乎是并列关系。

递进关系有三种不同形态:

时间上的递进关系

空间上的递进关系

纯粹逻辑上的递进关系

(1)时间上的递进关系

时间上的递进关系,是指在时间上由远到近,先从该制度的历史说起,从古罗马法说起,中世纪有什么变化,近代有何发展,直到现在的现状,实际是采历史研究方法。法制史研究论文,大体体现这样的递进关系。在部门法,例如民法硕士、博士论文中,也常常采用历史研究方法,因此在论文的某个部分会反映时间上的递进关系,通常在绪论或总论部分,或者某一章下面的节,不大可能一篇民法论文各章之间都反映时间上的递进关系。

这种递进关系,要求严格按照时间的先后顺序,如果出现时间先后顺序的错乱,就叫层次不清、逻辑混乱。

其规则是:时间愈早愈在前,时间愈近愈在后。

(2)空间上的递进关系

此所谓“空间”实际上包括两种情形:一是地域上的空间;一是抽象的空间。无论属于地域上的空间,或者抽象的空间,都要求“由外到内”,先讨论外部的问题,后讨论内部的问题。先研究外国的制度、发展、经验教训,再讨论本国的制度、发展、构成、适用、存在问题及对策等;或者先讨论该制度的外部关系,产生原因、背景、哲学思想、政策取向、功能等,然后进入该制度内部,讨论其构成要件、法律效果、解释适用等。

空间上的递进关系,要求区分内外,先外后内。如果外部问题未讨论完就进入内部问题的讨论,中途再反过来讨论外部问题,或者一开始讨论本国制度,中间突然插入外国制度的探讨,然后再回到本国制度的研究,就叫层次不清、逻辑混乱。

其规则是:先外、后内。

(3)纯粹逻辑上的递进关系

所谓纯粹逻辑上的递进关系,是指在逻辑上由抽象到具体。先从概念、定义说起,解释其含义,探讨其内涵、外延,确定其适用范围,分析适用条件、法律效果。通常采用法律解释学的研究方法,就反映这种递进关系。要求符合从抽象到具体的逻辑顺序,愈抽象的问题愈在前,愈具体的问题愈在后,否则就叫层次不清、逻辑混乱。

其规则是:愈抽象愈在前,愈具体愈在后。

实例:

硕士论文:最高额抵押权研究

第一部分 最高额抵押权的意义

第二部分 最高额抵押权的历史演进

第三部分 最高额抵押权的设定

第四部分 最高额抵押权的效力

第五部分 最高额抵押权的确定

第六部分 最高额抵押权的消灭

其第一、二部分属于总论;第三至第六部分属于分论。其分论的结构显然符合纯粹逻辑的递进关系。须注意的是,在一篇论文中,尤其是长篇专题研究论文、硕士论文、博士论文,不是只反映一种递进关系形态,可能某一编、某一章内部是时间上的递进关系,另外的编、章内部是逻辑上的递进关系。

关于本论部分的逻辑关系,还有一个要求是要有重点,避免等分式、无重点、面面俱到和过分枝蔓。我上研究生的时候,研究生院院长温济泽教授作报告,讲到学术论文写作多次引用前人的诗句:“删繁就简三秋树,领异标新二月花”。前一句就是指,文章的结构,要避免过分枝蔓,要突出重点。

下面举不合逻辑的实例:

实例1:

硕士论文:著作权若干问题研究

一、著作权的成因、发展和不同制度比较;

二、我国著作权制度的历史、现状和立法构想;

三、著作权若干问题的具体探究。

评论:

问题在于逻辑关系混乱,第一部分的成因、发展、不同制度比较与第二部分的历史、现状是重复的;立法构想应该在整个研究完成之后提出,却安排在第二部分,全部研究未完成,尤其对著作权的若干基本问题还未研究清楚,就提出立法构想,也不合逻辑。

实例2:

硕士论文:论新闻侵权为题的硕士论文

第一章 新闻侵权概述

第二章 新闻侵害名誉权的民事责任

第三章 新闻侵害隐私权的民事责任

第四章 新闻侵害肖像权的民事责任

第五章 新闻侵权民事责任主体

第六章 新闻侵权损害的救济方式

评论:

问题在于总分颠倒,第二、三、四章属于分论,却安排在前面,第五章新闻侵权的责任主体和第六章救济方式应当属于总论,却安排在后面。

实例3:

硕士论文:论企业集团的法律地位

一、企业集团产生的客观必然性;

二、企业集团的概念;

三、企业集团的类型及其规范化;

四、企业集团的法律地位是由其内外关系决定的;

五、问题与对策。

评论:

问题是各部分逻辑关系混乱。连什么是企业集团都未介绍,一开头就讲客观必然性,不合思维习惯和逻辑。思维习惯和逻辑是,先讲“是什么”,再讲“为什么”。概念属于“是什么”,本应当在前面,却安排在第二部分。客观必然性属于“为什么”,本应当在后面。第四部分是一个完整的句子,与标题不合,标题不能是句子,再说也与其他部分不协调。

实例4:

硕士论文:论时效制度

一、对时效制度的历史考察

二、关于消灭时效效力的探讨

三、我国民法是否需要设立取得时效制度

四、对完善我国民法时效制度的设想

五、时效完成后义务人的履行

六、除斥期间

评论:

同样逻辑混乱,第三、四部分交叉、重复,层次不清,第六部分除斥期间属于课题之外的问题。

实例5:

硕士论文:论民法的缔约过失责任

一、缔约过失责任的由来

二、缔约过失责任的几个基本理论问题

三、英美法系的缔约过失责任――非合同义务

四、缔约过失责任的新发展与合同预备性文件的效力

五、我国缔约过失责任的理论与实践

评论:

存在的问题是逻辑混乱:“缔约过失责任”是否包括英美法系的缔约过失责任?如果回答是肯定的,则何以单独论及英美法系的缔约过失责任,而未专门论及大陆法系的缔约过失责任?如果回答是否定的,即缔约过失责任仅指大陆法系的制度,则何以在研究过程的中间,突然提出英美法系的缔约过失责任问题?

实例6: 硕士论文:保证责任研究

一、保证责任的成立;

二、保证责任的性质;

三、保证责任的范围;

四、保证责任的方式;

五、保证责任的期间;

六、保证责任的消灭。

评论:

相对而言,成立、范围、方式、期间、消灭都是具体的,唯性质是抽象的。而将性质安排在成立之后、范围等之前,违背了“愈抽象愈在前”的规则,造成逻辑混乱。如果在前面设一个部分:保证责任概述,在其中论及责任性质,就合乎逻辑了。

实例7:

硕士论文:融资性租赁合同研究

一、融资性租赁合同的概念及特征;

二、融资性租赁合同的订立及条款;

三、融资性租赁合同的担保;

四、融资性租赁合同责任。

评论:

按照思维的逻辑习惯,一提到合同的订立,马上会想到合同的生效、合同的履行,这也正是事物本身的逻辑。但本文在论及订立之后,却未论及合同的生效、合同的履行等问题,而仅研究合同的担保。其逻辑难谓合理、严密。

实例8:

硕士论文:论农地承包经营权

第一部分 农地承包经营权的基本特点和主要缺点

第二部分 农地承包经营权的革新

(一)第三部分 农地承包经营权的革新

(二)第四部分 他国(地区)农地使用制度与农业发展的经验介绍

第五部分 农地承包经营权规范化建构的制约因素及其发展态势分析

第六部分 农地承包经营权目标模式的建构

评论:

问题是,先讲中国,后讲外国,最后再来讲中国,违反先外国后本国的空间上的递进关系。逻辑关系是混乱的。

实例9:

硕士论文:加害给付研究

第一章 德国法中的积极侵害债权

第二章 中国法的加害给付

第三章 加害给付的构成要件及法律效果

第四章 民事责任竞合概述及责任比较

第五章 外国民事责任竞合处理

第六章 中国法的责任竞合

评论:

问题是题目确定的研究范围不能涵盖第四、五、六章的“民事责任竞合”,各章之间逻辑关系不清。

实例10:

硕士论文:我国合同解除制度立法研究:

第一章 关于合同解除的历史考察

第二章 我国现行合同法关于合同解除的规定及其问题

第三章 合同解除的概念和意义

第四章 解除权的性质、种类与发生原因

第五章 行使法定解除权的原因

第六章 行使解除权的方法

第七章 合同解除的效力

第八章 合同解除权的消灭

评论:

问题主要是第二章我国现行合同法关于我国合同解除的规定及其问题,本应当安排在本文最后予以分析并提出对策建议,却安排在第二章,不符合逻辑思维顺序和习惯,破坏了其他各章从远到近、从抽象到具体的递进关系。

6、对各部分标题的要求

第一项要求:标题应当是名词或名词性短语,不能是一个句子

第二项要求:标题只确定本部分的研究对象,不表达作者观点

第三项要求:标题应明确、简短而忌冗长

第四项要求:标题应当出现题目中的关键词

第五项要求:同一层次的各标题应相互协调

实例1:

博士论文:荷兰国际私法研究

第一章 荷兰国际私法概述

第二章 荷兰与国际私法统一化

第三章 荷兰国际私法法典化编纂

评论:

我们看到各章的标题,都是名词性短语,而不是句子,符合第一项要求;各标题只是确定本章研究对象或范围,而不表达作者观点,符合第二项要求;各标题符合明确、简短而不冗长的第三项要求;论文题目中的关键词,亦即“动宾结构”中的名词“国际私法”,在各章标题中重复出现,这也就是所谓“切题”,符合第四项要求;各标题结构、长短比较协调,符合第五项要求。

再看其中第一章下面的各节:

第一节 荷兰国际私法的概念

第二节 荷兰国际私法的渊源

第三节 荷兰国际私法的性质

第四节 荷兰国际私法的历史发展

同样符合关于标题的五项要求。

实例2:

博士论文为例:责任保险论

第一章 责任保险概述

第二章 责任保险的分类

第三章 责任保险合同

第四章 责任保险人的给付责任

第五章 责任保险的第三人

第六章(责任保险的)抗辩与和解的控制

第七章 责任保险人的抗辩义务

第八章 责任保险与再保险

评论:

同样符合上述五项要求。须补充的是,在章节标题设计上,可能出现在一个标题中有两个名词性短语,亦即一个章、节可以有两个或三个研究对象。在节以下层次的标题,第三项要求,可以不象章、节(特别是章)那样严格。再就是,有的论文章标题似未重复论文题目中的关键词,而直接采用论文题目关键词的下位概念,以作为各章标题的宾语短语。

实例3:

博士论文为例:国际货币基金协定研究 第四章 国际收支平衡的法律制度研究

第五章 国际储备的法律制度研究

第六章 汇兑安排国际法律制度研究

第七章 外汇管制法律问题研究

第八章 基金协定的监督与磋商机制研究

评论:

实际上可以认为,各章标题中省略了论文题目中的关键词“国际货币基金协定”:

第五章(国际货币基金协定中的)国际收支平衡的法律制度研究

第六章(国际货币基金协定中的)国际储备的法律制度研究

第七章(国际货币基金协定中的)汇兑安排国际法律制度研究

第八章(国际货币基金协定中的)外汇管制法律问题研究

第九章(国际货币)基金协定的监督与磋商机制研究

不适当的实例1:

博士论文:论私法对国际法的影响

第一章 万民法与国际法

第二章 人或主体

第三章 领土主权与所有权

第四章 条约与契约

评论:

问题是不符合关于标题的第三项要求:各章标题中没有出现论文题目中的关键词,因此给人的印象是不切题。如果对各章标题的文字作一些调整,效果就会不同:

第一章 万民法与国际法

第二章 私法主体与国际法主体

第三章 私法所有权与国际法领土主权

第四章 私法契约与国际法条约

不适当的实例2:

硕士论文:论物权立法

其第三部分 我国现实历史条件下物权立法之必要性研讨

第一节 建立我国完整统一的物权制度是马克思所有权理论的必然要求

第二节 建立完整统一的物权制度是我国现经济基础的客观要求

第三节 建立完整而统一的物权制度是我国现实司法实务更有利于保护公民法人的合法权益的客观要求

评论:

存在的问题是,标题不是一个名词性短语,而是一个完整的句子,违背第一项要求;不是确定各节研究对象、研究范围,而是直接表达作者观点,违背第二项要求;各标题十分冗长,第三节标题长达41个字,显然违背第四项要求;第一节标题“建立我国完整统一的”,第二节标题“建立完整统一的”,第三节标题“建立完整而统一的”,甚不协调,违背第五项要求。

如果作下述调整,效果将会改观:

第一节 从马克思所有权理论看我国物权立法

第二节 从现实经济基础看我国物权立法

第三节 从公民法人合法权益保护看我国物权立法

结语

硕士论文、博士论文的结构,除导论、本论、结论三部分外,前面必须有目录,还可以有序言。后面必须有参考著作目录,可以再写个后记。后记的内容没有一定之规,通常是致谢及发感慨。

阿道夫 的 范德萨 啊订单式

第三篇:关于分析今天保定百度百度爱问爱第3章C54x DSP系统硬件结构DSP芯片

关于分析今天保定百度百度爱问爱第3章C54x DSP系统硬件结构DSP芯片.txt

-你脚踏俩只船,你划得真漂亮。-每个说不想恋爱的人 心里都装着一个不可能的人。我心疼每一个不快乐却依然在笑的孩子。(有没有那么一个人,看透我在隐身,知道我在等人。的的的地方地方爱的规格爱你啊好文章第3章 C54x DSP系统硬件结构

DSP芯片是一种特殊结构的微处理器,为了快速地实现数字信号处理运算,采用了流水线指令执行结构和相应的并行处理结构,可在一个周期内对数据进行高速的算术运算和逻辑运算。本章主要介绍C54x DSP芯片的硬件结构,重点对芯片的引脚功能、CPU结构、内部存储器、片内外设电路、系统控制以及内、外部总线进行讨论。3.1 C54x DSP的基本架构 TMS320 C54x DSP(简称C54x)是TI公司为实现低功耗、高速实时信号处理而专门设计的16位定点数字信号处理器。其内核包含在第1章已经讨论过的哈佛结构和高级算术特点中。另外,C54x还具有多总线结构以及强大的片上外设,具有高度的操作灵活性和运行速度,适应远程通信等实时嵌入式应用的需要,现已广泛地应用于无线通信系统中。3.1.1 C54x DSP的基本结构图

一块DSP芯片上集成CPU、片内存储器、外围电路、总线以及外部总线接口。图3-1所示为TMS320C54x基本结构框图,它包含了主要模块和总线结构。图3-2所示为TMS320C54x功能结构图。与传统微处理器相比较,DSP最显著的结构特点是具有高效存取数据、单周期乘法器和零开销硬件循环等。3.1.2 C54x DSP的主要特征

1.具有快速处理性能的CPU部分 CPU是DSP芯片中的核心部分,CPU内的硬件构成决定其指令系统的性能。采用了流水线指令执行结构和相应的并行处理结构,可在一个周期内对数据进行高速的算术运算和逻辑运算,TMS320C54x的CPU包括以下几部分:

(1)先进的多总线结构,包括1条程序总线、3条数据总线、4条地址总线和外设总线;(2)40位算术逻辑单元(ALU),包括1个40位的桶形移位寄存器和2个独立的40位累加器;

(3)17×17的并行乘法器,并与1个40位的专用加法器配合,用于非流水线的单周期乘/累加操作;DSP芯片技术及工程实例第3章 C54x DSP系统硬件结构图3-1 TMS320C54x基本结构框图

图3-2 TMS320C54x功能结构图

(4)比较、选择和存储单元,用于维特比运算中的加/比较选择;(5)指数编码器,可以在单周期内计算40位累加器的指数值;

(6)2个地址生成器,包括8个辅助寄存器和2个辅助寄存器算术单元;(7)双内核结构(只适用于C5420).2.具有哈佛结构的存储器系统

(1)具有独立的程序存储器和数据存储器,可同时访问,使许多处理运算比传统的冯·诺依曼结构有效得多。

(2)具有192K字可寻址存储空间,包括片内、外64K字程序存储空间,片内、外64K字数字存储空间和片外64K字的I/O空间。其中一些型号DSP的程序存储器空间可扩展至8M字,例如TMS320C548、TMS320C549、TMS320C5402、TMS320C5410和TMS320C5420.(3)提供一定容量的片内存储器,片内存储器配置因型而异,包括片内ROM和RAM,通过内部多总线,CPU可以同时、快速地访问它们,以实现并行处理。但对于外部存储器,DSP提供了外部接口,它与内部多总线结构复接,但外部只有一组I/O接口线,所以不能在单周期内并行实现读写操作。3.片内外设和专用电路

除了DSP内核外,DSP芯片上还需配置一些外设专用器件。这些器件可以与DSP内核平行操作,只占用很小的内核指令周期,依靠这些器件无缝出入DSP处理内核的能力,可大大提高DSP处理数据的能力。TMS320C54x的片内外设和专用电路采用模块化的结构设计,常见的外设包括以下几种。

(1)可编程软件等待状态发生器。(2)可编程分区转换逻辑电路。

(3)可采用内部振荡器或外部时钟源的片内锁相环(PLL)时钟发生器。

(4)外部总线接口可以禁止或允许外部数据总线、地址总线和控制线的输出。(5)数据总线具有总线保持功能。(6)可编程定时器。

(7)8位并行主机接口(HPI),有些产品还包括扩展的8位并行主机接口(HPI8)和16位并行主机接口(HPI16).(8)片内的串行口按不同的型号分为全双工串口(支持8位和16位数据传送)、时分多路(TDM)串口和缓冲(BSP)串口。

C54x系列定点DSP芯片共享同样的CPU内核和总线结构,但每一种器件片内存储器的配置和片内外设不尽相同。表3-1列出了TMS320C54x系列DSP基本配置汇总。表3-1 TMS320C54x系列DSP基本配置汇总表型 号电压/VcoreI/O片内存储器RAM/ KBROM/ KBDAT/ PRO/B外 设McBSPTimerHPIDMAMIPS封 装C54011.83.384128K/2M228位6通道50144LQFP/ 144BGAC54021.6/ 1.83.3328/32128K/2M/ 128K/16M2/31/28位6通道100/ 160144LQFP/ 144BGAC54041.53.332128128K/16M328/16位6通道120144LQFP/ 144BGA续表型 号电压/VcoreI/O片内存储器RAM/ KBROM/ KBDAT/ PRO/B外 设McBSPTimerHPIDMAMIPS封 装C54071.63.380256128K/16M328/16位6通道120144LQFP/ 144BGAC54091.5~

1.83.36432128K/16M318/16位6通道80~ 160144LQFP/ 144BGAC54101.5~

2.53.312832128K/16M318/16位6通道100~ 160144LQFP/ 144BGAC54161.5/ 1.63.325632128K/16M318/16位6通道120/ 160144LQFP/ 144BGAC54201.83.32000128K/16M6216位12通道200144LQFP/ 144BGA4.指令系统

在TMS320C54x的指令系统中,具有单指令重复和块指令重复操作指令,32位长操作数指令,同时读入2个或3个操作数的算术指令。支持存储器块传送指令,能并行存储和并行加载的算术指令,支持条件存储指令及中断快速返回指令。5.执行速度

对TMS320C54x而言,其执行单周期定点指令时间为25/20/15/12.5/10ns(对应每秒指令数分别为40/50/66/80/100MIPS).6.电源和功耗

TMS320C54x DSP芯片可采用5V、3.3V、3V和1.5V、1.8V或2.5V的超低电压供电。而且其功耗可采用下降指令IDLE1、IDLE2和IDLE3来控制,以便使DSP工作在节电模式下可控制关断CLKOUT信号。7.芯片仿真功能 具有符合IEEE 1149.1标准的片内仿真JTAG接口,其主要功能是用于与主机相连接,实现芯片的仿真与测试。3.2 总线结构

按照结构来区分,又可将总线分成内部总线和外部总线,本节只介绍内部总线,外部总线的结构和功能将在3.8节介绍。

为了提高CPU高度的并行性,达到最大的处理能力,例如在单周期内完成算术、逻辑和位操作等运算,TMS320C54x DSP片内采用多总线结构,用8条总线,可同时对程序指令和数据进行双访问,这8条16位的总线包括4条程序/数据总线和4条地址总线。另外,CPU访问片内外设是通过在片双向总线来实现的,如图3-2所示的功能结构图。正是这种改进型哈佛总线结构,形成了支持高速指令执行的硬件基础。

(1)程序总线(PB): 传送来自程序存储器的指令代码和立即数。

(2)3组数据总线(CB、DB和EB): 负责将片内的各种元器件相互连接,如CPU、数据地址产生逻辑、程序地址产生逻辑、片内外设和数据存储器等。其中,CB和DB总线传送从存储器读出的数据,即“读”操作使用的数据总线;EB总线传送向存储器写入的数据,即“写”操作使用的数据总线。

(3)地址总线(PAB、CAB、DAB、EAB): 负责传送执行指令所需的地址。

(4)在片双向总线: TMS320C54x用一组双向的片内总线访问片内外设,这组总线轮流使用DB和EB与CPU连接。用这组总线进行读/写操作需要两个或更多的周期,具体所需周期数取决于片内外设的结构。

TMS320C54x能利用两个辅助寄存器算术单元(ARAU0和ARAU1)在同一个周期内生成两个数据存储器地址,可实现片内RAM 的双访问功能。

表3-2列出了各种不同类型的总线访问形式。从表中看到,C54x器件在任何给定的机器周期内可执行4个并行存储器操作: 1次取指、读取2个操作数和写1个操作数。或通过CB、DB、PB总线同时取操作数,可在一个机器周期内完成从数据存储器读双数据同时从程序存储器读一个常数的3个操作数读取,而片上外设的读、写则是通过DB和EB总线轮流与CPU连接完成,所以使用这组总线进行读/写操作需要两个或更多的周期。表3-2 C54x DSP读/写操作占用总线情况读/写方式地 址 总 线PABCABDABEAB程序总线

PB数 据 总 线CBDBEB程序读√√程序写√√单数据读√√双数据读√√√√32位长数据读√(hw)√(lw)√(hw)√(lw)单数据写√√数据读/数据写√√√√双数据读/系数读√√√√√√外设读√√外设写√√3.3 存储器结构 TMS320C54x DSP存储器采用改进型哈佛结构。与冯·诺依曼结构的存储器相比较,哈佛结构的程序/数据总线和空间是分开的,冯·诺依曼结构的程序/数据总线和空间是合二为一的,而改进型哈佛结构的部分程序/数据空间可交叉,因此提供了高度的并行性。3.3.1 DSP存储器空间的划分 C54x DSP的存储空间共192K字,由3个独立可选的存储空间组成,包括64K字的程序存储空间、64K字的数据存储空间和64K字的I/O空间。其中有些型号芯片的程序空间还可以进一步扩展。

存储器分为片内存储器和片外存储器。片内存储器有3种类型: 双访问RAM(DARAM)、单访问RAM(SARAM)和ROM.RAM总是安排到数据存储空间,但也可以配置在程序存储空间。C54x片上还有26个映射到数据存储空间的CPU寄存器和外设寄存器。ROM一般映射到程序存储空间,也可以部分地映射到数据存储空间。在TMS320C54x DSP中,片外存储器主要包括程序存储器、数据存储器、I/O空间。与片外存储器相比,片内存储器不需插入等待状态,成本低,功耗低。但是,片外存储器具有寻址较大存储空间的能力,而片内存储器寻址存储空间较小。C54x通过设置处理器工作方式状态寄存器(PMST)中的3个状态位MP/MC、OVLY和DROM(详见3.4.1节),可以很方便地“使能”和“禁止”程序与数据空间中的片内存储器。(1)MP/MC位

MP/MC位决定是否使用片内ROM.若MP/MC=0,称微计算机模式,片内ROM使能并能够访问。若MP/MC=1,称微处理器模式,表示片内ROM无法访问。(2)OVLY位

OVLY位决定是否让数据存储器映射到程序存储器空间。若OVLY=0,片内RAM只映射到数据存储空间。

若OVLY=1,片内RAM同时映射到程序和数据存储空间。(3)DROM位

DROM位决定是否让部分程序存储器映射到数据存储器空间。若DROM=0,片内ROM不映射到数据存储空间。若DROM=1,部分片内ROM映射到数据存储空间。DROM位的用法与MP/MC位的用法无关。不同的C54x的数据和程序存储区分配并不完全相同。图3-3(a)所示为TMS320C549存储器空间分配图,图3-3(b)所示为TMS320C5416存储器空间分配图,从图中可以看到在任何一个存储空间内,RAM、ROM都可以驻留在片内或者片外,但需要通过对3个状态位MP/MC、OVLY和DROM的设置来配置。图3-3 存储器空间分配图 图3-3(续)

所有C54x DSP器件提供一定数量的片内ROM和RAM, DSP有两种类型的RAM,包括双寻址RAM(DARAM)和单寻址RAM(SARAM)。表3-3列出了不同C54x系列DSP 片内各种存储器的配置。

1.片内ROM 片内ROM是程序存储器的一部分,对某些DSP器件来说,也可是数据存储空间的一部分,如C5402。当MP/MC设置为0时,可以映射到程序存储空间的ROM为4KB;当DROM设置为1时,可以映射到数据存储器空间的ROM为4KB。不同芯片的片内可用ROM容量是不一样的,见表3-3。对于ROM少的DSP器件(2KB), ROM中含有自举加载器,在程序启动时,将用户的代码从慢的外部ROM、串口,或JTAG加载到内部存储器,这样可以加快程序的运行速度。对于具有较大ROM的器件,部分ROM可以映射到数据和程序空间,用户提供的代码或数据以目标文件格式写入ROM, TI公司可以将程序掩膜到该ROM中。2.片内双寻址RAM(DARAM)所谓DARAM,就是在一个指令周期内,CPU可对其进行读和写两次存取操作,DARAM由块组成,CPU能在每个周期内对同一块DARAM进行读和写。DARAM总是映射在数据存储器空间,用于存储数据,当OVLY设置为1时,它也可同时映射在程序存储器空间,用于存储程序代码。表3-3 常见的C54x系列DSP片内存储器配置KB存储器类型C541C542C543C545C546C548C549C5402C5416C5420ROM***程序ROM***程序/数据ROM800161600400DARAM***SARAM***3.片内单寻址RAM(SARAM)在一个指令周期内只能进行一次读或写操作。SARAM也由块组成,与DARAM一样,SARAM总是映射在数据存储器空间,用于存储数据,当OVLY设置为1时,也可同时映射在程序存储器空间,用于存储程序代码。4.存储器映射寄存器

CPU内部专用寄存器和片上外设寄存器总是映射在数据存储器的0页上,对它们的访问很简单,存储器映射访问提供了一种方便途径,用于寄存器的存储和恢复,也用于累加器与其他寄存器之间的信息传递。3.3.2 程序存储器

程序存储器空间存放要执行的指令和执行中所用的系数表。C54x DSP可以寻址64 K字的程序存储空间。但也有一些型号的DSP可以扩展到8 M字,如C548、C549、C5410、C5402和C5420。下面分别讲述程序存储器的组织、片内ROM 的安排、扩展程序存储器等内容。1.程序存储器空间的配置

程序存储器空间由片内和片外程序存储器组成。如前所述,片内程序空间的组织主要通过设置MP/MC、OVLY位来实现。表3-4列出了各种C54x芯片片内程序存储器配置情况。MP/MC和OVLY 位决定了哪个片内存储器在程序空间中可用。例如,当OVLY设置为1时,DARAM或SARAM才能被配置到程序存储器空间;而只有当MP/MC设置为0时,片内ROM才可用。MP/MC是DSP的一个外部引脚,设置MP/MC状态有两种方法,一种是直接给MP/MC引脚低电平或高电平,当DSP器件复位时,MP/MC引脚的逻辑状态被传送到PMST寄存器的MP/MC位。另一种方法是用户通过软件来设置或清除PMST寄存器的MP/MC位,以便禁止或使能片内ROM。如果片内存储器配置到程序存储器中,则芯片在访问程序存储器时会自动访问这些存储单元。当PAGEN产生了一个不在片内存储器的地址时,会自动使用一个外部总线操作。C54x器件程序存储器空间配置图如图3-3(a)和图3-3(b)所示。表3-4 TSM320C54x芯片片内程序存储器配置KB型 号ROMDARAM(OVLY=1)SARAM(OVLY=1)TMS320C541285-TMS320C542210-TMS320C543210-TMS320C545486-TMS320C546486-TMS320C5482824TMS320C54916824TMS320C5402416-TMS320C541616856TMS320C5420-32168 2.片内ROM 的组织和内容

为了增强处理器的性能,将片内ROM再细分为若干块,这样就可以在片内ROM的一个块内取指的同时又在别的块中读数据。图3-4所示为各种C54x器件的片内ROM分块图。根据C54x器件的不同,ROM可以组织为2KB、4KB或8KB的块。图3-4 C54x 器件片内ROM分块图

(1)对于2K-ROM器件,其ROM 块为2K字。

(2)对于4K-ROM和28K-ROM 器件,其ROM块为4K字。(3)对于16K-ROM和48K-ROM器件,其ROM块为8K字。C54x的片内ROM容量有大(28K或48K字)有小(2K或4K字),但对于程序存储器空间的最高2K字程序空间(F800h~FFFFh),容量大的片内ROM(C541/C545/C546)可以把 阿道夫 的 范德萨 啊订单式

下载关于分析今天长安保定百度爱问爱第2章代码初识 本章首先从较高层次word格式文档
下载关于分析今天长安保定百度爱问爱第2章代码初识 本章首先从较高层次.doc
将本文档下载到自己电脑,方便修改和收藏,请勿使用迅雷等下载。
点此处下载文档

文档为doc格式


声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:645879355@qq.com 进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。

相关范文推荐