内核实验(五):传统简单字符设备驱动

简介: 本文通过一个简单的字符设备驱动程序实验,演示了如何在Linux内核中编写、编译和测试驱动代码,并使用Qemu虚拟机和NFS环境进行部署和验证,同时检验了NFS环境对于提高开发效率的作用。

一、篇头

目标是把内核相关部分的实验都再做一次,虽然有现成源码,但从以往学习经历看,自己还是得多少做一些改动,对于要自虐的,可以从头打一遍,这样效果会好很多,练习完后,做个总结。实验简单,花的时间也不多,温故而知新。

二、源码

2.1 驱动关键部分

static int __init test_3_init(void)
{
   
   
    int ret;

        pr_info("test_3_init\n");

    ret = alloc_chrdev_region(&dev, 0, count, DEMO_NAME);
    if (ret) {
   
   
        pr_err("failed to allocate char device region");
        return ret;
    }

    test_3_cdev = cdev_alloc();
    if (!test_3_cdev) {
   
   
        pr_err("cdev_alloc failed\n");
        goto unregister_chrdev;
    }

    cdev_init(test_3_cdev, &test_3_fops);

        /*
          (1) 下面语句会创建 /proc/devices/test_3_dev, 但不会创建/dev下的设备节点
        */
    ret = cdev_add(test_3_cdev, dev, count);
    if (ret) {
   
   
        pr_err("cdev_add failed\n");
        goto cdev_fail;
    }

    pr_err("succeeded register char device: %s\n", DEMO_NAME);
    pr_err("Major number = %d, minor number = %d\n",
            MAJOR(dev), MINOR(dev));
// …… 省略 …… 
    return ret;
}
AI 代码解读

2.2 APP:test_3_app.c

  • 完成的linux app源码
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

#define MY_DEV_NAME "/dev/test_3_dev"

int main()
{
   
   
    char buffer[64];
    int fd;

    fd = open(MY_DEV_NAME, O_RDONLY);
    if (fd < 0) {
   
   
        printf("open device %s failded\n", MY_DEV_NAME);
        return -1;
    }
    read(fd, buffer, 64);
    close(fd);
    return 0;
}
AI 代码解读

2.3 驱动完整源码

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

#define DEMO_NAME "test_3_dev"
static dev_t dev;
static struct cdev *test_3_cdev;
static signed count = 1;

static int test_3_open(struct inode *inode, struct file *file)
{
   
   
    int major = MAJOR(inode->i_rdev);
    int minor = MINOR(inode->i_rdev);

    pr_info("%s: major=%d, minor=%d\n", __func__, major, minor);
    return 0;
}

static int test_3_release(struct inode *inode, struct file *file)
{
   
   
    pr_info("%s \n", __func__);

    return 0;
}

static ssize_t
test_3_read(struct file *file, char __user *buf, size_t lbuf, loff_t *ppos)
{
   
   
    pr_info("%s \n", __func__);
    return 0;
}

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

}

static const struct file_operations test_3_fops = {
   
   
    .owner = THIS_MODULE,
    .open = test_3_open,
    .release = test_3_release,
    .read = test_3_read,
    .write = test_3_write
};


static int __init test_3_init(void)
{
   
   
    int ret;

        pr_info("test_3_init\n");

    ret = alloc_chrdev_region(&dev, 0, count, DEMO_NAME);
    if (ret) {
   
   
        pr_err("failed to allocate char device region");
        return ret;
    }

    test_3_cdev = cdev_alloc();
    if (!test_3_cdev) {
   
   
        pr_err("cdev_alloc failed\n");
        goto unregister_chrdev;
    }

    cdev_init(test_3_cdev, &test_3_fops);

    ret = cdev_add(test_3_cdev, dev, count);
    if (ret) {
   
   
        pr_err("cdev_add failed\n");
        goto cdev_fail;
    }

    pr_err("succeeded register char device: %s\n", DEMO_NAME);
    pr_err("Major number = %d, minor number = %d\n",
            MAJOR(dev), MINOR(dev));

    return 0;

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

    return ret;
}

static void __exit test_3_exit(void)
{
   
   
    pr_info("test_3_exit\n");

    if (test_3_cdev)
        cdev_del(test_3_cdev);

    unregister_chrdev_region(dev, count);
}

module_init(test_3_init);
module_exit(test_3_exit);

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("szhou <66176468@qq.com>");
MODULE_DESCRIPTION("test_1, simple char kernel module");
AI 代码解读

2.4 Makefile

  • 实验和之前几篇文章有集成性,本文实验只需要在 obj-m 后面添加 test_3.o 即可
KDIR := /home/szhou/works/qemu_linux/linux-stable


obj-m := test_1.o test_2.o test_3.o


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

clean:
    $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) clean
    rm -f *.ko
AI 代码解读

三、编译

3.1 编译ko

szhou@bc01:~/works/qemu_linux/linux-stable/my_kmodules$ ls
Makefile  modules.order  Module.symvers  test_1.c  test_1.ko  test_1.mod  test_1.mod.c  test_1.mod.o  test_1.o  test_2.c  test_2.ko  test_2.mod  test_2.mod.c  test_2.mod.o  test_2.o  test_3_app.c  test_3.c
szhou@bc01:~/works/qemu_linux/linux-stable/my_kmodules$ make
make -C /home/szhou/works/qemu_linux/linux-stable M=/home/szhou/works/qemu_linux/linux-stable/my_kmodules modules
make[1]: Entering directory '/home/szhou/works/qemu_linux/linux-stable'
  CC [M]  /home/szhou/works/qemu_linux/linux-stable/my_kmodules/test_3.o
  MODPOST /home/szhou/works/qemu_linux/linux-stable/my_kmodules/Module.symvers
  CC [M]  /home/szhou/works/qemu_linux/linux-stable/my_kmodules/test_1.mod.o
  LD [M]  /home/szhou/works/qemu_linux/linux-stable/my_kmodules/test_1.ko
  CC [M]  /home/szhou/works/qemu_linux/linux-stable/my_kmodules/test_2.mod.o
  LD [M]  /home/szhou/works/qemu_linux/linux-stable/my_kmodules/test_2.ko
  CC [M]  /home/szhou/works/qemu_linux/linux-stable/my_kmodules/test_3.mod.o
  LD [M]  /home/szhou/works/qemu_linux/linux-stable/my_kmodules/test_3.ko
make[1]: Leaving directory '/home/szhou/works/qemu_linux/linux-stable'
szhou@bc01:~/works/qemu_linux/linux-stable/my_kmodules$
AI 代码解读

3.2 编译app

  • 因为使用qemu模拟,而我们之前建立的mini Linux系统尚未添加 so 等各种依赖库,所以需要在编译app时候添加 --static标志,使用静态链接,这样就可以不依赖于so文件。
szhou@bc01:~/works/qemu_linux/linux-stable/my_kmodules$ arm-linux-gnueabi-gcc test_3_app.c -o test_3_app --static
szhou@bc01:~/works/qemu_linux/linux-stable/my_kmodules$ file test_3_app
test_3_app: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, BuildID[sha1]=1a479f0e5a7f3cba6fb0ea4121337f56623cb5a5, for GNU/Linux 3.2.0, not stripped
szhou@bc01:~/works/qemu_linux/linux-stable/my_kmodules$
AI 代码解读

四、测试

4.1 部署

  • 透过NFS部署,不了解的,请参考之前的内核实验(四)
  • 将测试文件复制到nfs共享目录下
szhou@bc01:~/works/qemu_linux/linux-stable/my_kmodules$ cp test_3.ko ~/works/nfs_share/
szhou@bc01:~/works/qemu_linux/linux-stable/my_kmodules$ cp test_3_app ~/works/nfs_share/
AI 代码解读

4.2 测试

4.2.1 启动qemu虚拟机
qemu-system-arm   -nographic  -M vexpress-a9 -m 1024M -kernel arch/arm/boot/zImage   -initrd ../busybox/rootfs.ext4.img.gz    -dtb arch/arm/boot/dts/vexpress-v2p-ca9.dtb
AI 代码解读
4.2.2 运行测试文件
----------------------------------------
Welcome to szhou's tiny Linux
----------------------------------------

Please press Enter to activate this console. 
~ # 
~ # 
~ #  mount -t nfs -o nolock 192.168.3.67:/home/szhou/works/nfs_share /mnt
~ # cd /mnt/
/mnt # ls
abc_client  bcd_server  test_3.ko
/mnt # insmod test_3.ko 
test_3: loading out-of-tree module taints kernel.
test_3_init
succeeded register char device: test_3_dev
Major number = 248, minor number = 0
/mnt # 
/mnt # mknod /dev/test_3_dev c 248 0
/mnt # ./test_3_app 
test_3_open: major=248, minor=0
test_3_read 
test_3_release 
/mnt # 
/mnt # rmmod  test_3.ko 
test_3_exit
/mnt #
AI 代码解读

在这里插入图片描述

4.2.3 查看设备情况
  • 注意,此处尚未使用mknod手动创建设备文件
(1) insmod ko
/mnt # insmod test_3.ko 
test_3: loading out-of-tree module taints kernel.
test_3_init
succeeded register char device: test_3_dev
Major number = 248, minor number = 0


(2)查看 /dev 可见无目标设备


(3)查看 /proc/devices ,则创建了test_3_dev
/dev # cat /proc/devices 
Character devices:
  1 mem
  2 pty
  3 ttyp
  4 /dev/vc/0
  4 tty
  5 /dev/tty
  5 /dev/console
// …… 省略 ……
248 test_3_dev
// …… 省略 ……

Block devices:
// …… 省略 ……
/dev #
AI 代码解读

五、篇尾

略……
用这个简单的实验, 再次检验了内核实验四,NFS环境的高效。

目录
打赏
0
0
0
0
14
分享
相关文章
|
11月前
|
内核实验(六):使用misc框架,实现简单字符设备驱动
本文介绍了如何使用Linux的misc框架来实现一个简单的字符设备驱动程序,包括编写驱动源码、编译、部署以及在Qemu虚拟机中测试驱动程序,展示了如何动态分配次设备号并手动创建设备文件以进行测试。
193 0
内核实验(六):使用misc框架,实现简单字符设备驱动
开源项目推荐:C++ Web/Http Server/Rest开发框架(请重点关注Oat++和搜狗workflow)
开源项目推荐:C++ Web/Http Server/Rest开发框架(请重点关注Oat++和搜狗workflow)
4712 0
内核实验(三):编写简单Linux内核模块,使用Qemu加载ko做测试
本文介绍了如何在QEMU中挂载虚拟分区、创建和编译简单的Linux内核模块,并在QEMU虚拟机中加载和测试这些内核模块,包括创建虚拟分区、编写内核模块代码、编译、部署以及在QEMU中的加载和测试过程。
432 0
内核实验(三):编写简单Linux内核模块,使用Qemu加载ko做测试
内核实验(七):使用内核KFIFO环形缓冲区机制
本文通过一个内核模块实验,演示了如何在Linux内核中使用KFIFO环形缓冲区机制,包括定义KFIFO、编写驱动程序以及在Qemu虚拟机中进行编译、部署和测试,展示了KFIFO在无需额外加锁的情况下如何安全地在读者和写者线程间进行数据传输。
555 0
内核实验(七):使用内核KFIFO环形缓冲区机制
内核实验(一):使用QEMU+GDB断点调试Linux内核代码
如何配置环境并使用QEMU虚拟机结合GDB进行Linux内核代码的断点调试,包括安装QEMU、交叉编译工具链,编译内核以及通过GDB远程连接进行调试的详细步骤。
653 0
内核实验(一):使用QEMU+GDB断点调试Linux内核代码
内核实验(二):自定义一个迷你Linux ARM系统,基于Kernel v5.15.102, Busybox,Qemu
本文介绍了如何基于Linux Kernel 5.15.102版本和BusyBox创建一个自定义的迷你Linux ARM系统,并使用QEMU进行启动和调试,包括内核和BusyBox的编译配置、根文件系统的制作以及运行QEMU时的命令和参数设置。
910 0
内核实验(二):自定义一个迷你Linux ARM系统,基于Kernel v5.15.102, Busybox,Qemu
Linux grep技巧 结合awk查询
结合 `grep` 和 `awk`,可以实现灵活、高效的文本处理和数据分析。`grep` 用于快速过滤符合条件的行,`awk` 用于进一步处理和提取数据。这种组合使用在日志分析、数据处理和系统监控等场景中尤为常见。掌握这两者的基本用法和组合技巧,可以大大提升在 Linux 环境下的工作效率。
164 7
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问