从内核登程

原以为这章能够飞快跳过,但还是发现很多新的有意思的点。

获取源码

  1. https://www.kernel.org 压缩源码或者增量补丁

    • bz2
      tar xvjf linux-x-y-z.tar.bz2
    • gz
      tar xvzf linux-x-y-x.tar.gz
  2. https://github.com/torvalds/l...

装置源码

/usr/src/linux
不要把这个源码树用于开发,因为编译用的C库所用的内核版本就链接到这棵树。
正确:建设本人的主目录,而后仅用root进行装置。
对于补丁:
patch -p1 < ../patch-x.y.z

内核源码树

目录形容
arch特定体系结构的源码
block块设施io层
crypto秘密API
Documents内核源码文档
drivers设施驱动程序
firmware应用某些驱动程序而须要的设施固件
fsVFS和各种文件系统
include内核同文件
init内核疏导和初始化
ipc过程间通信
kernel外围子系统
lib通用内核函数
mm内存管理子系统和VM
net网络子系统
samples实例
scripts编译内核所用的脚本
securityLinux平安模块
sound语音子系统
usr晚期用户空间代码
toolsLinux开发工具
virt虚拟化体系

编译

编译Linux之前须要进行配置:CONFIG_XXXX: yes|no|module;

  1. 决定哪些文件能够被编译进内核;module意味着该配置选项被选定,但编译的时候这部分性能的实现代码是以模块的模式生成的(动静装置的独立代码块)。
  2. 通过预处理的命令解决代码;配置选项能够为字符串和证书,指定内核源码能够拜访的值,以预处理宏的模式;如制订动态调配数组的大小。
    配置命令:
  3. 逐个遍历
    make config
  4. 图形界面工具
    make menuconfig
  5. gtk+图形工具
    make gconfig
  6. 默认配置
    make defconfig
    配置选项会被放在根目录下.config文件中;每次编译前,更新配置
    make oldconfig
    能够配置CONFIG_IKCONFIG_PROC,把配置压缩放入/proc/config.gz下,能够从/proc下复制出配置文件并且应用它来编译一个新内核:
    zcat /proc/config.gz > .config
    make oldconfig
    配置实现后: make > /dev/null
    进步make效率多核,如16核:
    make -j32 > /dev/null

装置

  1. 查阅启动疏导工具的阐明,将内核映像拷贝到/boot目录下,按启动要求装置。
  2. 内核模块的装置是主动的,make modules_install,装到/lib/modules下
  3. 编译时也会在内核代码树的根目录下创立一个System.map文件,符号对照表,将内核符号和他们的起始地址对应,须要把内存地址翻译成容易了解的函数名以及变量名。

内核开发

内核编译时不能拜访c库文件(libc)和规范c头文件

lib/string.c => <linux/string.h>

  1. 内核开发头文件根本都在源码include目录下,头文件<linux/inotify.h>
  2. 体系相干的头文件arch/x86/include/asm上面<asm/ioctl.h>
    没有实现的printf() -> prink 将格式化的字符串拷贝到日志缓冲区中,syslog程序就能够通过读取该缓冲区来获取内核信息。 能够指定一个优先级标志符,相似于宏定义
    prink(KERN_ERR "this is an error.\n")

应用GNU C

  1. gcc工具蕴含了多种GNU编译器,能够编译内核。
  2. 内核C语言蕴含了ISO C99规范和GNU C扩大个性。 与规范c语言不同的大多是GNU C的扩大上。

    • 内联函数inline:

      • 函数在调用地位扩大,打消函数调用和返回的开销,代码变长,占用内存空间或者指令缓存更多;
      • 函数较大,被重复调用,没有工夫要求不举荐应用
      • 与static合用
        static inline void wolf(unsigned long tail_size)
      • 应用前定义,举荐在头文件中定义,优先应用inline函数而不是宏
    • 内联汇编

      • 只有晓得体系结构才能够应用,偏近底层或对执行工夫要求严格
      • asm()
        unsigned int low, hight
        asm volatile("rdtsc" : "=a" (low), "=d" (high));
    • 分支申明

      • gcc优化分支,经常出现likely, unlikely
        if (unlikely (error))
      • 肯定要弄清是否真的有偏差,对性能影响微小

    没有内存保护机制

  3. 用户程序非法内存拜访,内核会发现,发送SIGSEGV信号,完结过程
  4. 内核中产生内存拜访谬误导致oops

    • 非法内存拜访
    • 援用空指针
  5. 内核中内存不分页,用一个字节,物理内存就少一个

不要轻易应用浮点数

  1. 用户空间操作浮点数,内核须要从整数模式切换成内核模式,具体实现与体系相干
  2. 在内核中应用浮点数时,须要人工保留和回复浮点寄存器,还要有其余简单工作

内核只有小而固定的栈

  1. 用户空间能够动态分配大容积的栈,内核不行
  2. 内核栈大小随体系变动,x86,在编译时确定,4KB或者8KB;历史上内核栈大小两页,32位8KB, 64位16KB

留神同步和并发

起因:

  1. Linux是抢占多任务操作系统,调度过程中内核必须和工作同步
  2. 反对对称多处理器零碎SMP;两个或多个以上的处理器上执行的内核代码可能同时访问共享的同一个资源
  3. 中断异步到来,可能在拜访资源时到来,中断程序可能解决同一个资源
  4. Linux内核能够抢占,内核中正在执行的代码可能被同一段代码抢占,从而导致几段代码同时拜访雷同的资源。

办法:

  1. 自旋锁
  2. 信号量

可移植性很重要