Linux Kernel

git-bisect 使用体会

今天试着使用了一下git-bisect,收获颇多。总结一下教训:

1. 不要做git-reset( —hard),它可能会带来一些很严重的错误,比如:

fatal: Untracked working tree file ‘drivers/char/hw_random/virtio-rng.c’ would be overwritten by merge.

这时你只能无奈的重新clone。

2. 一定要找准good的起点,可以通过git-checkout -b来一个个测试寻找。

3. 多利用branch,不仅仅是针对bisect,其它场合也适用。

David Miller用的方法不错,推荐一下。

关于Linux的syslog

内核中printk发出的消息是这样传递到用户空间的。

内核留给用户空间的接口是syslog(2),glibc对它进行了包装,于是就有了klogctl(3)和syslog(3)。

先由klogd通过klogctl(3)搜集内核消息(见sysklogd/klogd.c),并通过自己实现的syslog()(注意:和syslog(2),syslog(3)都不一样)传递给 syslogd。syslogd根据/etc/syslog.conf的配置情况进行记录。syslogd提供的是一个统一的方式,它不单单记录内核的消息,还包括其它服务器进程的消息,比如Apache,vsftp。

klogd和syslogd用的syslog()的实现用的是Unix domain socket,如果你看源代码(sysklogd/syslog.c)的话很容易就知道这一点。所以,所谓的syslog()/openlog()/closelog()等接口无非就是对这个socket操作的一个包装。这也就清楚了klogd和syslogd之间的通信方式。

根据syslogd的源代码(sysklogd/syslogd.c)来看,记录日志也可以是发送到远程的,而那个socket就是用的inet socket了。

多说一句,其实内核给出的oops只是一堆地址,而把地址和相应的符号对应起来也是由klogd完成的。sysklogd/ksym.c里有搜索System.map的过程,搜索的顺序是:
“/boot/System.map”
“/System.map”
“/usr/src/linux/System.map”

这就是最好的证据。一些细心的人或许还会奇怪,我们不是还有/proc/kallsyms么?怎么不用它?我知道的解释是:为了照顾那些没有/proc文件系统的Linux系统。;-)

我们再来看syslog(3),即glibc实现的syslog()。看一下glibc中的源代码,我们就会发现glibc中的syslog和sysklogd自己的实现类似,也是用unix domain socket来实现的。具体可以看:glibc/misc/syslog.c。

这两种实现之所以都能够传递给syslogd,是因为它们的unix domain socket都是用的/dev/log这个文件。

而我们最常用的查看内核消息的命令dmesg(1)和上面两位守候进程没太大关系,它是直接通过glibc的klogctl(3)函数来读取内核消息的。详细见:util-linux-2.13-pre7/sys-utils/dmesg.c。

如果想了解更多关于syslogd的东西,可以参考这篇文章

想了解内核的中对syslog(2)的实现,可以参考内核源代码:kernel/printk.c。

关于e820

很久没接触BIOS中断这么陈旧的东西,再次挖出来都有些生疏了。

e820是和BIOS的一个中断相关的,具体说是int 0x15。之所以叫e820是因为在用这个中断时ax必须是0xe820。这个中断的作用是得到系统的内存布局。因为系统内存会有很多段,每段的类型属性也不一样,所以这个查询是“迭代式”的,每次求得一个段。

我们看内核源代码。主要涉及两个文件:arch/x86/boot/memory.c和arch/x86/kernel/e820_32.c。我们已经很幸运了,这部分代码已经用C重写过了。你可能会奇怪,启动调用e820时我们还在实模式,怎么能用C呢?答案是,这里用的是16位的C。gcc早已经支持.code16gcc模式了。

看detect_memory_e820()函数,里面就是e820的本质。它把int 0x15放到一个do-while循环里,每次得到的一个内存段放到struct e820entry里,而struct e820entry的结构正是e820返回结果的结构!而像其它启动时获得的结果一样,最终都会被放到boot_params里,e820被放到了boot_params.e820_map。

如果你对struct e820entry还有疑问,你可以看一下arch/x86/kernel/e820_32.c::print_memory_map(),看看里面是怎么使用它的。

当然了,在arch/x86/boot/memory.c里,你还会看到另外两个利用int 0x15查询内存的函数,不过用途不一样了。

凡是弄过操作系统启动这块的,肯定都有这么一个感慨:我的东西该往哪里放啊!怎么个放法啊!恩,或许Linux这种方式值得我们借鉴,它的虽然很科学,但也很复杂。那有啥办法呢,BIOS这块本来就已经很乱了!

git使用小结

1. git-config

配置git,一般需要配置的是user.name,user.email,有时sendemail.smtpserver也要配置,比如,我使用msmtp:

git-config —global sendemail.smtpserver /usr/local/bin/msmtp

如果你仅仅是想给这一个项目配置,把—global选项去掉。查看配置的选项是—list。

  1. git-pull

git-pull没必要带后面那长长的url(-_-b 我那么用了好多次,不过我用的是!git-pull)。如果你在给Linux内核这样的项目工作,记得git-pull之前检查是不是在master分支。

3. git-format-patch

如果发送多于一个补丁,最好用[PATCH n/m]的形式,加上-n。加signed-off-by那行是-s。指定为几次commit生成补丁,直接加数字,比如,

$ git-format-patch -3

检查补丁是—check,最好加上这个。

4. git-send-email

如果一次提交补丁比较多,最好用—no-chain-reply-to,因为如果不用的话,在thread嵌套会太深,不利于别人阅读。这个也可以通过选项sendemail.chainreplyto来控制。—signed-off-by-cc,要加上,可以省去手工处理的麻烦。—compose用来编辑[PATCH 0/m],这个一般是对整个patchset的描述。—smtp-server,如果你不想用git-config指定的话,用它也行。—cc和—to就不用说了。

5. git-commit

在git-commit之前最好git-add。git-commit几个常用的选项有:-s 会增加Signed-off-by行,-e编辑commit message,-a表示all,-m是指定commit信息。同样,删除文件是先git-rm。查看commit列表用git-rev-list,查看某个commit用git-show,查看commit的日志用git-log,-p是以补丁的形式查看。

6. 其它

git-diff也可以比较不同版本之间的差异,某个版本的某个文件的差异,如:

$ git-diff v2.6.22
$ git diff v2.6.20 init/main.c
$ git-diff v2.6.23 v2.6.24-rc1 init/main.c

git-whatchanged也差不多:

$ git-whatchanged -p init/main.c

7.错误提交了commit怎么办?

a) git-revert

这个本身就会产生一个commit,如果用得多了会让你的log看起来不那么干净。;-)

b) git-reset

用这个要当心,它会把那个commit之后的commit全部删除。一个好的办法是:先建立一个临时的分支,然后再git-reset,再git-rebase,最后再删除临时的分支。 详细可以看这里

内核紧急升级

vmsplice(2)惹祸了,爆出了一个安全漏洞,可以很容易提升root权限~!受影响的内核一大片。详细见下面这个bugreport:

https://bugzilla.redhat.com/show_bug.cgi?id=432229

这个bug很快就被修复:

http://lkml.org/lkml/2008/2/10/118

stable维护者今天接连推出3个稳定版升级皆是为了修复这个漏洞,见下:

2.6.24.2
http://lkml.org/lkml/2008/2/11/17

2.6.23.16
http://lkml.org/lkml/2008/2/11/19

2.6.22.18
http://lkml.org/lkml/2008/2/11/27

Fedora似乎也已经推出了新的kernel package,各位Fedora用户请尽快升级。

BTW,在Fedora7上试了试那个expliot,怎么不起作用呢?总是段错误~~

虚拟机啊,虚拟机~!

前一段时间不怎么走运,配了两个虚拟机都没配好,相当郁闷。

这两天否极泰来。Qemu在Fengguang Wu的指点下终于又跑起来了,原因还是配置内核时一些选项没有设置成y,郁闷。相当不容易啊,这次说什么不能不备份了,也顺便上传到网站上一份,可以在<-这里->看到。顺便说一句,FC7自带的虚拟机管理器不错,不过调试内核还是不能用它。没事玩儿的时候用用不错。而且FC7把Xen也给集成了,很强悍,可惜我还不知道怎么用它……

另一个虚拟机是UML,可能有不少人还不知道它,它其实是在用户空间的运行的Linux,把Linux内核放到一个用户空间进程里来跑。这两天看它的源代码时发现,其实它的中断什么的全都是用用户空间的东西模拟出来的。UML其实不需要多麻烦的配置,关键是我正好赶上UML子系统出问题(我用的是非稳定版内核),去UML用户邮件列表上发了N个邮件问Jeff,然后加了三四个patch之后,UML才总算运行起来。使用当前Linu-tree的同学可以试一下Jeff和Al的补丁[1],以及Al的另一个补丁[2],加这三个patch之后UML就可以顺利运行了。

UML其实挺好用的,而且编译迅速,以后就准备用它来测试内核了。现在一个很关键的问题是,虚拟机和主机之间的通信问题。Qemu以前弄好过,可现在又不是怎么弄了。UML还没有思路。看来又得折腾一阵子了……

[1] http://marc.info/?l=linux-kernel&m=119332492222601&q=raw

[2] http://lkml.org/lkml/2007/10/27/253

深入理解Linux内核构建系统(一)

Linux内核编译命令

作者:西邮 王聪

{做人要厚道,转载请注明出处!}

make tags
生成tags文件,供浏览代码使用。
make cscope
生成cscope索引文件,也是为了方便浏览源代码。

make oldconfig
在旧的.config基础上生成新的.config,非常有用。
make silentoldconfig
同上,但比较安静。;)
make defconfig
生成包含全部默认选项的.config文件。
make allyesconfig
生成包含全部选项的.config文件。
make allnoconfig
拒绝全部选项。
make randconfig
随机选择一些选项,一般供开发者测试使用。
make {config|xconfig|gconfig|menuconfig}
手工选择选项,只是用户界面不同,比如xconfig和gconfig都是图形界面的。

make
默认编译。
make bzImage
编译生成压缩的内核二进制文件,一般你用到的是这个。
make vmlinux
编译生成纯二进制内核文件。
make modules
编译生成内核模块。
make modules_install
安装生成的内核模块。
make {bzdisk|fdimage|isoimage}
编译生成启动软盘镜像,或光盘镜像。
make install
安装编译生成的内核文件,包括设置你的Lilo或Grub。
make all
等价于vmlinux+modules+bzImage。
make rpm
构建内核rpm包。
make foo/bar/foobar.ko
编译单个内核源文件。
make headers_install
安装内核头文件,默认安装到usr目录。
make M=some/sub/dir
编译一个指定的目录。
make O=/path/to/some/dir
把编译过程中生成的文件都放到指定目录中去。
make C={1|2}
用sparse检查内核代码。
make V={0|1|2}
控制编译过程中输出信息的多少。

make help
列出所有的make目标及其相关描述。
make kernelversion
输出Makefile里读到的内核版本信息。
make kernelrelease
输出内核发行标识。

make {rpm-pkg|deb-pkg|tar-pkg|targz-pkg|tarbz2-pkg}
构建{rpm|deb|tar|tar.gz|tar.bz2}格式的内核包。

make {htmldocs|mandocs|pdfdocs|psdocs|xmldocs}
生成{html|man|pdf|ps|xml}格式的内核文档。

make clean
清理生成文件,但会保留.config和一些模块文件。
make mrproper
清理全部文件,包括.config和一些备份文件。
make distclean
在make mrproper的基础上还清理编辑器可能生成的备份文件,patch的rej文件等。

Been there, done that...

在crquan的帮助下,总算找到了自己所有汇入Linus内核树的补丁了。可以在这里看到:

http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git&a=search&h=HEAD&st=author&s=xiyou.wangcong%40gmail.com

数了数,才十一个。印象中应该比这多,貌似被Andrew拒了几个,-mm树里似乎没了。看来还得努力啊,以后尽量不做clean up这种工作了。得做点儿实际的东西了~~

加油~!我们还刚刚开始~!^_^

一段精彩的汇编代码!

无意间在Linux内核中发现这么一段汇编代码,更准确地说应该是gcc内联汇编,写得很精彩,拿出来和大家分享一下。

代码是在include/asm-i386/string.h中,是针对i386对memcpy做进一步优化。我们知道,一个简单的memcpy其实和strcpy差不多,区别只是前者检查的是复制的个数,而后者检查的是当前字符是否是’’。

而这显然还有很大的优化余地,首先像这种非常常用的库函数是可以用汇编重写来提高速度的。当然了,可移植性是降低了,但我们可以针对每个平台写一套汇编。这样速度上的提升还是蛮乐观的。其次,memcpy显然没必要一个一个字节地复制,其实我们最多每次也就复制4个字节(当然是在i386上),但如果复制数量大的话,这种提升还是很明显的。于是下面的汇编就出来乐~!

 

评论如下:

34~36

rep=>重复

movsl=>以long为单位拷贝,从from(%esi)到to(%edi),复制n/4次。别担心,这里是i386。

37~38

恩,用n/4去和n相与。

39

若n/4个4就是全部则完成,跳转到后面的标号,结束。

40

还有剩下的,好,把n%4剩下的一个一个字节地复制。

41~44

这里看不懂的就去学gcc的嵌入汇编语法吧!