linux 内核模块的编译(用于驱动开发)

最近在入门linux的设备驱动,开始时连一个最简单的helloworld的驱动都编译失败,开始时走了很多弯路,最后经过几天反复的研究上网查询,算是有点点的经验吧。

        1.驱动的编写

         驱动的编写通常是模式化的,这里首先就用最简单的一个设备驱动来说明一下。

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

static int __init hello_init_module(void)
{
 printk(KERN_EMERG " This is a simple driver-module!\r\n");
 return 0;
}
static void __exit hello_cleanup_module(void)
{
 printk(KERN_EMERG " Goodbye driver-module!\r\n");
}

module_init(hello_init_module);
module_exit(hello_cleanup_module);     

这个其实很好理解,就是要记住程序的模式。这里要注意的是printk的一些参数使用。

2.makefile的编写

    这个是整个驱动编译的核心,这里也是一个模式。

obj-m := hello.o 
KERNELDIR := /lib/modules/2.6.20/build 
PWD := $(shell pwd) 
modules: 
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules 
modules_install: 
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install 

 3.编译完了呢就烧写进开发板,把hello.ko文件烧写进开发板,然后进行加载。insmod hello.ko     然后用lsmod进行查看        如果有显示的话就说明你成功了。而且成功注册设备的时候会打印一句话,上面程序有写。   rmmod hello 就可以删除了,很简单的。

4.上面是非常简单的设备驱动,这里讲讲稍微复杂一点的设备驱动。

驱动结构

/*设备驱动模块加载函数*/
static int memdev_init(void)
{
  int result;
  int i;

  dev_t devno = MKDEV(mem_major, 0);//设置主设备号和次设备号,构造成设备号

  /* 静态申请设备号*/
  if (mem_major)
    result = register_chrdev_region(devno, 2, "memdev");//注册两个名字叫memdev的设备,设备号是devno
  else  /* 动态分配设备号 */
  {
    result = alloc_chrdev_region(&devno, 0, 2, "memdev");
    mem_major = MAJOR(devno);
  }  
  
  if (result < 0)
    return result;

  /*初始化cdev结构*/
  cdev_init(&cdev, &mem_fops);
  cdev.owner = THIS_MODULE;
  cdev.ops = &mem_fops;//文件操作结构体
  
  /* 注册字符设备 */
  cdev_add(&cdev, MKDEV(mem_major, 0), MEMDEV_NR_DEVS);//MEMDEV_NR_DEVS设备的数目
   
  /* 为设备描述结构分配内存*/
  mem_devp = kmalloc(MEMDEV_NR_DEVS * sizeof(struct mem_dev), GFP_KERNEL);
  if (!mem_devp)    /*申请失败*/
  {
    result =  - ENOMEM;
    goto fail_malloc;
  }
  memset(mem_devp, 0, sizeof(struct mem_dev));//清零
  
  /*为设备分配内存*/
  for (i=0; i < MEMDEV_NR_DEVS; i++) 
  {
        mem_devp[i].size = MEMDEV_SIZE;
        mem_devp[i].data = kmalloc(MEMDEV_SIZE, GFP_KERNEL);
        memset(mem_devp[i].data, 0, MEMDEV_SIZE);//清零
  }
    
  return 0;

  fail_malloc: 
  unregister_chrdev_region(devno, 1);
  
  return result;
}

具体呢看一些例程都会有的。这里讲讲编译完成后,传到开发板上,insmod后怎么去使用应用程序调用。

应用程序呢其实主要就是调用struct file结构体的一些成员函数

/*文件操作结构体*/
static const struct file_operations mem_fops =
{
  .owner = THIS_MODULE,
  .llseek = mem_llseek,
  .read = mem_read,
  .write = mem_write,
  .open = mem_open,
  .release = mem_release,
};

打开设备文件fopen    fp = open("/dev/memdev0",O_RDWR);

读设备文件  fread   fread(Buf, sizeof(Buf), 1, fp);

fwrite(Buf, sizeof(Buf), 1, fp);    fseek(fp,0,SEEK_SET);

然后呢,要编译应用程序,使用arm-linux-gcc -static memdev.c -o memdev    要用静态链接的根文件系统。编译好了后之后就把.ko文件传到开发板上。cat /proc/devices 里查看内核里面的注册好的一些设备,程序里如果是动态分配设备号的话,就要千万不能与其他设备号冲突,所以最好就是设置得大一点。之后呢就要建立设备文件节点,其实就是在/dev 文件夹里建立一个设备文件,mknod /dev/memdev0 c 251 0 .这里分别就是设备的名字,这个跟你的驱动里面注册的名字要一致,c是字符设备文件,251是主设备号,0是次设备号。搞定后呢就可以运行你的应用程序,这样就完成了你的驱动的写入了。

5.删除设备节点  rmmod  然后重启就没有了。或者可以进入/dev 文件夹,rm 你要删除的节点。

 

附上建立内核源码树的步骤

ubuntu 10.04建立源码树,实现最简单的驱动模块


1.安装编译内核所需要的软件
build-essential、autoconf、automake、cvs、subversion
apt-get install build-essential kernel-package libncurses5-dev 
libncurses5这个软件包在使用menuconfig配置内核的时候会用到。
2.下载内核源码
使用uname -r 命令查看当前的内核版本号,我的是2.6.32-25-generic,使用apt-cache search linux-source查看软件库的源码包,我查询到的源码包有:
linux-source - Linux kernel source with Ubuntu patches
linux-source-2.6.32 - Linux kernel source for version 2.6.32 with Ubuntu patches
我选择linux-source-2.6.32 - Linux kernel source for version 2.6.32 with Ubuntu patches
sudo apt-get install linux-source-2.6.32
下载好后cd /usr/src 目录下就可以看见linux-source-2.6.32.tar.bz2,然后解压到当前的目录
sudo tar xjvf linux-source-2.6.32.tar.bz2
解压完毕,会生成linux-source-2.6.32目录
3.编译内核源码
在编译之前我们需要 ubuntu原来内核的一个配置文件
这是我/usr/src目录下的文件预览:
drwxr-xr-x  4 root root     4096 2010-09-04 21:31 fglrx-8.723.1
drwxr-xr-x 24 root root     4096 2010-09-04 20:35 linux-headers-2.6.32-25
drwxr-xr-x  7 root root     4096 2010-09-04 20:35 linux-headers-2.6.32-25-generic
drwxr-xr-x 25 root root     4096 2010-09-16 21:39 linux-source-2.6.32
-rw-r--r--  1 root root 65846876 2010-09-01 22:41 linux-source-2.6.32.tar.bz2
现在我们需要linux-headers-2.6.32-25-generic目录下的.config文件,我们把它拷贝到我们刚下好解压的目录,也就是linux-source-2.6.32
sudo cp /usr/src/linux-headers-2.6.32-25-generic/.config /usr/src/linux-2.6.32
接下来切换到root用户
sudo -i
cd /usr/src/linux-2.6.32
make menuconfig 
终端会弹出一个配置界面 
最后有两项:load a kernel configuration... 
save a kernel configuration... 
选择load a kernel configuration保存,然后在选择save akernel configuration再保存退出,并退出配置环境。
接下来我们开始编译
cd /usr/src/linux-2.6.32
make
记住一定要是管理员帐号运行,这个过程真的很久,如果你的cpu是双核的可以在make后面加个参数,make -j4.
make bzImage 执行结束后,可以看到在当前目录下生成了一个新的文件: vmlinux, 其属性为-rwxr-xr-x。 
make modules /* 编译 模块 */ 
make modules_install  这条命令能在/lib/modules目录下产生一个目录.
make install 
mkinitramfs 2.6.37 -o /boot/initrd.img-2.6.37-generic
update-grub 或者update-grub2 这条命令能建立新内核的启动项,重启后可以看到多出来一个选项。
4.测试驱动模块
这里就抄个程序测试了,初学者顾不了那么多了。
我在 /home/shana/linux_q/ 目录下创建2个文本文件 hello.c Makefile 
//hello.c 
#include <linux/init.h> 
#include <linux/module.h> 
MODULE_LICENSE("Dual BSD/GPL"); 
static int hello_init(void) 

printk(KERN_ALERT "Hello, world\n"); 
return 0; 

static void hello_exit(void) 

printk(KERN_ALERT"Goodbye, cruel world\n"); 

module_init(hello_init); 
module_exit(hello_exit); 
Makefile 文件 
obj-m := hello.o 
KERNELDIR := /lib/modules/2.6.20/build 
PWD := $(shell pwd) 
modules: 
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules 
modules_install: 
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install 
需要注意的是makefile的格式$(MAKE)前面要加个tab.
make 编译,产生如下文件:
hello.c   hello.mod.c  hello.o   modules.order
hello.ko  hello.mod.o  Makefile  Module.symvers
5.加载模块到内核去
sudo insmod ./hello.ko 这个命令把hello.ko加载到内核
sudo rmmod hello 这个命令是把hello这个模块移除掉
lsmod 这个命令可以查看当前所有的驱动模块
程序的输出结果可以在
/var/log/syslog文件中查看
Sep 16 21:50:10 wuyangyu-desktop kernel: [10428.551271] Hello,World
Sep 16 21:55:45 wuyangyu-desktop kernel: [10763.644605] Goodbye,cruel world
这是程序的输出。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值