linux驱动静态分配内存,Linux驱动静态加载和动态加载详解

说明:这是我最近给单位写的一篇文档,没有什么复杂的东东,对刚接触linuxdriver的朋友或许有点帮助。文档原本是针对咱们本身的产品的,有些地方(路径、mknod、动态分配主设备号等)原本应该改改,由于懒惰也没去改。

在LINUX下加载驱动程序能够采用动态和静态两种方式。静态加载就是把驱动程序直接编译到内核里,系统启动后能够直接调用。静态加载的缺点是调试起来比较麻烦,每次修改一个地方都要从新编译下载内核,效率较低。动态加载利用了LINUX的module特性,能够在系统启动后用insmod命令把驱动程序(.o文件)添加上去,在不须要的时候用rmmod命令来卸载。在台式机上通常采用动态加载的方式。在嵌入式产品里能够先用动态加载的方式来调试,调试完毕后再编译到内核里。下面以咱们的nHD板卡为例讲述一下加载驱动程序的方法。

假设咱们须要添加一个名为mydrv的字符型设备驱动,主设备号为254,次设备号为0(只有一个从设备)。

静态加载的步骤以下:

一、编写本身的驱动程序源文件mydrv.c,并放在firmware\uClinux-Samsung-2500\linux-2.4.x\drivers\char下面。一个典型的字符型驱动的最后通常包括以下内容:

static int mydrv_init(void)

{

int ret;

ret = register_chrdev(mydrv_major, " mydrv ", &my_fops);

if(ret == 0) printk("register_chrdev succeed!\n");

else printk("register_chrdev fail!\n");

return 0;

}

static __exit void mydrv _cleanup(void)

{

unregister_chrdev(mydrv _major, " mydrv ");

printk("register_chrdev succeed!\n");

return ;

}

module_init(mydrv _init);

module_exit (mydrv _cleanup);

函数mydrv_init的任务是注册设备,mydrv_cleanup的任务是取消注册。 Module_init和module_exit的做用后面会讲到。

2.在firmware\uClinux-Samsung-2500\vendors\Samsung\2500\Makefile中添加以下语句(以刚才的设备为例,实际添加时固然要根据你本身的设备名称和设备号来添加):

mknod $(ROMFSDIR) /dev/mydrv c 254 0

这句话的目的是在内核中建立一个与你的驱动程序对应的设备节点。

3.在firmware\uClinux-Samsung-2500\linux-2.4.x\drivers\char\Makefile

中添加以下语句:

obj-$(CONFIG_CHAR_MYDRV) +=mydrv.o

这句话的目的是根据编译选项$(CONFIG_CHAR_MYDRV)来决定是否要添加该设备驱动。

4.在firmware\uClinux-Samsung-2500\linux-2.4.x\drivers\char\config.in

中添加:

if [“$CONFIG_ARCH_SAMSUNG”=”y”]; then

tristate ' ,MYDRV driver module ' CONFIG_CHAR_MYDRV

这句话的目的是在运行make menuconfig时产生与你的设备对应的编译选项。

5.运行make menuconfgi,应该能看到你本身的设备的选项,选中就能够了。

6.编译内核,下载,运行本身的测试程序。

若是你以为上述步骤比较麻烦,能够把四、5两条都省去,把第3条中的

obj-$(CONFIG_CHAR_MYDRV) +=mydrv.o

改成

obj-y +=mydrv.o

这样在menuconfig就没有与你的设备对应的选项了,编译内核时直接会把你的驱动编译进去。

还有一个问题须要说明一下。在…/drivers/char下有一个mem.c文件,其中最后有一个int __init chr_dev_init(void)函数。你们能够看到,全部字符设备的初始化函数(IDE_INT_init之类)都要添加在这里,而咱们刚才的驱动程序的初始化函数并无添加到这里。这个问题涉及到系统启动时的do_initcall函数,详细讲述起来比较烦琐,你们有兴趣能够看一下《情景分析》下册P726~P729。这里简单介绍一下。若是对一个函数(一般都是一些初始化函数)做以下处理(仍以咱们的mydrv_init函数为例):

__initcall(mydrv_init)

那么在编译内核时会生成一个指向mydrv_init的函数指针__initcall_mydrv_int,系统启动时,在运行do_initcall函数时,会依次执行这些初始化函数,而且会在初始化结束后把这些函数所占用的内存释放掉。

回到mem.c文件,在最后有一行:

__initcall(chr_dev_init)

这句话的做用就显而易见了,在系统启动时自动执行chr_dev_init函数。因此咱们彻底能够不用在mem.c/chr_dev_init中添加咱们本身的初始化函数,而是在咱们本身的设备文件中(mydrv.c)添加以下一行:

__initcall(mydrv_init).

在咱们前面说到的咱们本身的设备文件mydrv.c中,最后有一句:

module_init(mydrv _init);

你们能够看一下module_init的定义,在…linux.-2.4.x\include\linux\init.h中。若是定义了宏MODULE时,module_init是做为模块初始化函数,若是没有定义MODULE,则

module_init(fn)就被定义为__initcall(fn)。静态编译时是不定义MODULE的,因此咱们的驱动中的module_init就等因而:

__initcall(mydrv_init).

这样咱们的初始化函数就会在启动时被执行了。

至于到底是在mem.c/chr_dev_init中添加你的设备初始化函数,仍是在设备文件中经过module_init来完成,彻底取决于你的喜爱,没有任何差异。若是你的初始化函数只是注册一个设备(没有申请内存等操做),那即便你在两个地方都加上(等于初始化了两次)也不要紧,不会出错(有兴趣能够看一下内核里注册设备的函数实现,

…linux-2.4.x\fs\devices.c\register_chrdev)。不过为了规范起见,仍是不建议这样做。

最后一个问题。在静态加载驱动的时候,咱们那个mydrv_cleanup和module_exit函数永远不会被执行,因此去掉是彻底能够的,不过为了程序看起来结构清晰,也为了与动态加载的程序兼容,仍是建议保留着。

下面讲一下动态加载驱动的方法:

一、运行make menuconfgi,在内核配置中进入Loadable module support,选择Enable loadable module support和Kernel module loader(NEW)两个选项。在应用程序配置中进入busybox,选择insmod, rmmod, lsmod三个选项。

二、在…vendors\Samsung\2500\Makefile中添加相应的设备节点,方法与静态加载时彻底同样。

三、编写本身的驱动程序文件,在文件开始处加一句:

#define MODULE

文件最后的

mydrv_init

mydrv_cleanup

module_init(mydrv _init)

module_exit (mydrv _cleanup)

这四项必须保留。

四、仿照以下的格式写本身的Makefile文件:

KERNELDIR= /home/hexf/hardware/nHD/Design/firmware/uClinux-Samsung-2500/linux-2.4.x

CFLAGS = -D__KERNEL__ -I$(KERNELDIR)/include -Wall -Wstrict-prototypes -Wno-trigraphs -O2 -fno-strict-aliasing -fno-common -fno-common -pipe -fno-builtin -D__linux__ -DNO_MM -mapcs-32 -mshort-load-bytes -msoft-float

CC = arm-elf-gcc

all: mydrv.o

clean:

rm -f *.o

五、编译本身的驱动程序文件。注意在动态加载时只编译不链接,因此获得的是.o文件。

六、把编译后的驱动程序的.o 文件,连同本身的测试程序(假设叫mytest,注意这个是可执行文件)一块儿放在编译服务器的/exports/本身的目录下。测试程序就是一个普通的应用程序,其编写和编译的步骤这里就不讲了。

七、启动nHD板卡,用nfs的方法把编译服务器上/exports/本身的目录mount上来(假设mount 到 /mnt下)。Nfs的使用你们都很熟悉了,这里就再也不说。

八、

cd /mnt

/bin/insmod mydrv.o

如今你的设备就已经被动态加载到系统里了。能够用lsmod命令查看当前已挂接的模块。

九、运行你的测试程序

十、调试完毕后用 rmmod mydrv把你的设备卸载掉。

补充几点:

一、关于创建设备节点的问题,由于你们所使用的系统不太同样,因此不须要按照我说的方法。总之只要在你本身的系统的dev目录下创建了本身的驱动程序的设备节点就能够了。

二、没有考虑动态分配主设备号的问题。因此注册设备那个地方稍微有点不严密。

三、模块加载时要把本身的.c文件编译成.o文件,CFLAGS后面那一串编译选项有时可能有点烦人,若是你没搞定,最简单的办法就是从新编译一遍内核并重定向到一个文件中(别忘了先make clean一下):make > out。

而后在out文件里随便找一个字符驱动程序的编译过程,把它的编译选项找出来,拷贝到你本身的Makefile里就能够了。我就是这么做的。

下面是一个最简单的字符设备驱动的例子。实际的驱动千差万别,但其实也就是“填充”本身的open,close,read,write,ioctl几个函数而已。

#ifndef __KERNEL__

#define __KERNEL__

#endif

#define MODULE

#define drvtest_major 254

#include

#include

#include

#include

#include // printk()

#include // kmalloc()

#include // error codes

#include // size_t

#include // mark_bh

#include

#include

#include

#include

#include

#include

#include

static int mytest_open(struct inode *inode,struct file *filp)

{

MOD_INC_USE_COUNT;

printk("mytest open!\n");

return 0;

}

static ssize_t mytest_read(struct file *flip,char * buff,size_t count,

loff_t * f_pos)

{

char buf[10] ={0x1,0x2,0x3,0x4,0x5};

memcpy(buff,buf,5);

return 5;

}

static int mytest_close(struct inode *inode,struct file *filp)

{ MOD_DEC_USE_COUNT;

printk("mytest close!\n");

return 0;

}

static struct file_operations my_fops = {

read: mytest_read,

// write: mytest_write,

open: mytest_open,

release: mytest_close,

// ioctl: mytest_ioctl,

};

static int mytest_init(void)

{

int ret;

ret = register_chrdev(drvtest_major, "drvtest", &my_fops);

if(ret == 0) printk("register_chrdev succeed!\n");

else printk("register_chrdev fail!\n");

return 0;

}

static __exit void mytest_cleanup(void)

{

unregister_chrdev(drvtest_major, "drvtest");

printk("register_chrdev succeed!\n");

printk("bye!\n");

return ;

}

module_init(mytest_init);

module_exit(mytest_cleanup);

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值