内核态写文件

来源:互联网 发布:多益网络有多少员工 编辑:程序博客网 时间:2024/06/05 14:29

一、test.c

#include <linux/module.h>#include <linux/init.h>#include <linux/fs.h>#include <linux/uaccess.h> #define DIRNAME "/home/snail" #define FILENAME "my_log_a_core.bin" static char buf[] = "你好1234213";   int __init hello_init(void) { struct file *filp; mm_segment_t fs; loff_t pos;  char *dirName=DIRNAME; char *fileHeader=FILENAME;  char filename[512]={'\0'}; unsigned int i=0; while(*dirName) { filename[i]=*dirName; i++; dirName++; } filename[i]='/'; i++; while(*fileHeader) { filename[i]=*fileHeader; i++; fileHeader++; } filename[i]='\0'; filp = filp_open(filename, O_RDWR| O_APPEND| O_CREAT, 0640);if(IS_ERR(filp)) { printk("open error...\n"); return 0; } fs=get_fs(); set_fs(KERNEL_DS); pos = 0; vfs_write(filp, buf, strlen(buf), &pos);set_fs(fs); filp_close(filp, NULL); return 1; } void __exit hello_exit(void) {     printk("hello exit\n"); }   module_init(hello_init); module_exit(hello_exit);


二、makefile

obj-m := test.o     KDIR := /lib/modules/`uname -r`/buildPWD := $(shell pwd) default:make -C $(KDIR) M=$(PWD) modules clean:rm -rf *.o .cmd *.ko *.mod.c .tmp_versions 


 

三、make

 

四、sudo insmod test.ko

 

五、sudo rmmod test.ko

 

可能错误:

1.warning: control reaches end of non-void function

函数忘记返回值

2.ISO C90 forbids mixed declarations and code 警告

变量定义之前任何一条非变量定义的语句(注意:语句是会带分号的)都会引起这个警告!

将非变量的定义移到变量定义之后 即可

 

 

 

/***********************************参考************************************************/

在/home/tmp/下建立两个文件hello.c,Makefile

hello.c
------------
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

MODULE_LICENSE("GPL");

static int hello_init(void)
{
printk(KERN_ALERT "hello module init\n");
return 0;
}

static void hello_exit(void)
{
printk(KERN_ALERT "hello module exit\n");
}

module_init(hello_init);
module_exit(hello_exit);

--------------

Makefile
----------
ifneq ($(KERNELRELEASE),)
obj-m := hello.o
else
KERNELDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif

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

-----------
编译模块

#make

清除

#make clean

-----------

为了能够在终端显示信息,要修改
/lib/modules/2.6.10/build/include/linux/kernel.h
文件的KERN_ALERT宏。
#define KERN_ALERT "<1>"
修改为
#define KERN_ALERT "<0>"

------------
安装模块

#insmod hello.ko

终端显示
hello module init

查看已安装的模块
#lsmod

卸载模块

#rmmod hello

终端显示
hello module exit

-----------
有以下几点要注意:
1,hello.c文件中调用的头文件
init.h中的module_init(),module_exit()
kernel.h中的printk(),KERN_ALERT
module.h中的MODULE_LICENSE()

2,Makefile文件中的核心是
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
1),-C $(KERNELDIR)
表示在$(KERNELDIR)目录下执行make命令。
2),M=$(PWD)
表示包含$(PWD)下的Makefile文件。
3),modules
表示模块编译。
4), 用到了ifneq...else...endif语句
由于开始还没定义KERNELRELEASE,所以只能执行else分支。
而在执行
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
后,会在内核的Makefile中定义KERNELRELEASE,当进入本Makefile时,
则只会执行ifneq的第一个分支,即
obj-m := hello.o
这一句话是非常重要的。事实上,这个Makefile做的本份工作就是它。
我们也可以用命令行的方式来编译:

在Makefile中的内容写为:
obj-m := hello.o
然后在终端敲入:
#make -C /lib/modules/2.6.10/build M=/home/tmp modules


/****************************************************************************************/

第一步:准备源代码

首先我们还是要来编写一个符合linux格式的模块文件,这样我们才能开始我们的模块编译。假设我们有一个源文件mymod.c。它的源码如下:

mymod.c

1. #include <linux/module.h>    /* 引入与模块相关的宏 */
2. #include <linux/init.h>        /* 引入module_init() module_exit()函数 */
3. #include <linux/moduleparam.h>/* 引入module_param() */
4
5. MODULE_AUTHOR("Yu Qiang");
6. MODULE_LICENSE("GPL"); 
7
8. static int nbr = 10;
9. module_param(nbr, int, S_IRUGO);
10.
11. static int __init yuer_init(void)
12.{
13.    int i;
14.    for(i=0; i<nbr; i++)
15.    {
16.        printk(KERN_ALERT "Hello, How are you. %d\n", i);
17.    }
18.    return 0;
19.}
20.
21.static void __exit yuer_exit(void)
22.{
23.    printk(KERN_ALERT"I come from yuer's module, I have been unlad.\n");
24.}
25.
26. module_init(yuer_init);
27. module_exit(yuer_exit);

我们的源文件就准备的差不多了,这就是一个linux下的模块的基本结构。第9行是导出我们的符号变量nbr。这样在你加载这个模块的时候可以动态修改这个变量的值。稍后将演示。yuer_init()函数将在模块加载的时候运行,通过输出的结果可以看到我们的模块是否加载成功。


第二步:编写Makefile文件
    
首先还是来看看我们Makefile的源文件,然后我们再来解释;

Makefile
obj-m := modules.o                   #要生成的模块名     
modules-objs:= mymod.o        #生成这个模块名所需要的目标文件

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

default:
make -C $(KDIR) M=$(PWD) modules

clean:
rm -rf *.o .* .cmd *.ko *.mod.c .tmp_versions


现在我来说明一下这个Makefile。请记住是大写的Makefile而不是小写的makefile;
obj-m :这个变量是指定你要声称哪些模块模块的格式为 obj-m := <模块名>.o
modules-objs :这个变量是说明声称模块modules需要的目标文件 格式要求   <模块名>-objs := <目标文件>
       切记:模块的名字不能取与目标文件相同的名字。如在这里模块名不能取成 mymod;
KDIR   :这是我们正在运行的操作系统内核编译目录。也就是编译模块需要的环境
M=     :指定我们源文件的位置
PWD   :这是当前工作路径$(shell   )是make的一个内置函数。用来执行shell命令。


第三步:编译模块

现在我们已经准备好了我们所需要的源文件和相应的Makefile。我们现在就可以编译了。在终端进入源文件目录输入make
运行结果:
    make[1]: Entering directory `/usr/src/linux-headers-2.6.24-24-generic'
    CC [M] /home/yuqiang/桌面/mymodule/mymodules.o
    LD [M] /home/yuqiang/桌面/mymodule/modules.o
    Building modules, stage 2.
    MODPOST 1 modules
    CC      /home/yuqiang/桌面/mymodule/modules.mod.o
    LD [M] /home/yuqiang/桌面/mymodule/modules.ko
    make[1]: Leaving directory `/usr/src/linux-headers-2.6.24-24-generic'


第四步:加载/卸载我们的模块

从上面的编译中我可以看到。已经有一个modules.ko生成了。这就是我们的模块了。现在我们就可以来加载了。
首先在终端输入:sudo insmod modules.ko
现在我们来看看我们的模块加载成功没有呢?
在终端输入:dmesg | tail -12   这是查看内核输出信息的意思。tail -12 显示最后12条;
显示结果如下:
    [17945.024417] sd 9:0:0:0: Attached scsi generic sg2 type 0
    [18046.790019] usb 5-8: USB disconnect, address 9
    [19934.224812] Hello, How are you. 0
    [19934.224817] Hello, How are you. 1
    [19934.224818] Hello, How are you. 2
    [19934.224820] Hello, How are you. 3
    [19934.224821] Hello, How are you. 4
    [19934.224822] Hello, How are you. 5
    [19934.224824] Hello, How are you. 6
    [19934.224825] Hello, How are you. 7
    [19934.224826] Hello, How are you. 8
    [19934.224828] Hello, How are you. 9

看到了吧。我们的模块的初始化函数yuer_init();已经成功运行了。说明我们的模块已经加载成功;
现在我们再来卸载模块试试看。
在终端输入:sudo rmmod modules
在终端输入:dmesg | tail -3
    [19934.224826] Hello, How are you. 8
[19934.224828] Hello, How are you. 9
[20412.046932] I come from yuer's module, I have been unlad.

可以从打印的信息中看到,我们的模块的退出函数已经被执行了。说明我们的模块已经被成功的卸载了。到目前位置我们就已经算是对模块的编译到编译运行算是有了一个整体上的认识了。对于以后深入的学习还是应该有点帮助的。下面我们将在看看于模块相关的一些简单的操作。


第五步:加载模块时传递参数
在终端输入:sudo insmod module_name.ko nbr=4
在终端输入:dmesg | tail -6
显示结果如下:
[20800.655694] Hello, How are you. 9
[21318.675593] I come from onefile module, I have been unlad.
[21334.425373] Hello, How are you. 0
[21334.425378] Hello, How are you. 1
[21334.425380] Hello, How are you. 2
[21334.425381] Hello, How are you. 3

这样我们就可以看到在模块加载的时候动态设置了我们的一个变量。初始化函数中的循环只执行了4次。
可能你会问我怎么知道一个模块可以设置那些变量呢。当然,你可以先不设变量加载一次。然后可以在终端输入ls /sys/module/<modules_name>/parameters/来查看。在这里我们是这样输入的
在终端输入:ls /sys/moedle/modules/parameters/
显示结果:
nbr

如果我们的模块加载成功了。最后我们还可以通过modinfo来查看我们的模块信息。如下
在终端输入:sudo modinfo modules.ko
    显示结果:
filename:       modules.ko
license:        GPL
author:         Yu Qiang
srcversion:     20E9C3C4E02D130E6E92533
depends:        
vermagic:       2.6.24-24-generic SMP mod_unload 586 
parm:           nbr:int

本文总结:
    本文的相关知识都好像有一点浅尝辙止的感觉。因为本篇文章主要是通过一条线式方式来讲解了模块编写的相关过程,其实在这个过程中还有很多可以发散的地方。例如:
在写到MODULE_AUTHOR("Yu Qiang")的时候,你应该想到还有
     MODULE_DESCRIPTION(模块用途的简单描述);
    MODULE_VERSION(模块的版本字符串);
    MODULE_ALIAS(模块的别名);
    ...

在写到module_param(nbr, int, S_IRUGO);的时候,你应该想到还有
     EXPORT_SYMBOL(name); 可以导出模块的函数,这也是模块编写的最终目的
...

在用到insmod 和 modinfo的时候。你应该想到还有
    depmod     分析可加载模块的依赖性,并生成modules.dep文件和映射文件
    modprobe   Linux内核添加删除模块
...

如果要成为一名专业的linux模块开发人员,还要走很多的路,就看各位的了,祝大家学习顺利。

我并不是什么linux相关的专家,我只是一个普通的linux相关开发人员(在读),我的目的也很简单,就是想给大家介绍一个方法,希望大家在学习的时候不要走过多的弯路。由于个人能力所限难免存在错误和缺点,所以请不要介意,如有发现请提出,我立即修改。方便大家学习


本文来源:『』 有水的地方就有余文章转载自20065562's Blog请点击这里查看原文

地址:http://hi.baidu.com/20065562/blog/item/1f918825f767df0b4d088dcf.html

 

原创粉丝点击