1 工具
vmware9+Fedora8+linux2.6.34.14的内核源代码+ext3文件系统(网上下载的)
题中所说的UML不是建模用的那玩意,它指User mode linux,可以让linux在用户模式下运行!
2 安装虚拟机与Fedora8系统
安装vmware,然后在vmware中安装Fedora8!十分简单,略……
3 构建UML
在http://www.kernel.org点击打开链接下载2.6.34.14的内核源代码,在Fedora8中以root身份,将内核源代码包拷贝到/usr/src目录。
如果包是.tar.xz后缀的包,需要执行以下命令解压:
xz -d linux-2.6.34.14.tar.xz
tar xvf linux-2.6.34.14.tar 此时/usr/src目录里面会有一个linux-2.6.34.14的文件夹。
进入linux-2.6.34.14目录,以root运行
make defconfig ARCH=um(如果提示没有gcc和相关包,用yum安装就可以了!)
make ARCH=um
make modules ARCH=um
等编译完毕,在此目录会生成一个linux的可执行文件,要让它能够运行起来,还需要一个文件系统,在http://fs.devloop.org.uk/点击打开链接下载一个比较小的文件系统就足够了(要支持UML,推荐Debian Squeeze 32位的文件系统)
4 编译和运行UML
文件系统下载完毕以后,将Debian-Squeeze-x86-root_fs.bz2拷贝到/usr/src目录,用
bzip2 -d Debian-Squeeze-x86-root_fs.bz2
解压,并将其重命名为root_fs(要不以后要输入很长的字符串!!)
进入linux-2.6.34.14目录,以root身份运行
./linux ubda=../root_fs
此时,输入root便能能进入系统,如果要退出UML,输入shutdown -h now即可!
5 调试
在调试内核的sys_socketcall之前,最好先编写一个socket的程序并进行编译,让其能在UML中运行,下面我给出一个能编译的例子(出自《追踪Linux TCP/IP代码运行》),最后将其编译成一个名为socket的可执行文件!
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
int main(){
int server_fd,client_fd;
int server_len,client_len;
struct sockaddr_in server_address;
struct sockaddr_in client_address;
char back[]={"I am server"};
server_fd=socket(AF_INET,SOCK_STREAM,0);
server_address.sin_family=AF_INET;
server_address.sin_addr.s_addr=inet_addr("127.0.0.1");
server_address.sin_port=htons(9266);
server_len=sizeof(server_address);
bind(server_fd,(struct sockaddr*)&server_address,server_len);
listen(server_fd,10);
while(1){
char recv[20];
printf("server is wating!\n");
client_len=sizeof(client_address);
client_fd=accept(server_fd,(struct sockaddr*)&client_address,&client_len);
read(client_fd,recv,20);
write(client_fd,back,20);
printf("received from client=%s\n",recv);
close(client_fd);
}
close(server_fd);
exit(0);
}
将这个socket的可执行文件拷贝到root_fs文件系统。要实现拷贝,得先挂载文件系统,以root身份依次执行
mkdir /mnt/my_fs
cd /usr/src
mount -o loop root_fs /mnt/my_fs
cp socket /mnt/my_fs/root
完成拷贝之后:
cd /usr/src/linux-2.6.34.14
./linux ubda=root_fs
从这个图中可以看出,socket文件能够在root_fs文件系统正常的运行。
显然,这个socket文件在执行的时候一定会调用sys_socketcall这个系统调用,现在开始调试
cd /usr/src/linux-2.6.34.14
gdb linux
进入gdb以后依次输入:
break sys_socketcall (在sys_socketcall设置断点)
run ubda=../root_fs
此时会收到一个SIGSEGV的信号,此时输入
handle SIGSEGV pass nostop noprint 就可以了(这条语句也可以在run之前输入)
输入continue。程序就会正常的停在sys_socketcall
不过此时内核并不是在执行socket时停住的,而是在系统初始化时停住的!要让系统完全执行起来,得继续执行continue,这个断点会让内核停很多次。按住回车就好(耐心,期间会让输入root),直到出现如下画面
之后输入./socket,内核就好停在sys_socketcall这个系统调用,之后就可以参照《追中Linux TCP/IP代码运行》来单步调试了!!!
可以用GNU的DDD调试器来调试,效果更好!(此方法不限于调试sys_socketcall,还可以调试其它函数,如start_kernel()函数)
能让内核执行到这里很是不容易,所以以后就不要关掉Fedora8了,Suspend就好,下次要用Resume就可以了!
ps: 阅读linux内核源代码是一项非常艰难的工作,调试是高效阅读代码的一种方式,在阅读源代码时对于一些可重用函数,要弄清楚它是在哪里被声明;函数跳转时,要记住是哪些参数被带入到了下级函数(返回值也可以看作带入下级函数的特殊参数)!