《Linux内核设计与实现》——第1、2章(内核简介)

时间:2021-05-22 16:56:59

Linux内核设计与实现——内核简介(1、2章)

一、Unix

Unix特点:

  • 简洁
  • 绝大部分东西都被当做文件对待。这种抽象使对数据和对设备的操作都是通过一套相同的系统调用借口来进行的:open(),read(),write(),lseek()和close()
  • 出色的平台可移植性——内核和相关的系统工具软件用C语言编写而成
  • 进程创建迅速
  • 进程间通信原语简单稳定

Unix是一个强大、健壮和稳定的操作系统。支持抢占式多任务、多线程。虚拟内存、换页、动态链接和TCP/IP网络。

二、单内核与微内核

《Linux内核设计与实现》——第1、2章(内核简介)
Linux的内核虽然是基于单内核的,但是经过这么多年的发展,也具备微内核的一些特征。(体现了Linux实用至上的原则)

主要有以下特征:

支持动态加载内核模块
支持对称多处理(SMP)
内核可以抢占(preemptive),允许内核运行的任务有优先执行的能力
不区分线程和进程

三、 内核开发的准备

1、获取源代码

内核是开源的,所有获取源码特别方便,参照以下的网址,可以通过git或者直接下载压缩好的源码包。

http://www.kernel.org

2、内核源码的结构

《Linux内核设计与实现》——第1、2章(内核简介)

3、内核开发的特点

1、无标准C库

为了保证内核的小和高效,内核开发中不能使用C标准库,所以连最常用的printf函数也没有,但是还好有个printk函数来代替。

2、使用GNU C,推荐用gcc 4.4或以后的版本来编译内核

因为使用GNU C,所有内核中常使用GNU C中的一些扩展:

(1 内联函数

内联函数在编译时会在它被调用的地方展开,减少了函数调用的开销,性能较好。但是,频繁的使用内联函数也会使代码变长,从而在运行时占用更多的内存。

所以内联函数使用时最好要满足以下几点:函数较小,会被反复调用,对程序的时间要求比较严格。

内联函数示例:

static inline void sample();

(2 内联汇编

内联汇编用于偏近底层或对执行时间严格要求的地方。示例如下:

unsigned int low, high;
asm volatile(“rdtsc” : “=a” (low), “=d” (high));
/* low 和 high 分别包含64位时间戳的低32位和高32位 */

(3 分支声明

如果能事先判断一个if语句时经常为真还是经常为假,那么可以用unlikely和likely来优化这段判断的代码。

/* 如果error在绝大多数情况下为0(假) */
if (unlikely(error)) {
    /* ... */
}

/* 如果success在绝大多数情况下不为0(真) */
if (likely(success)) {
    /* ... */
}

3、没有内存保护

因为内核是最低层的程序,所以如果内核访问的非法内存,那么整个系统都会挂掉!!所以内核开发的风险比用户程序开发的风险要大。

而且,内核中的内存是不分页的,每用一个字节的内存,物理内存就少一个字节。所以内核中使用内存一定要谨慎。

4、不使用浮点数

内核不能完美的支持浮点操作,使用浮点数时,需要人工保存和恢复浮点寄存器及其他一些繁琐的操作。

5、内核栈容积小且固定

内核栈的大小有编译内核时决定的,对于不用的体系结构,内核栈的大小虽然不一样,但都是固定的。

查看内核栈大小的方法:

ulimit -a | grep “stack size”

6、同步和并发

Linux是多用户的操作系统,所以必须处理好同步和并发操作,防止因竞争而出现死锁。

7、可移植性

Linux内核可用于不用的体现结构,支持多种硬件。所以开发时要时刻注意可移植性,尽量使用体系结构无关的代码。

四、理解

  • 关于内核态和用户态。学习的重点和主题是内核。用户界面是操作系统的外在表象,内核才是操作系统的内在核心。根据操作、访问权限的不同,划分为内核态和用户态。打个比方,图书馆中,读者可以*地检索目录、阅读书籍,却无权直接去书库取书,也无权修改图书馆的数据库。而工作人员就在特权状态,可以访问任意资源。这样的划分是为了系统的稳定和安全,也是免去了用户和系统底层打交道的麻烦。
  • 在系统中运行的应用程序通过系统调用与内核通信,也是通过系统调用界面陷入内核。而“接口”是实现的关键。图书馆中,读者想要借书时,只能填写借书单,由工作人员完成登记和取书工作。“借书单”起到了一个“接口”的作用。