固然用户空间应用程序一般不会太器重可移植性,但Linux确实是一个可移植性操作系统,应该坚持一致,这象征着与架构无关的C代码必须在大量的系统上正确地编译和运行,与架构相关的代码必须在内核源代码树中使用特定的目录分隔开。
内核最容易受竞争条件影响,和一个单线程的用户空间应用程序不一样,有许多内核特征允许同时访问共享资源,因此需要同步以避免竞争,特别是:
配置选项CONFIG_IKCONFIG_PROC指定了完全的内核配置文件紧缩包地位,默认是/proc/config.gz,这样在天生新内核时要克隆现有的配置就变得十分简略了。假如你当前的内核开启了这个选项,你能够从/proc拷贝该配置文件,而后在此基本上生成新的内核:
内联汇编
内核源代码通常都会安装到/usr/src/linux下,但在开发的时候最好不要使用这个源代码树,因为针对你的C库编译的内核版本通常也链接到这里的。
可以确定,内核有它奇特的性质,它有它自己的一些准则,不外,内核的复杂性和阻碍与其它大型软件名目相比,并没有什么大的不同,Linux开发途径上最重要的一步是意识到内核并不恐怖,不熟习?当然!不可超越?当然不是!
◆可移植性也很重要。
和许多Unix内核类似,Linux内核也是用C编写的,但兴许会让人很意外,内核不是用谨严的ANSI C编写的,内核开发人员用的却是gcc(GNU编译器集,包含了编译内核和Linux C程序的C编译器)中的各种语言扩展。
◆内核缺乏用户空间供给的内存维护;
分支注解
在生成过程中会受到忠告和错误的烦扰。最小化干扰信息的一个窍门是重定向make的输出,但依然会看到一些警告和错误:
内核源代码树分为许多目录,它们下面又包括很多子目录,源代码树的顶级目录及其描述参见下表。
GNU C
模块的装置与体系架构无关,都是主动实现的,以root用户运行:
$ make > /dev/null同时履行多个生成功课
默认情况下,make只能拆分成一个作业,因为Makefiles经常会包含不正确的依附信息,如果然是这样,多个并行执行的作业将会引起凌乱,终极会导致生成过程失败,如果Makefiles中的依赖信息无误,那么完全可以拆分成多个作业执行,如:
用户空间可以静态分配许多不同的堆栈,包括巨型构造和千元数组,这个行动是正当的,因为用户空间有很大的堆栈,并可以动态增加。
内核开发人员同时使用了C语言的ISO C99和GNU C扩展,这些变更让Linux内核与gcc联合得更严密,但最近又呈现了一个编译器 � 英特尔的C编译器 � 也对gcc的功能支持得相称好,因此也可以用它来编译Linux内核。最低支持的gcc版本是3.2,倡议采用gcc 4.4或更高的版本编译。使用ISO C99扩大也是可以的,因为C99是C语言的官方版本。
% make modules_install这个命令会将所有编译好的模块安装到/lib/modules下对应的子目录中。
与一般用户空间的应用程序比拟,Linux内核有多个特殊的属性,下面是我以为最重要的一些不同:
小型,固定大小的堆栈
先看下面这样的if语句:
这里的头文件指的是内核源代码树中的头文件,内核也只能使用树内的头文件,基础文件位于源代码根目录的include/目录下,例如,<linux/inotify.h>头文件就位于include/linux/inotify.h。
unsigned int low, high;asm volatile("rdtsc" : "=a" (low), "=d" (high));/* low and high now contain the lower and upper 32-bits of the 64-bit tsc */Linux内核是用C和汇编语言混杂编写的,与底层硬件相干的代码很多都是用汇编语言写的,剩下的大局部内核代码都是直接用C编写的。
应用补丁
static inline void wolf(unsigned long tail_size)函数必须先申明后使用,否则编译器就不能使函数内联,普通做法是将内联函数放在头文件中,因为它们被标记为static,不会创立输出函数,如果内联函数仅在一个文件中使用,可以放在该文件的顶部。
/* we predict 'error' is nearly always zero ... */
if (unlikely(error)) {
/* ... */
}相反,将这个分支标记为无比可能采取
下面我们就一一来懂得一下这些问题,所有内核开发人员都必须记住它们。
无libc或标准头
将干扰信息最小化
$ make menuconfig或是基于gtk+的图形化工具
◆内核不能容易地执行浮点运算;
$ make和2.6以前的内核不一样,在生成内核前不再需要执行make dep命令了,依赖树会自动维护,也不需要再指定特定的生成类型,如bzImage,或独立生成模块,默认Makefile规矩会自动处理好所有。
$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git然后使用下面的命令将你的代码树与Linus的代码树最新状况同步:
下载Linux内核当然要去官方网站了,网站提供了两种文件下载,一种是完整的Linux内核,另一种是内核增量补丁,它们都是tar归档压缩包。除非你有特殊的原因需要使用旧版本的Linux内核,否则你应该老是升级到最新版本。
printk("Hello world! A string '%s' and an integer '%d'\n", str, i);printf()和printk()之间最大的不同是,printk()许可你指定一个优先级标记,syslogd使用这个标记确定在哪里显示内核新闻,下面是一个使用优先级标记的示例:
既然已经拿到内核源代码,那咱们在开端编译前就可以根据需要自行配置和定制,上海师范大学2010年在沪招生2830人,可以编译你指定的功能和想要的驱动,配置内核是生成内核必需的一步,因为内核提供了大批的功效,支撑各种不同的硬件,有很多都需要配置,内核配置是由配置选项把持的,配置选项都有CONFIG前缀,香港六合彩,例如,对称多处理(SMP)是由CONFIG_SMP配置选项配置的,如果设置了这个选项,SMP就被启用了,反之则被禁用,配置选项可以肯定会生成哪个文件,也可以通过预处理指令操控代码。
$ zcat /proc/config.gz > .config
$ make oldconfig内核配置好后,应用下面的命令进行生成:
内核包有GNU zip(gzip)和bzip2格式。Bzip2是默认和首选格式,因为它的压缩比通常比gzip更好,bzip2格式的Linux内核包一般采用linux-x.y.z.tar.bz2情势的文件名,这里的x.y.z是内核源代码的详细版本号,下载到源代码包后,解压和抽取就很简单了,如果你下载的是bzip2包,运行:
C99和GNU C都支持内联函数,内联函数是直接插入到每个函数调用的位置的,打消了函数调用和返回的开销,答应进一步优化,因为编译器可以同时优化调用者和被调用函数,但它也有毛病,代码大小会增添,因为函数的内容被直接复制到所调用者内部了,因此也会增长内存耗费和指令缓存空间。内核开发人员正常在小型时间很要害的函数中才会使用内联函数。
定义函数时,使用static和inline症结字声明内联函数,例如:
Linux厂商也会随发行版提供预编译的内核,如Canonical为Ubuntu,或Red Hat为Fedora提供的内核,这样的内核通常只启用了需要的内核功能,简直所有驱动都被编译成模块了,这样的内核提供了一个良好的基础内核和普遍的硬件模块支持,无论如何,想要成为内核高手,你应该编译自己的内核。
$ make �jn这里的n表示拆分的作业数目,通常按每个处理器拆分成1-2个作业,例如,在一个16中心的机器上 ,你可以运行:
◆内核既不访问C库也不访问标准C头;
在内核中,与庞杂的宏相比,出于平安和可读性方面斟酌,组图:2009年全国高考拉开帷幕,内联函数是首选。
原文地址:http://www.informit.com/articles/article.aspx?p=1610334
遗漏的大部门都是相似printf()这样的函数,内核不会使用printf(),但它提供了printk()函数,其表示毫不比printf()差,printk()会拷贝格局化的字符串到内核日志缓冲区,syslog程序就是从这里读守信息的,其用法也和printf()类似:
Linux内核入门,包教会
不要担心,许多常用的libc函数都在内核中实现了,例如,常见的字符串操作函数就位于lib/string.c中,只需要包括它的头文件<linux/string.h >就可以了。
由Linus领头的内核开发步队从多少年前就开始使用Git版本控制系统管理Linux内核了(参考阅读:什么是Git?),而Git项目自身也是由Linus创建的,它和传统的CVS不一样,Git是散布式的,因此它的用法和工作流程很多开发人员可能会觉得很生疏,但我强烈提议使用Git下载和管理Linux内核源代码。
$ make > ../detritus如果你想查看生成输出,你可以事后浏览这个文件,如果你完整不想看到任何输出,那么就重定向到/dev/null:
总结
◆Linux是一种抢占式多任务操作系统,进程是由内核的进程调度器随便调度和再次调度的,内核必须在这些义务之间同步;
Gcc C编译器内置了一个指令优化前提分支,内核将这个打包成易于使用的宏 - likely()和unlikely()。
配置选项也可以是字符串或整数,这样的选项不会节制生成过程,指定的值由内核源代码访问预处理宏时使用,例如,可认为某个配置选项指定静态调配数组的大小。
if (error) {
/* ... */
}将这个分支标记为异常不可能采用
内核源代码树先容
可移植性的重要性
生成过程会在源代码树根目录下创建一个System.map文件,它包含一个符号查找表,映射内核符号到它们的起始地址,在调试期间可以用它将内存地址转换成函数和变量名。
同步和并发
◆中断是异步发生的,因此,如果没有适当的保护,在访问资源期间也可能产生中断,中断处理程序可能就会访问到相同的资源;
使用Git
◆Linux是有优先权的,因此,如果没有适当的保护,内核代码可能会优先执行,访问其它代码正在使用的资源。
◆内核是用GNU C编码的;
不能(轻易)使用浮点数
Make命令提供了一个功能可以将生成过程拆分成多个平行的作业,这些作业可以独立运行,也可以并行运行,在多处理器系统上可以极大地提高生成速度,也提高了处理器应用率,因为生成大型源代码树会涌现大量的I/O等候时间。
◆内核有一个小型的固定大小的进程堆栈;
生成内核实在很简单,甚至比编译和安装其它系统级组件,如glibc还要简单,从2.6版本开始,Linux内核引入了一个新的配置和生成系统,它使出产内核的操作变得更加简单了。
与架构相关的头文件则位于arch/<architecture>/include/asm,例如,如果在x86架构下编译,与你架构相关的文件就是arch/x86/include/asm,只需要在援用这些头的处所加上asm/前缀即可,如<asm/ioctl.h>。
你可以使用下面的Git命令获取Linus内核代码树的最新“推送”版本:
$ make gconfig上述三个工具都将配置选项分成多个类别,如“处理器类型和特点”,你可以在这些种别上往返移动,查看内核选项,当然也可以修改它们的设置了。
可能会碰到的问题
目录 描述
arch 特定架构的源代码
block 块I/O层
crypto 加密API
Documentation 内核源代码文档
drivers 设备驱动
firmware 使用某个驱动需要的装备固件
fs VFS和独破文件系统
include 内核头
init 内核启动和初始化
ipc 进程间通讯
kernel 核心子系统,如调度器
lib 助手例行程序
mm 内存管理子系统和VM
net 网络子系统
samples 示例,示范代码
scripts 用于生成内核的脚本
security Linux保险模块
sound 声音子系统
usr 早期的用户空间代码(叫做initramfs)
tools 帮助Linux开发的工具
virt 虚构化基础设施
配置选项存储在源代码树根目录下一个名叫.config的文件中,你可以翻开这个文件手工编纂其中的配置选项,修改后或要在新的内核源代码树上应用现有配置文件,你可以使用下面的命令验证和更新配置:
内联函数
当用户空间进程使用浮点指令时,内核要负责处理从整型到浮点模式的转换。
$ patch �p1 < ../patch-x.y.z留神,补丁包也有明白的版本号,这里的版本号与Linux内核源代码的版本号要一致,内核和补丁版本号不一致时,强迫运用补丁会引起意想不到的成果。
配置内核
在源代码树的根目录下还有许多文件须要阐明,COPYING是内核允许描写文件(即GNU GPL v2),CREDITS是参加Linux内核的开发职员名单,MAINTAINERS列出了保护各个子系统和驱动的个人,Makefile是内核Makefile的基础。
生成内核
$ make oldconfig在生成内核之前必须运行这个命令。
printk(KERN_ERR "this is an error!\n");注意在KERN_ERR和打印的消息之间没有逗号,这是成心这么设计的,优先级使用一个预约义的字符定义,在编译期间它与打印的信息是串联的。
内核生成好之后,你需要安装它,如何安装于系统架构和领导加载程序有关,我们以x86架构,grub引诱加载程序为例进行解释。
Gcc C编译器容许在C函数中嵌入汇编指令,asm()编译器指令用于内联汇编代码,例如,这个内联汇编指令执行x86处置器的rdtsc指令,返回时光戳存放器(tsc)的值:
值得庆幸的是,内核提供了很多工具简化配置 ,最简单的工具是基于文本命令行的适用程序,如:
$ make config这个工具会一个选项一个选项地配置,但用户需要介入,如指定“是(y)”,“否(m)”还是“模块(m)”,全部配置进程需要很长的时间,因而,除非是有人按小时计费请你升级内核,切实找不出别的理由用这种最原始的办法配置内核了,相反,有现成的基于ncurses的图形化工具可以取代。
$ tar xvzf linux-x.y.z.tar.gz无论执行上面哪一个命令,最后都会将源代码解压和抽取到linux-x.y.z目录下,如果你使用Git下载和治理内核源代码,你不需要下载tar包,只要要运行git clone命令,它就会自动下载和解压。
◆因为内核支持异步中止和SMP,因此同步和并发是内核主要担忧的问题;
$ tar xvjf linux-x.y.z.tar.bz2如果你下载的是gzip包,则运行:
无内存保护
跟用户空间利用程序不一样,内核并没有链接到尺度的C库,也不链接到任何其它的库,这样设计的原因有良多,包含如先有鸡还是先有蛋的问题,但重要起因仍是速度和内核大小,不要说完整的C库,就是它的一个子集也够大,内核太大只会导致效力低下。
Robert Love
内核堆栈不大也不是动态的,相反,它很小且是固定的,内核堆栈的准确大小依据架构有所不同,在x86上,堆栈大小是在编译时断定的,个别是4KB或8KB,历史上,内核堆栈有2页,通常表示它处于32位架构上,大小是8KB,如果是16KB就表示是64位架构,总之大小是固定的,每个过程接受它本人的堆栈。
首先将arch/i386/boot/bzImage拷贝到/boot,重命名为vmlinuz- version,这里的version也是版本号,然后编辑/boot/grub/grub.conf,为新内核增加相应的项目,如果是使用LILO引导装载程序,则修改/etc/lilo.conf文件,然后运行lilo。
$ git pull安装内核源代码
此外,内核内存也是不可分页的,因此你消费的每个内存字节都比物理内存的一个字节要少。
配置选项可以控制生成过程要么是布尔型,要么是三态型,布尔型就是“是”或“否”,大部分内核配置选项都属于布尔型,如CONFIG_PREEMPT,而三态型则在“是”和“否”的基础上,又增加一个“模块”选项,模块选项表示配置选项被设置了,但最后会编译成模块,而不是直接编译进内核,感冒患者天天闻热饮可缓解鼻塞加速恢复,模块可以懂得为可独立动态载入的对象,一般来说,驱动配置通常都是三态型。
当用户空间的应用程序尝试一个非法的内存访问时,内核可以捉拿到毛病,发送SIGSEGV信号,杀掉进程,如果内核尝试一个非法的内存访问时,成果就不受掌握了,因为谁也无奈去控制内核,这也是内核最主要的失误。
$ make -j32 > /dev/null使用distcc或ccache等优良的工具也可以大大进步生成速度。
安装新内核
/* we predict 'success' is nearly always nonzero ... */
if (likely(success)) {
/* ... */
}当分支指令已经晓得一个优先级,或你想在一种情况下优化另一种情形时应当使用上述指令,最主要的是,当分支准确标记时,这些指令会晋升性能,但如果分支标志过错则会下降机能,在内核代码中,unlikely()要使用得更多,由于if语句偏向于表现一种特别情况。
与用户空间不一样,内核不能无缝支持浮点数,因为它自己不能容易地捕获到自己,在内核中使用浮点数需要手动保留和恢复浮点数寄存器,因此除非却有必要,否则尽量不要在内核中做浮点运算。
解决这些问题的一般方法是自旋锁和信号量。
$ make defconfig虽然默认配置有些果断(在i386上,默认配置是由Linus配置的),但如果你从未配置过内核,它提供了一个良好的开始。
◆Linux支持对称多处理(SMP),六合彩资料,因此,如果没有恰当的掩护,在两个或多个处理器上同时执行的内核代码可能会同时拜访雷同的资源;
Linux内核开发人员会将自己的修正做成补丁与其它人员分享,而且补丁是增量的,增量补丁是从一个内核树挪动到另一个内核树的有效方式,不必下载完整的内核包就可以升级内核,不仅可节俭带宽,也节省了内核进级时间,应用补丁之前进步入内核源代码树所在目录,然后运行:
下面这个命令会根据你的架构创建一个默认的配置基础。