Linux内核编程实战经验谈 李艳彬 |
---|
01-7-27 上午 10:45:17 |
当前,在国产自主版权的操作系统这面大旗的倡导下,IT界掀起了一浪高过一浪的Linux编程热潮。Linux以其源码开放、配置灵活等不可多得的优越性吸引着越来越多的编程爱好者深入Linux的内核开发。笔者近来实践过一个Linux的实时化改造课题任务,积累了一点Linux内核编程的实战经验,在这里想就编译内核、增加系统调用等方面的问题和感兴趣的爱好者共做切磋。 |
---|
编译内核 |
---|
在Linux编程的实践中,经常会遇到编译内核的问题。为什么要编译内核呢?其一,可以定制内核模块。Linux引入了“动态载入模块”的概念,使用户可以把驱动程序以及非必要的内核功能代码编译成“模块”,由系统在需要时动态载入,不需要时自动卸载,从而提高了系统的效率和灵活性。其二,可以定制系统功能。当添加某种设备时、增加系统功能时、系统暴露出缺陷需要打“补丁”时,当新版内核出现准备用来升级时,编译内核是不可避免的。而且,编译内核正是Linux独有的“系统级DIY”的魅力所在! |
---|
好,现在就让我们一起开始——编译内核! |
---|
(1)安装源码 |
---|
首先要确定自己Linux系统是否已安装了内核源码: |
---|
# rpm -q kernel-source |
---|
kernel_source-2.2.5-16 |
---|
如果证实没有安装,则需要找来安装盘或从网上下载kernel-source-2.2.5-15.i386.rpm并安装: |
---|
# rpm -Uhv kernel-source-2.2.5-15.i386.rpm |
---|
如果是升级到新版本,则需要找来升级包(linux-2.2.16.tar.gz),自己解压安装: |
---|
# cd /usr/src |
---|
进入源码目录。 |
---|
# rm -rf linux |
---|
删除以前的链接。 |
---|
# tar xzvf linux-2.2.16.tar.gz |
---|
解压升级包。 |
---|
# ln -s linux-2.2.16 linux |
---|
重建目录链接。 |
---|
(2)配置内核 |
---|
进入内核源码所在目录: |
---|
# cd /usr/src/linux |
---|
先清除多余的(一般是以前编译生成的)文件: |
---|
# make mrproper |
---|
开始配置内核(如果对各选项不是很熟悉的话,建议按回车键): |
---|
# make config |
---|
(3)编译内核 |
---|
清除以前生成的目标文件及其他文件: |
---|
# make clean |
---|
理顺各文件之间的依存关系: |
---|
# make dep |
---|
编译压缩的内核: |
---|
# make bzImage |
---|
编译模块: |
---|
# make modules-install |
---|
(4)装新内核 |
---|
将新内核文件复制到用于存放启动文件的 /boot目录: |
---|
# cp /usr/src/linux/System.map /boot/System.new |
---|
# cp /usr/src/linux/arch/i386/boot/bzImage /boot/vmlinuz.new |
---|
进入启动目录: |
---|
# cd /boot |
---|
给新内核建立链接: |
---|
# rm System.map |
---|
# ln -s System.new System.map |
---|
# rm vmlinuz |
---|
# ln -s vmlinuz.new vmlinuz |
---|
编辑LILO的配置文件/etc/lilo.conf ,使LILO能启动新内核: |
---|
# vi /etc/lilo.conf |
---|
在文件末加入以下部分:(后两行内容要与旧内核相应行保持一致) |
---|
image=/boot/vmlinuz.new |
---|
lable=new |
---|
root=/dev/hda3 |
---|
read-only |
---|
重写LILO的启动扇区,使改动生效: |
---|
# lilo |
---|
(5)重启系统 |
---|
# reboot |
---|
当重启后出现 lilo: 提示时输入新内核的标号(按TAB键可显示所有的标号): |
---|
lilo: new |
---|
OK!!boot new…… |
---|
….. |
---|
一切运行正常,新内核引导成功! |
---|
以上步骤在pentium Ⅲ/64M/20G、Red Hat Linux 6.0(2.2.5-15)机上测试通过。 |
---|
增加系统调用 |
---|
在实际编程中,尤其是当我们需要增加或完善系统功能的时候,我们经常会用到系统调用函数。系统调用函数通常由用户进程在用户态下调用,内核通过system_call 函数响应系统调用产生的软中断,在正确访问核心栈、系统调用开关表之后陷入到操作系统内核中进行处理。 |
---|
系统调用是用户进程由用户态切换到核心态的一种常见方式。利用编写系统调用函数来直接调用了部分操作系统内核代码,也是Linux内核编程者必修之功。下面笔者以在Linux中创建一个名为print_info的系统调用函数为例,来说明如何为内核增加系统调用。 |
---|
需要以下几个基本步骤: |
---|
1、编写系统调用函数 |
---|
编辑sys.c文件: |
---|
# cd /usr/src/linux/kernel |
---|
# vi sys.c |
---|
在文件的最后增加一个系统调用函数: |
---|
asmlinkage int sys_print_info(int testflag) |
---|
{ |
---|
printk(” Its my syscall function!n”); |
---|
return 0; |
---|
} |
---|
该函数有一个int型入口参数testflag,并返回整数0。 |
---|
2、修改与系统调用号相关的文件 |
---|
编辑入口表文件: |
---|
# cd /usr/src/linux/arch/i386/kernel |
---|
# vi entry.S |
---|
把函数的入口地址加到sys_call_table表中: |
---|
arch/i386/kernel/entry.S中的最后几行源代码修改前为: |
---|
…… |
---|
.long SYMBOL_NAME(sys_sendfile) |
---|
.long SYMBOL_NAME(sys_ni_syscall) /* streams1 */ |
---|
.long SYMBOL_NAME(sys_ni_syscall) /* streams2 */ |
---|
.long SYMBOL_NAME(sys_vfork) /* 190 */ |
---|
rept NR_syscalls-190 |
---|
.long SYMBOL_NAME(sys_ni_syscall) |
---|
.endr |
---|
修改后为: |
---|
…… |
---|
.long SYMBOL_NAME(sys_sendfile) |
---|
.long SYMBOL_NAME(sys_ni_syscall) /* streams1 */ |
---|
.long SYMBOL_NAME(sys_ni_syscall) /* streams2 */ |
---|
.long SYMBOL_NAME(sys_vfork) /* 190 */ |
---|
.long SYMBOL_NAME(sys_print_info) /* added by I */ |
---|
.rept NR_syscalls-191 |
---|
.endr |
---|
修改相应的头文件: |
---|
# cd /usr/src/linux/include/asm |
---|
# vi unistd.h |
---|
把增加的sys_call_table表项所对应的向量,在include/asm/unistd.h中进行必要申明,以供用户进程和其他系统进程查询或调用。 |
---|
#define __NR_putpmsg 189 |
---|
#define __NR_vfork 190 |
---|
#define __NR_print_info 191 /* added by I */ |
---|
3、编译内核,再重启动 |
---|
4、测试 |
---|
编写用户测试程序(test.c): |
---|
# vi test.c |
---|
#include |
---|
#include |
---|
extern int errno; |
---|
_syscall1(int,print_info,int,testflag) |
---|
main() |
---|
{ |
---|
int i; |
---|
i= print_info(0); |
---|
if(i==0) |
---|
printf(“i=%d , syscall success!n”,i); |
---|
} |
---|
如果要在用户程序中使用系统调用函数,那么在主函数main前必须申明调用_syscall,其中1 表示该系统调用只有一个入口参数,第一个int 表示系统调用的返回值为整型,print_info为系统调用函数名,第二个int 表示入口参数的类型为整型,testflag为入口参数名。 |
---|
编译测试程序: |
---|
# gcc -o test test.c |
---|
执行测试程序: |
---|
# ./test |
---|
Its my syscall function! |
---|
i=0, syscall success! |
---|
ok!!!增加系统调用函数成功! |
---|
以上步骤在pentium Ⅲ/64M/20G、Red Hat Linux 6.0(2.2.5-15)机上测试通过。 |
---|
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/184684.html原文链接:https://javaforall.cn