Linux内核学习:动手写一个字符驱动模块

目录

1 字符驱动设备

2 编写驱动并测试

2.1 实验目的及操作步骤

2.2 简单的字节驱动模块

2.3 编译字节驱动模块

2.4 插入到QEMU上的Linux内核中

2.5 写用户层测试代码测试插入的内核模块


1 字符驱动设备

字符设备是以字节为单位的I/O传输,这种字符流的传输率通常比较低,常见的字符设备有鼠标、键盘、触摸屏等。

2 编写驱动并测试

可以先不去管代码具体的内容,后面会进行分析,最先要做的就是搞懂实验目的是什么,怎么去做。要注意的问题是,现在的实验环境是在用QEMU来模拟ARM开发环境,搭载 Linux4.0 的内核系统,那么编译驱动程序的时候需要在同样的Linux4.0 ARM开发环境下编译,才能成功地插入内核模块,因为编译内核模块的Linux版本与架构必须和植入内核模块的Linux版本与架构高度一致。

关于搭建所说的QEMU+ARM+Linux4.0的编译环境请查看第一篇Linux内核学习文章:https://blog.csdn.net/qq_32473685/article/details/103362844

文章在Linux内核版本上有所出入,但只要换成对应版本就好,操作无差别。

2.1 实验目的及操作步骤

实验目的:

1)编写一个简单的字符驱动设备,实现open,read,write方法。

2)能从用户空间测试编写的字符驱动设备,调用read( )函数查看。

操作步骤:

1)编写字符模块并编译

2)将字符模块放入到QEMU搭载的Linux4.0内核中并插入

3)编写用户层测试程序并编译

4)将测试程序放入到QEMU搭载的Linux4.0内核中并测试

2.2 简单的字节驱动模块

字符驱动代码如下:

#include <linux/module.h>                                                     
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/init.h>
#include <linux/cdev.h>

#define DEMO_NAME "my_demo_dev"
static dev_t dev;
static struct cdev *demo_cdev;
static signed count=1;

static int demodrv_open(struct inode *inode,struct file *file)
{
    int major  =MAJOR(inode->i_rdev);
    int minor =MINOR(inode->i_rdev);
    printk("%s: major=%d,minor=%d\n",__func__,major,minor);
    return 0;
}

static int demodrv_release(struct inode *inode,struct file *file)
{
    return 0;
}

static ssize_t demodrv_read(struct file *file,char __user *buf,size_t lbuf,loff_t *ppos)
{

    printk("%s enter\n",__func__);
    return 0;

}

static ssize_t  demodrv_write(struct file *file,const char __user *buf ,size_t count,loff_t *f_pos)
{
    printk("%s enter\n",__func__);
    return 0;
}

static const struct file_operations demodrv_fops={
    .owner=THIS_MODULE,
    .open=demodrv_open,
    .release=demodrv_release,
    .read=demodrv_read,
    .write=demodrv_write
};

static int __init simple_char_init(void)
{
    int ret;
    ret=alloc_chrdev_region(&dev,0,count,DEMO_NAME);
    if(ret){
        printk("failed to allocate char device region");
        return ret;
    }

    demo_cdev=cdev_alloc();
    if(!demo_cdev){
        printk("cdev_alloc failed\n");
        goto unregister_chrdev;
    }

    cdev_init(demo_cdev,&demodrv_fops);
                         ret=cdev_add(demo_cdev,dev,count);
    if(ret){
        printk("cdev_add failed\n");
        goto cdev_fail;
    }

    printk("successed register char device : %s\n",DEMO_NAME);
    printk("Major numbner =%d,minor number =%d\n",MAJOR(dev),MINOR(dev));
    return 0;

cdev_fail:
    cdev_del(demo_cdev);
unregister_chrdev:
    unregister_chrdev_region(dev,count);

    return ret;


}

static void __exit simple_char_exit(void)
{
    printk("removing device\n");
    if(demo_cdev)
        cdev_del(demo_cdev);

    unregister_chrdev_region(dev,count);
}

module_init(simple_char_init);
module_exit(simple_char_exit);

MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Henry Fordham");
MODULE_DESCRIPTION("simple chararcter device");
MODULE_ALIAS("Henry"); 

2.3 编译字节驱动模块

这里要注意,我们需要在QEMU+ARM的环境下插入内核模块,那么就要在QEMU+ARM的环境下编译模块,为了节省时间,可以先下载一个基础环境,如下:

https://github.com/figozhang/runninglinuxkernel_4.0

直接git clone这个环境下来,下载按照README安装,安装完后,编写刚才内核模块的Makefile,注意第一行BASEINCLUDE刚才下载下来的runninglinuxkernel_4.0,Makefile如下。

BASEINCLUDE ?= ~/tools/runninglinuxkernel_4.0                                 

mytest-objs  :=my_demodev.o
obj-m  :=mydemo.o

all :
    $(MAKE) -C      $(BASEINCLUDE)  M=$(PWD)        modules;
clean:
    $(MAKE)  -C $(BASEINCLUDE)  SUBDIRS=$(PWD) clean;
    rm -f *.ko;

写完之后,make,可以看到my_demo.ko

2.4 插入到QEMU上的Linux内核中

首先,还是挂载一块ext4磁盘到Linux内核中,通过QEMU启动:

​qemu-system-arm -M vexpress-a9 -smp 4 \
 -m 1024M \
 -kernel arch/arm/boot/zImage \
 -append "rdinit=/linuxrc console=ttyAMA0 loglevel=8" \
 -dtb arch/arm/boot/dts/vexpress-v2p-ca9.dtb  \
 -nographic \
 -sd ext4.img

如果不知道怎么创建一个ext4的磁盘,还是参考第一篇文章。

挂载我们的ext4磁盘到QEMU的mnt:

mount -t ext4 /dev/mmcblk0 /mnt/

同时在主机上将ext4挂载到一个文件目录,使之同步。具体参考第一篇文章。

QEMU中mnt下将mydemo.ko插入进去:

/mnt # ls
lost+found  mydemo.ko
/mnt # insmod mydemo.ko
successed register char device : my_demo_dev
Major numbner =252,minor number =0

2.5 写用户层测试代码测试插入的内核模块

如下是用户级代码: test.c

#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#define DEMO_DEV_NAME "/dev/demo_drv"

int main(){
    char buffer[64];
    int fd;
    fd = open(DEMO_DEV_NAME, O_RDONLY);
    if (fd < 0) {
        printf("open device %s failed\n", DEMO_DEV_NAME);
        return -1;
    }
    read(fd, buffer, 64);
    close(fd);
    return 0;
}

编译:

arm-linux-gnueabi-gcc test.c -o test --static

得到一个可执行文件test,将这个文件还是同步到qemu的内核文件系统ext4.img中,然后执行:

/ # ls mnt/
hello.ko    lost+found  mydemo.ko   test
/ # cd mnt/
/mnt # insmod mydemo.ko 
successed register char device : my_demo_dev
Major numbner =252,minor number =0
/mnt # mknod /dev/demo_drv c 252 0
/mnt # ls -l
total 646
-rw-r--r--    1 0        0            55940 Dec  4 23:37 hello.ko
drwx------    2 0        0            12288 Dec  4 14:24 lost+found
-rw-r--r--    1 0        0            88788 Dec  4 16:24 mydemo.ko
-rwxr-xr-x    1 0        0           503004 Dec 18 14:51 test
/mnt # ./test 
demodrv_open: major=252,minor=0
demodrv_read enter

可以看到,从用户层代码读取到了内核模块的信息。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值