热门关键字:  ubuntu  分区  Fedora  linux系统进程  函数

当前位置 :| 主页>Linux教程>内核研究>

用kprobes实现内核反射机制

来源: 作者: 时间:2007-08-23 Tag: 点击:
 

前几天在设计事件管理器时,我就在考虑磁盘满的问题,磁盘满是一个典型的系统事件,没有什么好说的,问题是应该何时何地触发它呢?如果由应用程序在操作文件时触发,那将有很多地方需要修改,这不是我们期望的。如果能在一个地方统一处理,那就省事多了,说到统一处理,我们自然会想到修改glibc或者内核的代码。

但修改glibc或者内核的代码也非我所愿,对于这些标准的软件包,除非是有BUG,否则我是不情愿去修改它们的,特别是加入这种专用功能,因为那让以后的维护比较麻烦,升级不方便。最好的办法就是不用修改代码,而动态的改变某些函数的行为。

gcc有一个选项-finstrument-functions,它可以在函数调用前后注入指定代码,利用这些注入的代码来改变函数的行为,这是一个非常酷的特性,常用作高级的调试技巧。但我可不想给glibc中每一个函数调用都注入代码,那可能会对性能造成严重的影响。

ELF文件都是由/lib/ld-linux.so.2加载的,ld-linux提供了一种PRELOAD机制,它用于优先加载指定的共享库,可以通过LD_PRELOAD环境变量或/etc/preload.conf配置文件来指定共享库。这倒是一个不错的方法,在PRELOAD的共享库去实现部分文件操作函数,这样就可以方便的为这些函数增加新功能了。

今天无意中发现了一种更酷的方法,原来内核提供了一种称为kprobes的功能,利用它我们可以很容易实现反射机制,动态的修改某些函数的行为。下面是一个从linux-2.6.21/Documentation/kprobes.txt中抄出来的例子:

kretprobe-example.c

#include <linux/kernel.h>

#include <linux/module.h>

#include <linux/kprobes.h>

static const char *probed_func = "sys_open";

/* Return-probe handler: If the probed function fails, log the return value. */

static int ret_handler(struct kretprobe_instance *ri, struct pt_regs *regs)

{

    int retval = regs_return_value(regs);

    if (retval < 0) {

        printk("%s returns %d\n", probed_func, retval);

    }

    return 0;

}

static struct kretprobe my_kretprobe = {

    .handler = ret_handler,

    /* Probe up to 20 instances concurrently. */

    .maxactive = 20

};

static int __init kretprobe_init(void)

{

    int ret;

    my_kretprobe.kp.symbol_name = (char *)probed_func;

    if ((ret = register_kretprobe(&my_kretprobe)) < 0) {

        printk("register_kretprobe failed, returned %d\n", ret);

        return -1;

    }

    printk("Planted return probe at %p\n", my_kretprobe.kp.addr);

    return 0;

}

static void __exit kretprobe_exit(void)

{

    unregister_kretprobe(&my_kretprobe);

    printk("kretprobe unregistered\n");

    /* nmissed > 0 suggests that maxactive was set too low. */

    printk("Missed probing %d instances of %s\n",

        my_kretprobe.nmissed, probed_func);

}

module_init(kretprobe_init)

module_exit(kretprobe_exit)

MODULE_LICENSE("GPL");

Makefile

obj-m := kretprobe-example.o

KDIR := /lib/modules/$(shell uname -r)/build

PWD := $(shell pwd)

default: 

    $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules

clean:

    rm -f *.mod.c *.ko *.o

Make之后用inmod插入kretprobe-example.ko:

make;insmod kretprobe-example.ko

再用vim打开/var/log/messages,可以看到诸如:

May 29 20:35:49 lixj kernel: sys_open returns –2

之类的信息。

不过,遗憾的是它只支持下面几个平台,没有ARM版本的实现,让我白开心了一回。

- i386

- x86_64 (AMD-64, EM64T)

- ppc64

- ia64 (Does not support probes on instruction slot1.)

-          sparc64 (Return probes not yet implemented.)

更详细的内容可以阅读linux-2.6.21/Documentation/kprobes.txt
作者联系方式:李先静 <xianjimli at hotmail dot com>




相关文章:
精通initramfs构建step by step
Linux利用kexec迅速切换内核
进程上下文VS中断上下文
内核通知链 学习笔记
linux spi子系统驱动分析
menuconfig 配置选项
《Linux操作系统内核实习》之练习一
udev详解
什么叫微内核,宏内核?
Linux 信号signal处理机制
开发简单的 Linux2.6 内核模块
删除内核的perl脚本
Linux2.6内核usb gadget驱动移植
GCC hacks in the Linux kernel
iomem
kernel学习的想法
让自己的驱动支持udev
linux内核编译步骤
内核的等待队列
Linux内核wait_queue深入分析
升级和删除内核
SD卡驱动分析2
Linux Kernel VDSO本地权限提升漏洞
内核中的TCP的追踪分析-15-TCP(IPV4)的客户端与
linux 2.6内核可加载模块的编译
内核模块HelloWorld
在环回接口上发送一个数据报
ARP初始化
1分钟编译FreeBSD内核
linux设备模型之uart驱动架构分析