20135319zl内核模块编译报告

内核模块编程学习报告

1.编写一个简单的输出信息的模块

源代码:

Makefile

编译模块

加载模块

测试模块(dmesg)

卸载模块

Sudo rmmod 1

使用dmesg查看情况

2.输出当前进程信息

源代码(作为2.c)

修改makefile中的目标文件为2.o

编译,加载并测试模块

3.进程列表读取功能(3.c)
源代码

修改makefile为3.o

编译,加载并测试模块:

4.可读写的文件节点内核模块

在/proc文件系统中创建一个文件节点exp2

源代码

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>

#include <linux/fs.h>     // for basic filesystem  
#include <linux/proc_fs.h>    // for the proc filesystem  
#include <linux/seq_file.h>   // for sequence files  
#include <linux/jiffies.h>    // for jiffies  
#include <linux/slab.h>       // for kzalloc, kfree  
#include <linux/uaccess.h>    // for copy_from_user  



//static struct task_struct *pcurrent;
int print_current_task_info(void);

    // global var  
    static char *str = NULL;

    // seq_operations -> show  
    static int jif_show(struct seq_file *m, void *v)
    {
        //seq_printf(m, "current kernel time is %llu
", (unsigned long long) get_jiffies_64());  

        seq_printf(m, "str is %s
", str);
        return 0; //!! must be 0, or will show nothing T.T  
    }

    // file_operations -> write  
 static ssize_t jif_write(struct file *file, const char __user *buffer, size_t count, loff_t *f_pos)
    {
        //分配临时缓冲区  
        char *tmp = kzalloc((count+1), GFP_KERNEL);
        if (!tmp)
            return -ENOMEM;

        //将用户态write的字符串拷贝到内核空间  
        //copy_to|from_user(to,from,cnt)  
        if (copy_from_user(tmp, buffer, count)) {
            kfree(tmp);
            return -EFAULT;
        }

        //将str的旧空间释放,然后将tmp赋值给str  
        kfree(str);
        str = tmp;

        return count;
    }

    // seq_operations -> open  
    static int jif_open(struct inode *inode, struct file *file)
    {
        return single_open(file, jif_show, NULL);
    }

    static const struct file_operations jif_fops =
 {
        .owner      = THIS_MODULE,
        .open       = jif_open,
        .read       = seq_read,
        .write      = jif_write,
        .llseek     = seq_lseek,
        .release    = single_release,
    };

    // module init  
    static int __init jif_init(void)
    {
        struct proc_dir_entry* jif_file;

        jif_file = proc_create("exp2", 0, NULL, &jif_fops);
        if (NULL == jif_file)
        {
            return -ENOMEM;
        }

        return 0;
    }

    // module exit  
    static void __exit jif_exit(void)
    {
        printk("******************************************
");
        remove_proc_entry("exp2", NULL);
        kfree(str);

    }

    module_init(jif_init);
    module_exit(jif_exit);

    MODULE_AUTHOR("why");
    MODULE_LICENSE("GPL");

编译,加载模块

往新建立的exp2文件中写入字符串”zhuli”,可见结果:

5.虚拟地址转换模块

源代码:

#include <linux/module.h>
#include <asm/pgtable.h>
#include <linux/version.h>
#include <asm/page.h>
#include <linux/gfp.h>
#include <linux/page-flags.h>
#include <linux/sched.h>//find_task_by_vpid
#include <linux/mm.h>//find_vma

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("CONVERT USER VIRTUAL ADDRESS TO PHYADDRESS");

static int pid;
static unsigned long va;

module_param(pid,int,0644);
module_param(va,ulong,0644);

static int find_pgd_init(void)
{
    	unsigned long pa=0;
    	struct task_struct *pcb_tmp=NULL;
    	pgd_t *pgd_tmp=NULL;
    	pud_t *pud_tmp=NULL;
    	pmd_t *pmd_tmp=NULL;
    	pte_t *pte_tmp=NULL;

    	printk(KERN_ALERT "test:va=0x%lx,pid=%d.
",va,pid);

    	rcu_read_lock();
    	if( !( pcb_tmp = pid_task(find_vpid(pid), PIDTYPE_PID) ) ) 
    	{
        	rcu_read_unlock();
        	printk(KERN_ALERT "Can't find the task %d.
",pid);
        	return 0;
    	}
    	rcu_read_unlock();

	printk("The page index_table address = 0x%p

",pcb_tmp->mm->pgd);
    
    	printk(KERN_ALERT "pgd=0x%p
",pcb_tmp->mm->pgd);
    	if(!find_vma(pcb_tmp->mm,va))
    	{
        	printk(KERN_ALERT "virt_addr 0x%lx not available.
",va);
        	return 0;
    	}
    	pgd_tmp=pgd_offset(pcb_tmp->mm,va);
    	printk(KERN_ALERT "pgd_tmp=0x%p
",pgd_tmp);
    	printk(KERN_ALERT "pgd_val(*pgd_tmp)=0x%lx

",pgd_val(*pgd_tmp));
   	 if(pgd_none(*pgd_tmp))
    	{
     	   printk(KERN_ALERT "Not mapped in pgd.
");
     	   return 0;
    	}

    	pud_tmp=pud_offset(pgd_tmp,va);
    	pmd_tmp=pmd_offset(pud_tmp,va);


    	pte_tmp=pte_offset_kernel(pmd_tmp,va);
    	if(pte_none(*pte_tmp))
    	{
        	printk(KERN_ALERT "Not mapped in pte.
");
        	return 0;
    	}
    	if(!pte_present(*pte_tmp))
    	{
   	     	printk(KERN_ALERT "pte not in RAM,maybe swaped.
");
    	    	return 0;
    	}
    	pa=(pte_val(*pte_tmp)&PAGE_MASK)|(va&~PAGE_MASK);
    	printk(KERN_ALERT "Virtual address: 0x%lx in RAM is 0x%lx.
",va,pa);
    	printk(KERN_ALERT "Part content in 0x%lx is 0x%lx.
",pa,*(unsigned long*)((char *)pa+PAGE_OFFSET));
	int i;
	printk("some content:
");
	for(i=0;i<40;i=i+4)
	{
		printk("%lx
",*(unsigned long*)((char*)pa+PAGE_OFFSET+i));
	}	
    return 0;
}

static void find_pgd_exit(void)
{
    	printk(KERN_ALERT "Goodbye.
");
}

module_init(find_pgd_init);
module_exit(find_pgd_exit);

MODULE_LICENSE("GPL");

MODULE_AUTHOR("Why");

MODULE_DESCRIPTION("GET WESSAGE");

源代码中find_pgd_init()为模块初始化函数,find_pgd_exit为模块析构函数。

代码中还定义了所要传入的参数,即所指定进程的进程号pid,数据类型为int,模块参数以module_param(name,type,perm)的形式定义,其中name为参数名,type为参数的数据类型,perm是一个权限值,控制谁可以存取模块参数在sysfs中的表示。此处还要注意的是要对内核模块的初始化函数和析构函数进行声明,即指明哪个函数是内核模块的初始化函数,哪个是内核模块的析构函数。

module_init()指明的是模块初始化函数,module_exit()指明的是模块析构函数。

MODULE LICENSE、MODULE_AUTHOR、MODULE_DESCRIPTION 分别为模块证书、模块作者和模块描述。

选取gedit验证模块

查询当前开启的gedit的pid

查询gedit的虚拟地址

可见此时gedit的pid为5207,虚拟地址的16进制表示为0x805e604,使用计算器转换为10进制为134604292

编译,加载并测试模块,可见:(加载模块时需要做如下操作)

测试结果:

可见,输出序列为53,83,ec ,08…

验证模块功能

6.使用文件节点存储页表查询结果

源代码:

#include <linux/module.h>  

#include <linux/init.h>  

#include <linux/kernel.h>  
  

#include <linux/fs.h>     // for basic filesystem  

#include <linux/proc_fs.h>    // for the proc filesystem  

#include <linux/seq_file.h>   // for sequence files  

#include <linux/jiffies.h>    // for jiffies  

#include <linux/slab.h>       // for kzalloc, kfree  

#include <linux/uaccess.h>    // for copy_from_user 


#include <asm/pgtable.h>

#include <linux/version.h>

#include <asm/page.h>

#include <linux/gfp.h>

#include <linux/page-flags.h>

#include <linux/sched.h>//find_task_by_vpid

#include <linux/mm.h>//find_vma



static unsigned long pid=5263;
static unsigned long va=134604292;
int i=0;
unsigned long pa = 0;

//static struct task_struct *pcurrent;

int print_current_task_info(void);
static int jif_show(struct seq_file *m, void *v);
    
// global var  
    
//static char *str = NULL;        
    // file_operations -> write  
    static ssize_t jif_write(struct file *file, const char __user *buffer, size_t count, loff_t *f_pos)  
    {  
         
        unsigned  long *tmp = kzalloc((count+1), GFP_KERNEL);  
        if (!tmp)  
            return -ENOMEM;  
      
         
        //copy_to|from_user(to,from,cnt)  
        if (copy_from_user(tmp, buffer, count)) {  
            kfree(tmp);  
            return -EFAULT;  
        }  
      
         
        kfree(tmp);  
        //pid = *tmp;  
      
        return count;  
    }  
      
    // seq_operations -> open  
    static int jif_open(struct inode *inode, struct file *file)  
    {  
        return single_open(file, jif_show, NULL);  
    }  
      
    static const struct file_operations jif_fops =   
    {  
        .owner      = THIS_MODULE,  
        .open       = jif_open,  
        .read       = seq_read,  
        .write      = jif_write,  
        .llseek     = seq_lseek,  
        .release    = single_release,  
    };  
      
    // module init  
    static int __init jif_init(void)  
    {  
        struct proc_dir_entry* jif_file;  
      
        jif_file = proc_create("exp4", 0, NULL, &jif_fops);  
        if (NULL == jif_file)  
        {  
            return -ENOMEM;  
        }  
      
        return 0;  
    }  
      
    // module exit  
    static void __exit jif_exit(void)  
    { 
	printk("******************************************
");
        remove_proc_entry("exp4", NULL);  
        kfree(pid);
  
    }

static int find_pgd_init(struct seq_file *m) 
{
	struct task_struct *pcb_tmp = NULL;
	pgd_t *pgd_tmp = NULL;
	pte_t *pte_tmp = NULL;

    pud_t *pud_tmp = NULL;
    pmd_t *pmd_tmp = NULL;

    seq_printf(m,"test:va=0x%lx,pid=%ld.
",va,pid);
	
	rcu_read_lock();
	if(!(pcb_tmp = pid_task(find_vpid(pid),PIDTYPE_PID))) 
	{
		seq_printf(m,"can not find pro %ld .
", pid);
		return 0;
	}
	rcu_read_unlock();

	seq_printf(m,"ye_mu_lu_biao_ji_zhi = 0x%p
",pcb_tmp->mm->pgd);
	if(!find_vma(pcb_tmp->mm,va))	
        {
        	seq_printf(m,"xu_ni_di_zhi%lxbu_cun_zai
",va);
        	return 0;
	}

	pgd_tmp=pgd_offset(pcb_tmp->mm,va);
	seq_printf(m,"
pgd=----0x%p
",pgd_tmp);

    pud_tmp=pud_offset(pgd_tmp,va);
    pmd_tmp=pmd_offset(pud_tmp,va);


    pte_tmp=pte_offset_kernel(pmd_tmp,va);





	seq_printf(m,"
pte=----0x%p
",pte_tmp);
	seq_printf(m,"

 pid=%ld
va=%lx

",pid,va);

	pa=(pte_val(*pte_tmp)&PAGE_MASK)|(va&~PAGE_MASK);
	seq_printf(m, "xu_ni_di_zhi %lx qi_zai_nei_cun_zhong %lx.
",va,pa);
	seq_printf(m, "bu_fen_nei_rong  0x%lx is  0x%lx.
.
",pa,pa);
	for(i=0;i<40;i=i+4)
	seq_printf(m,"	%lx
",*(unsigned long*)((char*)pa+PAGE_OFFSET+i));

	return 0;
}


// seq_operations -> show  

     static int jif_show(struct seq_file *m, void *v)
    {
        //seq_printf(m, "current kernel time is %llu
", (unsigned long long) get_jiffies_64());  

            //seq_printf(m, "str is %s
", str);
                find_pgd_init(m);
        return 0; //!! must be 0, or will show nothing T.T  
    }



MODULE_LICENSE("GPL");

MODULE_AUTHOR("ny");

MODULE_DESCRIPTION("GET WESSAGE");

module_init(jif_init);  
    
module_exit(jif_exit);  

其中参数pid和va在一开始写入源代码。

编译,加载模块后,直接显示创建的文件exp4内容,可见:

模块功能实现

原文地址:https://www.cnblogs.com/20135319zl/p/5560692.html