Linux 下 xxx.ko 内核模块制作的全过程

内核模块的制作

有时候在学习过程中,为了方便学习一些知识,经常需要事先写一个简单的驱动 driver,然后再写一个应用程序进行调试测试,那么问题来了,怎样先写一个驱动并且编译好?下面将给出一个案列,案列程序直接引用别人的(结尾会给出原文程序链接),当然也可以自己写 。案列程序主要做的事情就是 用户空间和驱动程序的内存映射 , 直接看代码就清楚了。

  • 驱动
zj@zj-virtual-machine:~/c_study/mmap_prj$ cat driver_demo1.c
#include <linux/miscdevice.h> 
#include <linux/delay.h> 
#include <linux/kernel.h> 
#include <linux/module.h> 
#include <linux/init.h> 
#include <linux/mm.h> 
#include <linux/fs.h> 
#include <linux/types.h> 
#include <linux/delay.h> 
#include <linux/moduleparam.h> 
#include <linux/slab.h> 
#include <linux/errno.h> 
#include <linux/ioctl.h> 
#include <linux/cdev.h> 
#include <linux/string.h> 
#include <linux/list.h> 
#include <linux/pci.h> 
#include <linux/gpio.h> 
 
 
#define DEVICE_NAME "mymap" 
 
 
static unsigned char array[10]={0,1,2,3,4,5,6,7,8,9}; 
static unsigned char *buffer; 
 
 
static int my_open(struct inode *inode, struct file *file) 
{ 
    return 0; 
} 
 
 
static int my_map(struct file *filp, struct vm_area_struct *vma) 
{ 
    unsigned long page; 
    unsigned char i; 
    unsigned long start = (unsigned long)vma->vm_start; 
    //unsigned long end = (unsigned long)vma->vm_end; 
    unsigned long size = (unsigned long)(vma->vm_end - vma->vm_start); 
 
    //得到物理地址 
    page = virt_to_phys(buffer); 
    //将用户空间的一个vma虚拟内存区映射到以page开始的一段连续物理页面上 
    if(remap_pfn_range(vma,start,page>>PAGE_SHIFT,size,PAGE_SHARED))//第三个参数是页帧号,由物理地址右移PAGE_SHIFT得到 
        return -1; 
 
    //往该内存写10字节数据 
    for(i=0;i<10;i++) 
        buffer[i] = array[i]; 
 
    return 0; 
} 
 
 
static struct file_operations dev_fops = { 
    .owner = THIS_MODULE, 
    .open = my_open, 
    .mmap = my_map, 
}; 
 
static struct miscdevice misc = { 
    .minor = MISC_DYNAMIC_MINOR, 
    .name = DEVICE_NAME, 
    .fops = &dev_fops, 
}; 
 
 
static int __init dev_init(void) 
{ 
    int ret; 
 
    //注册混杂设备 
    ret = misc_register(&misc); 
    //内存分配 
    buffer = (unsigned char *)kmalloc(PAGE_SIZE,GFP_KERNEL); 
    //将该段内存设置为保留 
    SetPageReserved(virt_to_page(buffer)); 
 
    return ret; 
} 
 
 
static void __exit dev_exit(void) 
{ 
    //注销设备 
    misc_deregister(&misc); 
    //清除保留 
    ClearPageReserved(virt_to_page(buffer)); 
    //释放内存 
    kfree(buffer); 
} 
 
 
module_init(dev_init); 
module_exit(dev_exit); 
MODULE_LICENSE("GPL"); 
MODULE_AUTHOR("LKN@SCUT");
  • 应用程序
zj@zj-virtual-machine:~/c_study/mmap_prj$ cat mmap_demo1.c
#include <unistd.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <fcntl.h> 
#include <linux/fb.h> 
#include <sys/mman.h> 
#include <sys/ioctl.h> 
 
#define PAGE_SIZE 4096 
 
 
int main(int argc , char *argv[]) 
{ 
    int fd; 
    int i; 
    unsigned char *p_map; 
 
    //打开设备 
    fd = open("/dev/mymap",O_RDWR); 
    if(fd < 0) 
    { 
        printf("open fail\n"); 
        exit(1); 
    } 
 
    //内存映射 
    p_map = (unsigned char *)mmap(0, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0); 
    if(p_map == MAP_FAILED) 
    { 
        printf("mmap fail\n"); 
        goto here; 
    } 
 
    //打印映射后的内存中的前10个字节内容 
    for(i=0;i<10;i++) 
        printf("%d\n",p_map[i]); 
 
 
here: 
    munmap(p_map, PAGE_SIZE); 
    return 0; 
}

模块的编译

涉及到 makefile 的知识本身很多,这里只给出最简单的编译方式,具体见如下代码

zj@zj-virtual-machine:~/c_study/mmap_prj$ cat Makefile 
obj-m:=driver_demo1.o
KDIR:=/lib/modules/$(shell uname -r)/build
PWD:=$(shell pwd)

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

clean:
	rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions *.order *symvers *Module.markers

对于这个 makefile 文件进行简单的解释一下 :
• obj-m := <模块名>.o
obj-m 是 kbuild makefile 中用来做目标定义的,obj-m 是指编译成模块,亦即生成 ko 文件。
obj-m:=driver_demo1.o —-> 这里面 driver_deno1.o 是需要变化的,通常而言和源代码的文件同名即可,比如 c 代码是 driver_demo1.c,所以这里就是 driver_demo1.o,编出来就是 driver_demo1.ko。
• <模块名>-objs := <目标文件>
但是有时候,ko 是由 kxxx.o file1.o file2.o这样多个.o文件编译成 driver_demo1.ko模块,那需要:
obj-m := driver_demo1.o
• KDIR
KDIR:=/lib/modules/$(shell uname -r)/build
这是我们正在运行的操作系统内核编译目录。也就是编译模块需要的环境。
uname -r可以查看当前系统使用的内核版本以及子版本号。
• M=
指定源文件的位置

简单检查

把驱动编译好了以后,我们需要简单 check 一下,check 之前要先装载编译好的模块,最后还要记得卸载我们的模块 。因为驱动程序会在设备去创建设备,所以这个也可以检查 。

zj@zj-virtual-machine:~/c_study/mmap_prj$ sudo insmod driver_demo1.ko
zj@zj-virtual-machine:~/c_study/mmap_prj$ ls -la   /dev | grep mym*
  1 root root     10,  53 12月 28 09:08 mymap  # create the device
  zj@zj-virtual-machine:/sys/module$ ls -la | grep driver_demo1
drwxr-xr-x   5 root root 0 12月 28 09:08 driver_demo1  # insmod the module
zj@zj-virtual-machine:/sys/module/driver_demo1/sections$ sudo cat .bss 
0xffffffffc06fb4c0  # it can help us to debug the driver in kernel space

run 一下应用程序

最后 run 一下应用程序 , 结果如下 , 表明这个过程还是比较成功的 。

zj@zj-virtual-machine:~/c_study/mmap_prj$ sudo ./mmap_demo1 
[sudo] password for zj: 
Sorry, try again.
[sudo] password for zj: 
0
1
2
3
4
5
6
7
8
9

参考

分析案例中程序的来源
http://blog.csdn.net/dlutbrucezhang/article/details/9967849

猜你喜欢

转载自blog.csdn.net/m0_37329910/article/details/85317472