第一二章读书笔记

第一章

关于Unix根本强大的原因:

  • Uniix很简洁,仅仅提供几百个系统调用,并有非常明确的设计目的
  • 在Unix中所有东西都被当做文件对待,这种抽象使对数据和对设备的操作是通过一套相同的系统调用接口来进行的:open(),read(),write(),lseek(),close().
  • Unix的内核和相关的系统工具软件是用C语言编写成的——Unix有惊人的移植能力
  • Unix的进程创建非常迅速,并有fork()系统调用
  • Unix提供一套非常简单又稳定的进程间通信原语

正因为以上策略和机制分离的设计理念,确保了Unix系统具备清晰的层次化结构

如今的Unix已发展成:

一个支持抢占式多任务,多线程,虚拟内存,换页,动态链接和TCP/IP网络的现代化操作系统

Linux并没有抛弃Unix的设计目标并保证了应用程序编程接口的一致。


关于操作系统和内核

1)操作系统是值在整个系统中负责完成最近本功能和操作系统管理的那些部分。

2)系统包含了操作系统和所有运行在她之上的应用程序。

3)内核有时候被称为管理者和操作核心。
通常由:

负责响应中断的中断服务程序
负责管理多个进程从而分享处理器时间的调度程序
负责管理进程地址空间的内存管理程序
网络、进程间通信等系统服务程序

共同组成。

系统中运行的应用程序通过系统调用来与内核通信。

这种交互关系——应用程序通过系统调用界面陷入内核——是应用程序完成其工作的基本行为方式。

内核负责管理系统的硬件设备:

当硬件设备想和系统通信时,先发出一个异步中断信号去打断处理器的执行,继而打断内核的执行。

中断通常对应着一个中断号,内核通过这个中断号来查找相应的中断服务程序,并调用这个程序响应和处理中断。

许多系统的中断服务程序(包括Linux)都不在进程上下文中执行。它们在一个与进程无关的 专门的中断上下文中运行。——为了保证中断服务能够在第一时间响应和处理中断请求,然后快速退出。


LInux内核与Unix内核的比较

Unix内核几乎毫无例外的都是一个不可分割的静态可执行库。
Unix内核通常需要硬件系统提供页机制(MMU)以管理内存。

关于页机制(MMU),它可以加强对内存空间的保护,并保证每个进程都可以运行不同的虚地址空间上。

操作系统可分:单内核和微内核

单内核(Linux就是):

从整体上作为一个单独的大过程来实现,同时也运行一个单独的地址空间上。——简单,性能高

微内核:

被分成多个独立的进程,每个过程叫做一个服务器。理想状态下只有特权服务的服务器才运行在特权模式下,其他服务器都运行在用户空间。(不过所有服务器都保持独立并运行在各自的地址空间上)
因此——需要通过消息传递处理内核通信:系统采用进程间通信(IPC)机制(服务器的各自独立有效的避免了一个服务器的失效祸及另一个)
同样,模块化的系统允许一个服务器为了另一个服务器换出

IPC机制开销多用于函数调用,又因用户空间的上下文切换,消息传递需要一定的周期。

Lnux内核与Unix内核的显著差异:

  • LInux支持动态加载内核模块
  • Linux支持对称多处理(SMP)机制
  • Linux内核可抢占
  • Linux对线程的实现有些小特别——内核并不区分线程和其他的一般进程
  • Linux提供具有设备类的面向对象的设备模型,热插拔事件,以及用户空间的设备文件系统
  • Linux忽略了一些被认为是设计的很拙劣的Unix特性
  • Linux体现了自由

关于Linux的内核版本:
命名机制:用三个或四个点分隔的数字来代表不同的内核版本:

第一个数字是主版本号,第二个数字是从版本号,第三数字是修订版本号,第四个数字为稳定版本号:副版本号数字为偶数,则为稳定版,如果是奇数,就是开发版。

第二章

获取Linux内核官网http://www.kernel.org

使用Git管理Linux内核源代码,Git是分布式的。

保持与内核官方代码一致:

获取最新提交到linus版本树的一个副本:

$ git clone git://git.kernel.org/pub/scm/Linux/kernel/git/torvarlds/linux-2.6.git

更新分支:

$ git pull

解压源代码:

1)压缩形式bzip2

$ tar xvjf linux-x.y.z.tar.bz2
  1. 压缩形式GNU的zip:

    $ tar xvzf linux.x.y.z.tar.gz

(如果是使用git,不需要下载压缩文件)

*不要以root身份对内核进行修改,而应该建立自己的主目录,仅以root身份安装新内核

打补丁的方法(一个给定版本的内核补丁总是打在前一个版本上):

$ patch -p1 < ../patch-x.y.z

编译内核

1)配置内核

这些配置有

二选一:是否开启

三选一:yes(代码编译进主内核映像,而不是作为一个模块),no,module(配置被选定)

*驱动程序一般都是三选一的配置选项。

配置选项也可以是字符串或者整数。这些选项并不控制编译过程,而只是指定内核源码可以访问的值,一般以预处理宏的形式表示。

简化内核配置命令行工具:

1)逐一配置,不推荐

$ make config

2)基于ncurse库编制的图形界面工具:

$ make menucofig

3)gtk+图形工具

$ make gconfig

这三种工具将所有配置分门别类放置。

这条命令会基于默认的配置为你的体系结构创建一个配置:

$ make deconfig

这些配置选项会被存放在内核代码树根目录下.config文件中。
在修改配置文件后或者在用配置文件配置新的代码树的时候,应该更新和验证:

$ make oldconfig 

配置选项CONFIG_IKCONFIG_PROC把完整的压缩过的内核配置文件存放在/proc/config.gz——方便克隆当前的配置,启用此选项后,可以从/proc下复制出配置文件并用来编译一个新内核:

$ zcat /proc/config.gz > .config
$ make oldconfig

配置好后进行:

make

减少编译垃圾的方法

1)尽量少地看到垃圾信息,不希望错过错误报告或警告信息

$ make > .. /detritus

2)把无用输出信息重定向到永无返回值的黑洞/dev/null

衍生多个编译作业

多个作业编译内核:

$ make -jn(n是要衍生出的作业数)

安装新内核

一定要保证随时有一个两个启动的内核,以防新编译的内核出现问题。

模块的安装时自动的,也是独立于体系结构的
以root身份运行:

% make modules_intsall

编译时会在内核树的根目录下创建System.map文件。是一份符号对照表,用以将内核符号和它们的起始地址对应起来。调试的时候,如果需要内存地址翻译成容易理解的函数名以及变量名,这会很有用。


内核开发的独特之处

  • 内核编程时既不能访问C库也不能访问标准的C头文件
  • 内核编程必须使用GNC
  • 内核编程时缺乏像用户空间那样的内存保护机制
  • 内核编程难以进行浮点运算
  • 内核给每个进程只有一个很小的定长堆栈
  • 由于内核支持异步中断,抢占和SMP,因此必须时刻注意同步和并发
  • 要考虑可移植性地重要性

1)无libc库或无标准头文件

内核不能连接使用标准C函数库

体系结构相关的头文件集中在内核源代码树的arch//include/asm目录下

*printf和printk之间的一个显著区别在于:

printk允许你通过一个指定一个标志来设置优先级。syslogd会根据这个优先级来决定在什么地方显示这条系统信息。

1)内核函数

inline函数会在它调用的位置上展开(这么做会消除函数调用和返回锁带来的开销)。

定义一个内核函数的时候,需要使用static作为关键字,并用inline限定它。

2)内核汇编

linux的内核混合使用了c语言和汇编语言,在偏近体系结构的底层或对执行时间严格要求严格的地方,一般使用的是汇编语言。

3)分支声明

条件选择语句:

if(error){
	/*...*/
	}

绝少发生的分支:

/*我们认为error绝大多数时间都会为0*/
if(unlikely(error)){
	/*...*/
}

通常为真的分支:

/*我们认为success绝大多数时间都会为0*/
if(likely(success)){
	/*...*/
}

内核中很容易产生竞争,要保证不出现竞争的时候:

  • LInux是抢占式多任务操作系统。内核的进程调度程序即兴对进程进行调度和重新调度。内核必须与它们同步。
  • Linux内核支持对多处理器(SMP)。
  • 中断是异步发生的,完全不顾及正在执行的代码。
  • linux内核可以抢占。