Linux 内核为了减少命名空间的污染,并做到正确的信息隐藏,内核提供了管理内核符号可见性的方法,没有被 EXPORT_SYMBOL 相关的宏导出的变量或函数是不能直接使用的,为了说明并解决这个问题,我们不妨先看如下一段内核模块,功能为打印超级块 super_block 结构中一些域的值。
我们知道vfs(虚拟文件系统)是用 super_block (超级块)来描述整个文件系统的信息,内核在对一个文件系统进行初始化和注册时,就为其分配了一个 super_block,该文件系统卸载时,其对应的 super_block 也会被自动删除。super_block 结构中有一个 list_head 类型的字段 s_list 用来把系统中的 super_block 组成一个双向循环链表,并使用一个叫做 super_ blocks 的全局变量来指向该双向循环链表中的第一个元素。super_block 中还有一个叫做 s_inodes 的字段,指向链接该超级块中所有的 inode 的链表 i_sb_list。我们也使用了自旋锁 spin_lock 对链表的相关操作进行了加锁,保护共享变量,现在看内核模块:
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/kdev_t.h>
#include <linux/kallsyms.h> //方法二
/*
*方法二,使用kallsyms_lookup_name()查找函数或变量的虚拟地址
*使用时,需要注释其它方法的代码,取消此处及下面方法二的注释
spinlock_t * sb_lock_address;
struct list_head * super_blocks_address;
*/
/*
*方法三,内核模块中直接使用内核函数的虚拟地址
*使用时,需要注释其他方法的代码,取消此处及下面方法三的注释
#define SUPER_BLOCKS_ADDRESS 0xffffffff91d2efe0
#define SB_LOCK_ADDRESS 0xffffffff922f35d4
*/
static int __init my_init(void)
{
struct super_block *sb;
struct list_head *pos;
struct list_head *linode;
struct inode *pinode;
unsigned long long count = 0;
printk("\nPrint some fields of super_blocks:\n");
/*
*方法二
sb_lock_address = (spinlock_t *)kallsyms_lookup_name("sb_lock");
super_blocks_address = (struct list_head *)kallsyms_lookup_name("super_blocks");
spin_lock(sb_lock_address);
list_for_each(pos, super_blocks_address) {
*/
/*
*方法三
spin_lock((spinlock_t *)SB_LOCK_ADDRESS);
list_for_each(pos, (struct list_head *)SUPER_BLOCKS_ADDRESS) {
*/
/*此处使用了未导出变量,若使用方法二或方法三时需要注释以下两行*/
spin_lock(&sb_lock); //加锁,此处使用了未导出的变量
list_for_each(pos, &super_blocks) {
sb = list_entry(pos, struct super_block, s_list);
printk("dev_t:%d:%d", MAJOR(sb->s_dev),MINOR(sb->s_dev));
//打印文件系统所在设备的主设备号和次设备号
printk("file_type name:%s\n", sb->s_type->name);
//打印文件系统名
list_for_each(linode, &sb->s_inodes) {
pinode=list_entry(linode, struct inode, i_sb_list);
count++;
printk("%lu\t", p