首页 > 系统相关 >掌握Linux内核技能:创建你的第一个字符设备驱动

掌握Linux内核技能:创建你的第一个字符设备驱动

时间:2024-04-07 13:58:32浏览次数:28  
标签:字符 struct dev char 内核 file Linux 驱动 设备

Linux字符设备驱动简介

在Linux系统中,设备驱动是操作系统的重要组成部分,负责在用户程序和硬件设备之间提供接口。设备驱动可以分为字符设备驱动和块设备驱动,本文将重点介绍字符设备驱动的基本概念和实现方法。

1. 字符设备驱动概念

字符设备是指那些以字符为单位进行数据传输的设备,例如键盘、串口和其它一些类似的设备。与之相对的是块设备(如硬盘驱动器),它以数据块为单位进行传输。

2. 字符设备文件和设备号

在Linux中,所有的设备都被抽象为文件,通过对文件的读写来操作设备。字符设备文件通常位于/dev目录下。每个设备文件都关联到一个唯一的设备号,设备号包括主设备号和次设备号。主设备号用于标识设备类型,次设备号用于标识同类设备中的不同设备。

3. 注册字符设备驱动

要在Linux内核中实现字符设备驱动,首先需要注册设备。以下是常用的注册和注销设备的函数:

  • int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);
  • void unregister_chrdev(unsigned int major, const char *name);

register_chrdev用于注册设备,它需要设备号、设备名称和一个指向file_operations结构体的指针。file_operations结构体包含了指向设备操作函数的指针。

4. file_operations结构体

file_operations是设备操作函数的集合,示例如下:

struct file_operations {
    struct module *owner;
    loff_t (*llseek) (struct file *, loff_t, int);
    ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
    ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
    int (*open) (struct inode *, struct file *);
    int (*release) (struct inode *, struct file *);
    // 更多操作...
};

要深入理解字符设备驱动的工作原理及其在Linux内核中的实现,我们需要通过一个具体的示例来展开详细讨论。在这个示例中,我们会创建一个简单的字符设备驱动并展示如何加载驱动模块、创建设备文件,并通过常规文件操作函数如open, read, write, 和 close 来测试设备驱动的功能。

5. 创建字符设备驱动

首先,我们需要编写字符设备驱动的代码。以下是一个简单的示例,该驱动仅实现了open, read, write, 和 close 方法:

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

#define DEVICE_NAME "simple_char_dev"
#define CLASS_NAME "simple_char"

static int majorNumber;
static struct class* charClass = NULL;
static struct cdev charCdev;

// Device open function
static int dev_open(struct inode *inodep, struct file *filep){
   printk(KERN_INFO "SimpleChar: Device has been opened\n");
   return 0;
}

// Device read function
static ssize_t dev_read(struct file *filep, char *buffer, size_t len, loff_t *offset){
   printk(KERN_INFO "SimpleChar: Device read\n");
   return 0; // Bytes read
}

// Device write function
static ssize_t dev_write(struct file *filep, const char *buffer, size_t len, loff_t *offset){
   printk(KERN_INFO "SimpleChar: Received %zu characters from the user\n", len);
   return len;
}

// Device close function
static int dev_close(struct inode *inodep, struct file *filep){
   printk(KERN_INFO "SimpleChar: Device successfully closed\n");
   return 0;
}

static struct file_operations fops =
{
   .open = dev_open,
   .read = dev_read,
   .write = dev_write,
   .release = dev_close,
};

static int __init chardev_init(void){
   printk(KERN_INFO "SimpleChar: Initializing the SimpleChar\n");

   // Dynamically allocate a major number
   majorNumber = register_chrdev(0, DEVICE_NAME, &fops);
   if (majorNumber<0){
      printk(KERN_ALERT "SimpleChar failed to register a major number\n");
      return majorNumber;
   }
   printk(KERN_INFO "SimpleChar: registered correctly with major number %d\n", majorNumber);

   // Register the device class
   charClass = class_create(THIS_MODULE, CLASS_NAME);
   if (IS_ERR(charClass)){
      unregister_chrdev(majorNumber, DEVICE_NAME);
      printk(KERN_ALERT "Failed to register device class\n");
      return PTR_ERR(charClass);
   }
   printk(KERN_INFO "SimpleChar: device class registered correctly\n");

   // Register the device driver
   if (IS_ERR(device_create(charClass, NULL, MKDEV(majorNumber, 0), NULL, DEVICE_NAME))){
      class_destroy(charClass);
      unregister_chrdev(majorNumber, DEVICE_NAME);
      printk(KERN_ALERT "Failed to create the device\n");
      return -1;
   }
   printk(KERN_INFO "SimpleChar: device class created correctly\n");
   return 0;
}

static void __exit chardev_exit(void){
   device_destroy(charClass, MKDEV(majorNumber, 0));
   class_unregister(charClass);
   class_destroy(charClass);
   unregister_chrdev(majorNumber, DEVICE_NAME);
   printk(KERN_INFO "SimpleChar: Goodbye from the LKM!\n");
}

module_init(chardev_init);
module_exit(chardev_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple Linux char driver");
MODULE_VERSION("0.1");

6. 加载驱动和创建设备文件

编译并加载上述模块到内核后,内核会为该设备分配一个主设备号。可以通过dmesg命令查看分配的主设备号。一旦获得该设备号,我们就可以使用mknod命令创建设备文件:

sudo mknod /dev/simple_char_dev c [majorNumber] 0

请将[majorNumber]替换为实际分配给你的设备的主设备号。

7. 测试设备驱动功能

现在,你可以通过常规文件操作函数来测试设备驱动的功能了。例如,使用以下命令:

  • 打开设备文件:sudo cat /dev/simple_char_dev
  • 写入设备文件:echo "Hello, device!" | sudo tee /dev/simple_char_dev
  • 读取设备文件:sudo cat /dev/simple_char_dev
  • 关闭设备文件:关闭文件操作是隐式执行的,由系统在cat命令完成后自动处理。

通过检查dmesg命令的输出,你可以看到设备驱动在每次文件操作时打印的消息,这证明了驱动的open, read, write, 和 close 方法被成功调用。

8. 结语

本文提供了一个关于如何开发、加载和测试Linux字符设备驱动的简单示例。虽然这个示例相对基础,但它为深入理解字符设备驱动的开发过程奠定了良好的基础。

标签:字符,struct,dev,char,内核,file,Linux,驱动,设备
From: https://blog.csdn.net/weixin_37787043/article/details/137449623

相关文章

  • 2024年最新,Linux平台 CentOS8安装mysql流程,以及可能遇到的问题
    0.删除mysql如果下载过mysql,请先删除mysql,不确定的也可以先查询一下查询命令: rpm-qa|grepmysqlrpm-qa|grepmariadb删除查询到的这些文件rpm-e--nodeps//查询到的软件名称例如:查询到的mysql相关文件删除mysql相关文件,并查询***别忘记去解压目录下......
  • 手把手教你构建嵌入式Linux根文件系统
    /bin:此目录下存放着系统需要的可执行文件,一般都是一些命令,比如ls、mv等命令/dev:device的缩写,此目录下的文件都是和设备有关的。在Linux下一切皆文件,即使是硬件设备,也是以文件的形式存在的,比如/dev/ttymxc0就表示串口0/etc:此目录下存放着各种配置文件/lib:library的简称,也就是......
  • 两个字符串间的最短路径问题【华为OD机试】(JAVA&Python&C++&JS题解)
    一.题目-两个字符串间的最短路径问题给定两个字符串,分别为字符串A与字符串B。例如A字符串为ABCABBA,B字符串为CBABAC可以得到下图m*n的二维数组,定义原点为(0,0),终点为(m,n),水平与垂直的每一条边距离为1,映射成坐标系如下图。从原点(0,0)到(0,A)为水平边,距离为1,从(0,A)......
  • Linux云计算架构:颠覆性创新还是过度炒作?
    Linux云计算架构:颠覆性创新还是过度炒作?一、Linux云计算架构的崛起与影响Linux云计算架构作为当前IT领域的热门话题,其崛起速度之快令人瞩目。随着企业对云计算需求的不断增长,Linux凭借其开源、灵活和安全的特性,在云计算市场中占据了重要地位。Linux云计算架构的广泛应用,不仅......
  • 【字符串】Manacher
    Manacher算法的本质是计算以字符串中的“每个字符”和“每两个相邻字符之间的空隙”作为对称中心的最大回文串的长度。所以利用这个性质可以解决一系列与子串是否是回文串、子串有多少是回文串的问题。namespaceManacher{constintMAXN=1.1e7;intn;chars[MAXN+10];......
  • Linux简单安装和命令
    1.在VMwareWorkstation安装2.简单设置命令root账户设置切换用户su<用户名>#切换后所在目录不变su-<用户名>#切换后所在目录变成用户家目录永久设置主机名hostnamectlset-hostname<主机名>查看版本内核版本uname-r发行版本cat/etc/os......
  • 在Linux中,内核模块是什么以及如何加载和卸载它们?
    在Linux中,内核模块是内核的一部分,但它们不是编译进内核的代码。这些模块可以在运行时动态地加载到内核中,或者从内核中卸载,从而扩展或修改内核的功能而无需重启系统。内核模块的使用提供了灵活性和易于维护的特点。1.内核模块的作用扩展内核功能:内核模块允许在不修改内核源代码......
  • 在Linux中,RAID级别和它们的用途是什么?
    RAID(RedundantArrayofIndependentDisks,独立磁盘冗余阵列)是一种将多个磁盘组织在一起,通过不同的数据分布和冗余策略,以提高数据可靠性、可用性和/或性能的技术。以下是一些常见的RAID级别及其用途:RAID0(条带化):数据分布:数据块均匀分布在多个磁盘上,实现数据的并行读写,提高......
  • 在Linux中,什么是系统监控和性能分析工具?举例说明。
    在Linux中,系统监控和性能分析工具是用来跟踪、评估和优化系统运行状况的软件。这些工具可以帮助系统管理员了解系统的资源使用情况,识别性能瓶颈,并采取相应的措施来提高系统的效率和稳定性。1.系统监控工具系统监控工具主要用于实时跟踪系统的运行状态,包括CPU使用率、内存使用、......
  • 在Linux中,什么是设备驱动程序?如何安装和卸载设备驱动程序?
    在Linux中,设备驱动程序(DeviceDriver)是一种特殊的软件模块,它的主要职责是作为操作系统和硬件设备之间的中介,负责管理和控制硬件设备,使其能够与操作系统进行有效的通信和交互。设备驱动程序允许操作系统及其他上层软件通过统一且标准化的接口访问和控制硬件设备,如打印机、显卡、声......