• 1. 第一章 操作系统概述认识操作系统 操作系统的发展 开放源代码的Unix/Linux操作系统 Linux内核 Linux内核源代码 Linux 内核模块编程入门Linux 内核中链表的实现及应用
  • 2. 不同角度看到的操作系统操作系统整体看操作系统设计者使用者普通开发者认识操作系统
  • 3. 打开计算机,首先跳入眼帘的是什么? 要拷贝一个文件,具体的拷贝操作是谁完成的? 你需要知道文件存放在何处吗? 柱面、磁道、扇区描述什么? 数据的搬动过程怎样进行 繁琐留给自己,简单留给用户 操作系统穿上华丽的外衣-图形界面 操作系统穿上朴素的外衣-字符界面<>认识操作系统-从使用者的角度看
  • 4. 拷贝命令的C语言实现片断 <>inf=open(“/floppy/TEST”,O_RDONLY,0); out=open(“/mydir/test”,O_WRONLY,0600); do{ l=read(inf,buf,4096); write(outf,buf,l); } while(l); close(outf); close(inf);认识操作系统-从程序开发者的角度看
  • 5. <>浏览器 信息管理 文件管理系统 游戏 编译程序 编辑程序 命令 解释程序 操作系统 CPU、内存、I/O接口硬件内核 认识操作系统-从所处位置看
  • 6. 操作系统是其它所有用户程序运行的基础。 <>#include main() { printf(“ Hello world\n”) } 用户告诉操作系统执行test程序 操作系统通过文件名找到该程序 检查其类型,检查程序首部,找出代码和数据存放的地址 文件系统找到第一个磁盘块 操作系统建立程序的执行环境 操作系统把程序从磁盘装入内存,并跳到程序开始处执行 该程序的执行过程简述如下:操作系统检查字符串的位置是否正确 操作系统找到字符串被送往的设备 操作系统将字符串送往输出设备窗口系统确定这是一个合法的操作,然后将字符串转换成像素 窗口系统将像素写入存储映像区 视频硬件将像素表示转换成一组模拟信号控制显示器(重画屏幕) 显示器发射电子束。你在屏幕上看到Hello world。 从中看到什么认识操作系统-从程序执行看
  • 7. 从操作系统设计者的角度看 操作系统的设计目标是什么? 尽可能地方便用户使用计算机 让各种软件资源和硬件资源高效而协调地运转起来。 计算机的硬件资源和软件资源各指什么? 假设在一台计算机上有三道程序同时运行,并试图在一台打印机上输出运算结果,必须考虑哪些问题 ? 从操作系统设计者的角度考虑,一个操作系统必须包含以下几部分 操作系统接口 CPU管理 内存管理 设备管理 文件管理<>认识操作系统-从设计者角度看
  • 8. 操作系统是计算机系统中的一个系统软件,是一些程序模块的集合——它们能以尽量有效、合理的方式组织和管理计算机的软硬件资源,合理的组织计算机的工作流程,控制程序的执行并向用户提供各种服务功能,使得用户能够灵活、方便、有效的使用计算机,使整个计算机系统能高效、顺畅地运行。<>认识操作系统-定义
  • 9. <>操作系统的组成(1) 通常意义上的操作系统被认为是整个系统中负责完成最 基本的功能和系统管理的部分。除了内核,这些部分还应该 包括启动引导程序、命令行shell或者其他种类的用户界面、 基本的文件管理工具和系统工具等。 实际上,人们在得到操作系统的同时,更需要的是构架 于其上的应用软件,从而完成所需的实际功能。为此,操作 系统一般要和应用软件绑定发行和出售。这样的软件包在 linux领域被称作发布版。
  • 10. <>操作系统的组成(2) 从开发者的角度看,操作系统本质上是大型软件包,因此 结构组织不会与其它大型软件迥异:操作系统的设计采用分层 结构,越向上层抽象程度越高,越接近用户;越向下层,越靠 近硬件,抽象也越接近硬件。 上层软件依靠下层软件提供的服务,而且上层软件自身还 提供附加服务。因此,操作系统的结构整体总体呈现倒金字塔 形。 用一组简单的公式描述操作系统的组成要素:操作系统=内核+系统程序 系统程序=编译环境+API(应用程序接口)+AUI(用户接口) 编译环境=编译程序+链接程序+装载程序 API=shell+系统服务例程+应用程序 对于整个软件系统: 软件系统=操作系统+AUI
  • 11. 操作系统的演变 单道批处理系统 串行执行预先组织好的一组任务 提高了系统效率 。 多道批处理系统 可以交错运行多个程序 再次提高系统效率。 分时系统 将处理器的运行时间分成数片,均分或依照一定权重派发给系统中的用户使用 快速响应 <>操作系统的发展
  • 12. 硬件角度下的操作系发展轨迹 年 代 硬 件 特点 操作系统特点 背 景 机械计算机时代17世纪~20世纪初 1)纯机械结构,低速 2)只能进行简单的数学运算 纯手工操作 从计算尺至差分机到分析机发展了数百年 第一代计算机 1946年~50年代末电子管计算机 1)体积大、能耗高、故障多、价格贵 2)难以普及应用 无操作系统 (程序按机器码编写,载体从插件板到卡片与纸带) 1906年发明电子管 1946 ENIAC研制成功 (第一台电子管计算机) 年 代 硬 件 特点 操作系统特点 背 景 第二代计算机 50年代末~60年代中期 晶体管计算机 1)采用印刷电路 2)稳定性与可靠性大大提高 3)批量生产成为可能 4)进入实际应用领域但数量有限1)单道批处理系统 2)操作系统以监督软件形式出现 3)任务按顺序方式处理 1947年发明晶体管 第三代计算机 60年代中期~70年代初 集成电路计算机 1)体积减小,性价比迅速提高 2)小型计算机发展迅速 3)进入商业应用 4)尚不适合家庭应用的需求 1)涌现大批操作系统 多道批处理系统、分时系统和实时系统 2)奠定了现代操作系统的基本框架 1958年发明集成电路 1971年INTEL发明微处理器 硬件角度下的操作系统发展轨迹
  • 13. 分析 在硬件的性价比较低的时候,操作系统设计追求什么? 在硬件性价比越来越高后,操作系统的设计开始追求的目标是什么? 计算机开始普及后,操作系统的设计开始追求? 从第三代到第四代计算机,操作系统的发展逐渐摆脱追随硬件发展的状况 ,形成自己的理论体系 进入第四代系统后,分布式系统和多处理器系统虽然极大的扩充了操作系统理论,但系统结构并没有变化,只是各功能模块得以进一步完善。 <>操作系统的发展硬件角度下操作系统发展的分析
  • 14. <>主流操作系统 系统特点 计 算 机 语 言 背 景 无 手工操作 无编程语言直接使用机器代码 1936年图灵提出图灵机 单道批处理系统 作业运行的监督程序 编程语言雏形期 1957年 FORTRAN语言开发成功多道批处理 分时系统 实时系统 多处理系统 操作系统结构确立,分为处理机管理、内存管理、设备管理、文件管理等模块 1)编程语言大量涌现 2)结构化程序设计 3)C语言逐渐 60年代的软件危机导致软件工程的发展 1969年 Unix诞生 1972年 C语言推出 主流操作系统 系统特点 计 算 机 语 言 背 景 类Unix系列 WINDOWS系列 人机交互成为主题 1)可视化界面 2)多媒体技 面向对象语言成为主流 80年代中期开始面向对象技术逐步发展 网络操作系统 分布式操作系统 微内核技术兴起 1)JAVA语言 2)脚本语言兴起 1995年JAVA推出 嵌入式系统 单内核与微内核竞争激烈 编程工具向跨平台方向发 1991年免费的操作系统Linux发布 软件角度下的操作系统发展轨迹
  • 15. 分析 程序设计理论约束着操作系统设计。操作系统的发展滞后于计算机语言的发展,从结构化设计到对象化设计,操作系统总是最后应用新编程理论的软件之一。 至今操作系统对于是否需要彻底对象化(即微内核化),还处于徘徊时期,仍在探索单内核与微内核的最佳结合方式。 人机交互技术主要是为用户考虑,这是对操作系统设计进行的变革。 以Linux为代表的开源软件的出现,打破了带有神秘色彩的传统的封闭式开发模式。 <>软件角度下的操作系统发展轨迹分析
  • 16. <>讲究效率的单模块操作系统进程管理内存管理设备管理文件管理模块之间可以互相调用的单模块结构
  • 17. <>讲究效率的单模块操作系统模块之间直接调用函数,除了函数调用的开销外,没有额外开销。 庞大的操作系统有数以千计的函数 复杂的调用关系势必导致操作系统维护的困难
  • 18. <>追求简洁的微内核操作系统客户进程进程服务器内存服务器文件服务器…微内核
  • 19. <>追求简洁的微内核操作系统内核与各个服务器之间通过通信机制进行交互,这使得微内核结构的效率大大折扣。 内核发出请求,服务器做出应答 为各个服务器模块的相对独立性,使得其维护相对容易
  • 20. <>历史悠久的Unix 在MULTICS(1969) 的肩上 制研制者Ken Thompson和Dennis M. Ritchie Unix的诞生还伴有C语言呱呱落地 Unix是现代操作系统的代表:安全、可靠、强大的计算能力 Unix的商业化是一把双刃剑
  • 21. <>自由而奔放的黑马-Linux诞生于学生之手 成长于Internet 壮大于自由而开放的文化
  • 22. <>Linux之父-Linus Torvalds芬兰、赫尔辛基大学、1990 起始于写两个进程 然后写驱动程序、文件系统、任务切换程序,从而形成一个操作系统邹形
  • 23. <>Linux得以流行的原因之一 -遵循POSIX标准POSIX 表示可移植操作系统接口(Portable Operating System Interface) POSIX是在Unix标准化过程中出现的产物。 POSIX 1003.1标准定义了一个最小的Unix操作系统接口 任何操作系统只有符合这一标准,才有可能运行Unix程序
  • 24. <>Linux的肥沃土壤-GNUGNU 是 GNU Is Not Unix 的递归缩写,是自由软件基金会的一个项目 。 GNU 项目产品包括 emacs 编辑器、著名的 GNU C 和 Gcc编译器等,这些软件叫做GNU软件。 GNU 软件和派生工作均适用 GNU 通用公共许可证,即 GPL(General Public License ) Linux的开发使用了众多的GUN工具
  • 25. <>GPL-开源软件的法律GPL 允许软件作者拥有软件版权 但GPL规定授予其他任何人以合法复制、发行和修改软件的权利。
  • 26. <>Linux系统或发布版 符合 POSIX 标准的操作系统内核、 Shell 和外围工具。 C 语言编译器和其他开发工具及函数库 X Window 窗口系统 各种应用软件,包括字处理软件、图象处理软件等。
  • 27. <>开放与协作的开发模式 世界各地软件爱好者集体智慧的结晶 提供源代码,遵守GPL。 经历了各种各样的测试与考验,软件的稳定性好。 开发人员凭兴趣去开发,热情高,具有创造性。
  • 28. <>Linux内核 Linus领导下的开发小组开发出的系统内核 是所有Linux 发布版本的核心 内核开发人员一般在百人以上,任何自由程序员都可以提交自己的修改工作。 采用邮件列表来进行项目管理、交流、错误报告 有大量的用户进行测试,正式发布的代码质量高
  • 29. <>Linux内核的技术特点 linux内核被设计成单内核结构,这是相对微内核而言的 2.6版本以前的linux内核是单线程结构,是非抢占式的内 核结构 linux内核支持动态加载内核模块 Linux内核被动地提供服务 linux内核采用了虚拟内存技术,使得内存空间达到4GB linux文件系统实现了一种UNIX风格的抽象文件模型——虚拟文件系统(Virtual Filesysterm Switch,VFS) linux提供了一套很有效的延迟机制——下半部分、软中断 tasklet和2.6版本新引进的工作队列等。
  • 30. <>整个系统的核心-内核 硬件系统调用接口 应用 程序进程1 应用 程序进程2 应用 程序进程3Linux内核用户进程 内核子系统系统调用
  • 31. <>整个系统的核心-内核 用户进程—运行在Linux内核之上的一个庞大软件集合。 系统调用—内核的出口,用户程序通过它使用内核提供的功能。 Linux内核—操作系统的灵魂,负责管理磁盘上的文件、内存,负责启动并运行程序,负责从网络上接收和发送数据包等等。 硬件—包括了Linux安装时需要的所有可能的物理设备。例如,CPU、 内存、硬盘、网络硬件等等。
  • 32. <>内核子系统
  • 33. <>内核子系统 进程调度-控制着进程对CPU的访问。 内存管理-允许多个进程安全地共享主内存区域 虚拟文件系统-隐藏各种不同硬件的具体细节,为所有设备提供统一的接口。 网络-提供了对各种网络标准协议的存取和各种网络硬件的支持。 进程间通信(IPC)- 支持进程间各种通信机制,包括共享内存、消息队列及管道等。
  • 34. Linux内核版本树 0.01 Linux(第一版)                                                             0.13版                                |                                                        产品化版本       实验版本                    1.0.0        1.1.0(1.0.0的拷贝)                                                              1.0.X(修改)   1.1.X(增加新功能,进行测试) … 1.1.95(成为1.2.0)
  • 35. 内核源代码结构
  • 36. Linux内核源代码分析工具 Linux超文本交叉代码检索工具 http://lxr.linux.no/ Windows平台下的源代码阅读工具Source Insight
  • 37. Linux内核模块编程入门 认识内核模块 内核模块是linux内核向外部提供的一个插口,是内核的一部分,但是并没有被编译到内核里面去,其全称为动态可加载内核模块(Loadable Kernel Module,LKM),简称模块。 为什么要使用模块? linux内核之所以提供模块机制,是因为它本身是一个单内核。而单内核的最大优点就是效率高,因为所有的内容都集成在一起,但其缺点是可扩展性和可维护性相对较差,模块机制就是为了弥补这一缺陷。
  • 38. Linux内核模块编程入门 模块的定义 模块是具有独立功能的程序,它可以被单独编译,但不能独立运行。它在运行时被链接到内核作为内核的一部分在内核空间运行,这与运行在用户空间的进程是不同的。模块通常由一组函数和数据结构组成,用来实现一种文件系统、一个驱动程序或者其他内核上层的功能。 编写简单的内核模块 模块和内核都在内核空间运行,模块编程在一定意义上说就是内核编程。一个内核模块应该至少有两个函数,第一个为module_init(),是模块加载函数,当模块被插入到内核时调用它;第二个为module_exit(),是模块卸载函数,当模块从内核移走时调用它。
  • 39. Linux内核模块编程入门 简单的内核模块实例: 任何模块都要包含的三个头文件: #include #include #incldue 说明:module.h头文件包含了对模块的版本控制;kernel.h包含了常用的内核函数;init.h包含了宏__init和__exit,宏__init告诉编译程序相关的函数和变量仅用于初始化,编译程序将标有__init的所有代码存储到特殊的内存段中,初始化结束就释放这段内存。在此使用了printk()函数,该函数是由内核定义的, 功能和C库中的printf()类似,它把要打印的日志输 出到终端或系统日志。字符串中的<1>是输出的级 别,表示立即在终端输出。
  • 40. Linux内核模块编程入门 内核模块的Makefile文件 内核模块不是独立的可执行文件,但在运行时其目标文件被链接到内核中。只有超级用户才能加载和卸载模块。 给前面的程序起名叫module_example.c,那么其对应的Makefile文件的基本内容如下:obj-m :=这个赋值语句的含义是说明要使用目标文件module _example.o建立一个模块,最后生成的模块名为module_ example.ko。.o文件是经过编译和汇编,而没有经过链接的 中间文件。 注:makefile文件中,若某一行是命令,则它必须以一个Tab 键开头。
  • 41. Linux内核模块编程入门 运行代码 当编译好模块,就可以用insmod命令将新的模块插入到内核中,如: insmod module_example.ko 然后,可以用lsmod命令查看模块是否正确地插入到了内核中。模块的输出由printk()产生。该函数默认打印系统文件/var/log/messages的内容。卸载模块时,使用rmmod命令加上在insmod中看到的模块名,就可以从 内核中移除该模块: rmmod module_example
  • 42. Linux内核模块编程入门 应用程序与内核模块的比较 C语言应用程序 内核模块程序 使用函数 Libc库 内核函数 运行空间 用户空间 内核空间 运行权限 普通用户 超级用户 入口函数 main() module_init () 出口函数 exit() module_cleanup () 编译 gcc –c make 连接 gcc insmod 运行 直接运行 insmod 调试 gdb kdbug, kdb,kgdb等
  • 43. Linux内核中链表的实现及应用为什么要用链表?① 与数组相比,链表中可以动态插入或删除元素,在编译时不必知道要 创建的元素个数。 ② 在内存无需占用连续的内存单元。 ③ 每个元素都包含一个指向下一个元素的指针,当有元素加入链表或者 从链表中删除元素时,只需要调整下一个节点的指针就可以了。
  • 44. Linux内核中链表的实现及应用通过前趋(prev)和后继(next) 两个指针域,就可以从两个方向 遍历双链表,这使得遍历链表的 代价减少。链表的演化 在C语言中,一个基本的双向链表定义如下: struct my_list { void *mydata; struct my_list *next; struct my_list *prev; };
  • 45. Linux内核中链表的实现及应用链表的定义 linux内核对链表的实现方式与众不同,不是在链表中包含数据,而是在数据结构中包含链表。其具体的定义如下: struct list_head {                          struct list_head *next, *prev;    }; 这个不含数据结构的通用双向链表可以嵌入到任何结构中。 说明:1.list域隐藏了链表的指针特性 2.struct list_head可以位于结构的任何位置,可以给其起任何名字 3.在一个结构中可以有多个list域   以struct list_head为基本对象,可以对链表进行插入、删除、合并以及遍历等各种操作。 
  • 46. Linux内核中链表的实现及应用链表的声明和初始化 struct list_head只定义了链表节点,并没有专门定义链表头,那么一个链表结构是如何建立起来的? 内核代码list.h中定义了两个宏: #define LIST_HEAD_INIT(name) { &(name), &(name) } /*仅初始化*/ #define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name) /*声明并初始化*/ 当我们调用LIST_HEAD(name)声明一个名为name的链表头时,它的 next和prev指针都初始化指向自己。这样,我们就有了一个空链表, 因为linux用头指针的next是否指向自己来判断链表是否为空: static inline int list_empty(const struct list_head *head) { return head->next == head; }
  • 47. Linux内核中链表的实现及应用在链表中增加一个节点 在include/linux/list.h中增加结点的函数为: static inline void list_add(); static inline void list_add_tail(); 在内核代码中,函数名前加两个下划线表示内部函数。list_add()和list_add_tail()均调用__list_add()真正实现头插和尾插。static inline void __list_add(struct list_head*new, struct list_head *prev, struct list_head *next) { next->prev = new; new->next = next; new->prev = prev; prev->next = new; }static inline void list_add(struct list_head *new, struct list_head *head) { __list_add(new, head, head->next); } 该函数向指定链表的head结点后插入new结点。 因为是循环链表,而且通常没有首尾结点的概 念,所以可以将任何结点传给head。若传最后 一个元素给head,该函数就可以实现一个栈。
  • 48. Linux内核中链表的实现及应用 list_add_tail()的内核实现: static inline void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); } list_add_tail()函数向指定链表的head结点前插入new结点。 说明:关于static inline关键字。 “static”加在函数前,表示这个函数是静态函数 ,所谓静态函数,实际上是 对函数作用域的限制,指该函数的作用域仅局限于本文件。所以说,static 具有信息隐藏的作用。而关键字"inline“加在函数前,说明这个函数对编译 程序是可见的,也就是说编译程序在调用这个函数时就立即展开该函数。
  • 49. Linux内核中链表的实现及应用 链表的遍历 这种链表只是找到了一个个结点在链表中的偏移位置pos,如下图(a)。那么如何通过pos获得结点的起始地址,从而可以引用结点中的域呢?(a)(b)list.h中定义了晦涩难懂的list_entry()宏: #define list_entry(ptr, type, member) \    ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) 指针ptr指向结构体type中的成员member;通过指针ptr,返回结构体type的起始地址, 也就是list_entry返回指向type类型的指针,如上图(b)。#define list_for_each(pos, head) \     for (pos = (head)->next; pos != (head); \              pos = pos->next) list.h中定义了如下遍历遍历链表的宏:
  • 50. Linux内核中链表的实现及应用关于list_entry宏的详细分析: ((type *)0)->member把0地址强制转化为type结构的指针,再访问type结构体中的member成员,(&((type *)0)->member)获得了member在type结构中的偏移量。其中(char *)(ptr)求出的是ptr的绝对地址,二者相减,于是得到type类型结构体的起始地址,如图(c)所示。 (c)list_entry宏解析 至此,对Linux内核中链表的实现机制有了初步了解,更多的函数和实现请查看include/linux/list.h中的代码。
  • 51. “内核之旅 ”网站 http://www.kerneltravel.net/ 电子杂志栏目是关于内核研究和学习的资料 第一期“走入Linux世界”涉猎了操作系统的来龙去脉后与大家携手步入Linux世界。 下载代码,亲手搭建实验系统。
  • 52. Linux内核中链表的实现及应用关于list_entry宏的详细分析: ((type *)0)->member把0地址强制转化为type结构的指针,再访问type结构体中的member成员,(&((type *)0)->member)获得了member在type结构中的偏移量。其中(char *)(ptr)求出的是ptr的绝对地址,二者相减,于是得到type类型结构体的起始地址,如图(c)所示。
  • 53. 作业及上机1. Linux系统由哪些部分组成?Linux内核处于什么位置? 2. Linux内核由哪几个子系统组成?各个子系统的主要功能是什么? 3. 访问http://www.kernel.org/,理解Linux的内核的版本树,了解最新内核的特点 4. 了解Linux内核源代码结构,访问源代码导航网站http://lxr.linux.no/,说明/kernel目录下包含哪些文件。 5.编写简单的内核模块,上机调试,给出调试过程,遇到的问题以及解决思路。 6. 分析include/linux/list.h中哈希表的实现,给出分析报告,并编写内核模块,调用其中的函数和宏,实现哈希表的建立和查找。并上机调试。
  • 54. 上机1. 熟悉Linux内核源代码,编写简单的内核模块,并调试 2.