• Linux内核开发——自定义字符设备


    1. 前言

    Linux内核的驱动入门比较简单,只需要注册module_init和module_exit两个函数即可以完成最简单的驱动编译。然后就可以编译,接着将驱动文件载入系统就自动执行。

    2. 普通驱动

    2.1. 驱动代码

    新建hello.c文件,内容如下:

    #include<linux/kernel.h>
    #include<linux/init.h>
    #include<linux/module.h>
    
    MODULE_LICENSE("Dual BSD/GPL");
    MODULE_AUTHOR("Mike");
    
    static int __init hello_init(void)
    {
            printk("Hello Mike!\n")  ;
            return 0;
    }
    
    static void __exit hello_exit(void)
    {
            printk("Exit Mike!\n");
    }
    
    module_init(hello_init);
    module_exit(hello_exit);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    2.2. 编译

    内核编译方法:

    $(MAKE) -C ( K D I R ) M = (KDIR) M= (KDIR)M=(PWD) modules

    参照内核编译方法编写一个Makefile:

    # obj-m表示后面的内容编译为ko文件,如果有多个文件按空格分隔开添加
    obj-m:=hello.o
    
    PWD:=$(shell pwd)
    KDIR=/lib/modules/$(shell uname -r)/build
    
    # 下面利用makefile的隐式规则编译hello.o的同名hello.c文件,生链接生成ko文件
    all :
            make -C $(KDIR) M=$(PWD) modules
    clean :
            make -C $(KDIR) M=$(PWD) clean
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    2.3. 编译载入

    sudo make
    sudo insmod helloKo.ko
    sudo rmmod helloKo
    
    • 1
    • 2
    • 3

    通过dmesg可以看到驱动的载入及退出。
    在这里插入图片描述

    3. 字符设备

    3.1. 字符设备驱动

    字符设备主要通过register_chrdev来注册,并实现file_operations中的几个基本的接口即可。

    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/fs.h>
    #include <linux/init.h>
    #include <linux/delay.h>
    
    #define    MAJOR_NUM    250
    #define    DEVICE_NAME  "hello"
    
    int DriverOpen(struct inode *pslINode, struct file *pslFileStruct)
    {
        printk( "hello open.\n" );
        return(0);
    }
    
    
    ssize_t DriverWrite( struct file *pslFileStruct, const char __user *pBuffer, size_t nCount, loff_t *pOffset )
    {
        printk( "hello write.\n" );
        return(0);
    }
    
    
    long DriverIOControl( struct file *pslFileStruct, unsigned int uiCmd, unsigned long ulArg )
    {
        printk(" hello ioctl.\n" );
        return(0);
    }
    
    
    struct file_operations hello_flops = {
        .owner      = THIS_MODULE,
        .open       = DriverOpen,
        .write      = DriverWrite,
        .unlocked_ioctl = DriverIOControl
    };
    
    static int __init hello_init( void )
    {
        int ret;
    
        ret = register_chrdev( MAJOR_NUM, DEVICE_NAME, &hello_flops );
        if ( ret < 0 )
        {
            printk(" can't register major number.\n" );
            return(ret);
        }
        printk( " initialized.\n" );
        return(0);
    }
    
    
    static void __exit hello_exit( void )
    {
        printk("exit .\n" );
        unregister_chrdev(MAJOR_NUM, DEVICE_NAME);
    }
    
    module_init( hello_init );
    module_exit( hello_exit );
    MODULE_LICENSE( "GPL" );
    MODULE_AUTHOR( "Mike" );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    3.2. 编译安装

    sudo make
    sudo insmod helloKo.ko
    
    • 1
    • 2

    使用lsmod命令查看安装的模块。
    在这里插入图片描述

    3.3. 生成设备文件

    如果想访问某设备,则必须有对应的设备文件。驱动虽然已经载入到系统中,但是并没有对应的设备文件,这样应用程序就无法访问该驱动。Linux内核提供了mknod工具用来为驱动创建相应的设备文件。
    mknod语法:

    mknod DEVNAME {b | c} MAJOR MINOR

    DEVNAME 为设备名,b|c则代码块设备或字符设备,后面则为主次版本号。
    为驱动创建设备文件:

    mknod /dev/hello c 250 0
    
    • 1

    执行上面的命令之后,就可以在dev目录看到hello文件,我们就可以正式访问这个字符设备了。在这里插入图片描述

    3.4. 测试

    3.4.1. 代码

    测试代码是用户态代码,主要是调用open打开驱动,并调用ioctl向驱动发起请求。编译main.cpp代码,如下:

    #include <fcntl.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <iostream>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <sys/ioctl.h>
    #include <stropts.h>
    #include <string.h>
    using namespace std;
    
    int main( void )
    {
        int fd;
        if ( (fd = open( "/dev/hello", O_RDWR ) ) < 0 )
        {
            cerr << strerror( errno ) << endl;
            return(-1);
        }
    
        ioctl(fd, 1, 0);
        close(fd);
    
        return(0);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    3.4.2. 编译运行

    g++ main.cpp -o main
    sudo ./main
    
    • 1
    • 2

    查看dmesg结果:
    在这里插入图片描述

  • 相关阅读:
    pysot-master-train.py 运行记录
    基于安卓android微信小程序音乐播放器
    【夜读】丰富自己的4个习惯,请逼自己养成
    c++ cin 简单用法
    神经网络应用场景——图像识别
    LeetCode算法心得——生成特殊数字的最少操作(贪心找规律)
    Redis——字典
    防火墙ensp实验
    数据结构——图的应用
    android kotlin学习
  • 原文地址:https://blog.csdn.net/feihe027/article/details/125541886