【 声明:版权所有,欢迎转载,请勿用于商业用途。  联系信箱:feixiaoxing @163.com】

昨天我们说了一些简单模块编写方法,但是终归没有涉及到设备的编写内容,今天我们就可以了解一下相关方面的内容,并且用一个实例来说明在linux上面设备是如何编写的。虽然我不是专门做linux驱动的,却也经常收到一些朋友们的来信。在信件中,很多做驱动的朋友对自己的工作不是很满意,认为自己的工作就是把代码拷贝来拷贝去,或者说是改来改去,没有什么技术含量。有这种想法的朋友不在少数,我想这主要还是因为他们对自己的工作缺少了解导致。如果有可能,我们可以问问自己这样几个问题:

(1)我真的搞懂设备的开发驱动流程了吗?我是否可以从0开始,编写一个独立的驱动代码呢?

(2)我真的了解设备的初始化、关闭、运行的流程吗?

(3)当前的设备驱动流程是否合理,有没有可以改进的地方?

(4)对于内核开发中涉及的api调用,我自己是否真正了解、是否明白它们在使用上有什么区别?

(5)如果我要驱动的设备只是在一个前后台系统中运行,在没有框架帮助的情况下,我是否有信心把它启动和运行起来?

当然,上面的内容只是我个人的想法,也不一定都正确。但是,知其然,更要知其所以然,熟悉了当前开发流程的优缺点才能真正掌握和了解驱动开发的本质。这听上去有些玄乎,其实也很简单,就是要有一种刨根问底、不断改进的精神,这样才能做好自己的工作。因为我们是在pc linux上学习驱动的,因此暂时没有真实的外接设备可以使用,但是这丝毫不影响我们学习的热情。通过定时器、进程,我们可以仿真出真实设备的各种需求,所以对于系统来说,它是无所谓真设备、假设备的,基本的处理流程对它来说都是一样的。只要大家一步一步做下去,肯定可以了解linux驱动设备的开发工程的。

下面,为了说明问题,我们可以编写一段简单的char设备驱动代码,文件名为char.c,

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>static struct cdev chr_dev;
static dev_t ndev;static int chr_open(struct inode* nd, struct file* filp)
{int major ;int minor;major = MAJOR(nd->i_rdev);minor = MINOR(nd->i_rdev);printk("chr_open, major = %d, minor = %d\n", major, minor);return 0;
}static ssize_t chr_read(struct file* filp, char __user* u, size_t sz, loff_t* off)
{printk("chr_read process!\n");return 0;
}struct file_operations chr_ops = {.owner = THIS_MODULE,.open = chr_open,.read = chr_read
};static int demo_init(void)
{int ret;cdev_init(&chr_dev, &chr_ops);ret = alloc_chrdev_region(&ndev, 0, 1, "chr_dev");if(ret < 0 ){return ret;}printk("demo_init(): major = %d, minor = %d\n", MAJOR(ndev), MINOR(ndev));ret = cdev_add(&chr_dev, ndev, 1);if(ret < 0){return ret;}return 0;
}static void demo_exit(void)
{printk("demo_exit process!\n");cdev_del(&chr_dev);unregister_chrdev_region(ndev, 1);
}module_init(demo_init);
module_exit(demo_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("feixiaoxing@163.com");
MODULE_DESCRIPTION("A simple device example!");

在module_init中的函数是模块加载时处理的函数,而模块卸载的函数则是在module_exit中。每一个设备都要对应一个基本的设备数据,当然为了使得这个设备注册在整个系统当中,我们还需要分配一个设备节点,alloc_chrdev_region就完成这样一个功能。等到cdev_add的时候,整个设备注册的过程就全部完成了,就是这么简单。当然为了编写这个文件,我们还需要编写一个Makefile文件,

ifneq ($(KERNELRELEASE),)
obj-m := char.oelse
PWD  := $(shell pwd)
KVER := $(shell uname -r)
KDIR := /lib/modules/$(KVER)/build
all:$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions modules.*  Module.*
endif

这个Makefile文件和我们之前编写的makefile基本上没有区别,唯一的区别就是文件名称改成了char.o,仅此而已。为了编写模块,我们直接输入make即可。这时候,char.ko文件就可以生成了。然后,模块需要被注册在系统当中,insmod char.ko是少不了的。如果此时,我们还不确信是否模块已经加入到系统当中,完全可以通过输入lsmod | grep char进行查找和验证。为了创建设备节点,我们需要知道设备为我们创建的major、minor数值是多少,所以dmesg | tail 查找一下数值。在我hp的机器上,这两个数值分别是249和0,所以下面可以利用它们直接创建设备节点了,输入mknod /dev/chr_dev c 249 0即可,此时可以输入ls /dev/chr_dev验证一下。那么,按照这种方法,真的可以访问这个虚拟设备了吗,我们可以编写一段简单的代码验证一下,

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>#define CHAR_DEV_NAME "/dev/chr_dev"int main()
{int ret;int fd;char buf[32];fd = open(CHAR_DEV_NAME, O_RDONLY | O_NDELAY);if(fd < 0){printf("open failed!\n");return -1;}read(fd, buf, 32);close(fd);return 0;
}

代码的内容非常简单,就是利用CHAR_DEV_NAME直接打开设备,读写设备。当然。首先还是需要对这个文件进行编译,文件名为test.c,输入gcc test.c -o test,其次就是运行这个文件,直接输入./test即可。如果没有问题的话,那么说明我们的代码是ok的,但是我们还是没有看到任何内容。没关系,我们还是通过dmesg这个命令查看内核中是否存在相关的打印内容,直接输入dmesg | tail即可。此时如果没有意外的话,我们就可以看到之前在chr_open和chr_read中留下的printk打印,这说明我们的代码完全是ok的。

上面的代码只是一段小例子,真实的内容要比这复杂一下。不过既然我们都已经入门了,那么后面的内容其实也没有什么好怕的了。最后有两个事情补充一下:(1)如果大家在创建节点后想删除设备节点,直接rm -rf /dev/chr_dev即可;(2)上面这段代码的原型来自于《深入linux设备驱动程序内核机制》这本书,稍作修改,如果大家对内核机制的内容感兴趣,可以参考这本书的内容。

linux驱动编写(虚拟字符设备编写)相关推荐

  1. Linux驱动开发:字符设备驱动开发实战

    Linux驱动开发:字符设备驱动开发实战 一.工程创建 VSCode 创建工程,设置 C/C++ 配置,导入 linux kernel 源码目录,方便 vscode 写代码自动补全,vscode 配置 ...

  2. linux驱动编写(字符设备编写框架)

    [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com]     上次我们编写了一个简单的字符设备,但是涉及的内容比较少,只有open和read两个函数 ...

  3. linux 驱动 文件计数,字符设备驱动程序1

    一.主设备号和此设备号主设备号表示设备对应的驱动程序:次设备号由内核使用,用于正确确定设备文件所指的设备.内核用dev_t类型()来保存设备编号,dev_t是一个32位的数,12位表示主设备号,20为 ...

  4. Linux驱动开发: 杂项字符设备

    一.什么是杂项设备? 杂项设备(misc device)也是在嵌入式系统中用得比较多的一种设备驱动. 在Linux内核的include\linux目录下有Miscdevice.h文件,misc设备定义 ...

  5. linux驱动设备开发1——字符设备驱动

    0 LInux内核 linux内核的内部结构:内核各个模块如下所示 linux的驱动只有三种类型:字符设备驱动(键盘,鼠标).块设备驱动(硬盘).网络设备驱动(网卡,can等) 驱动的静态加载和动态加 ...

  6. linux驱动私有数据,linux驱动开发之字符设备--私有数据和container_of

    前言 驱动开发中通常为设备定义一个设备相关的设备结构体,其包含该设备的cdev .私有数据.信号量.irq等这些信息. 驱动开发中通常将文件的私有数据private_data指向设备结构体,在read ...

  7. [linux-nopage]内存映射虚拟字符设备驱动【P119】

    文章目录 目的:内核空间映射到用户空间 环境:Ubuntu 20.04 linux内核源码5.11.0-37-generic(版本自选) 实验结果 实验知识点 实验难点 实验代码 nopage.c M ...

  8. 虚拟字符设备驱动开发步骤

    目录 前言 字符设备驱动简介 内核驱动操作函数集合(file_operations结构体) 字符设备驱动开发步骤 .ko驱动模块的加载和卸载(module_init驱动入口.insmod驱动加载) 字 ...

  9. Linux内核学习-字符设备驱动学习(二)

    在Linux内核学习-字符设备驱动学习(一)中编写字符设备驱动的一种方法,但是需要手动创建设备节点. 有没有能够自动的创建设备节点的呢? 有!使用class_create()和device_creat ...

最新文章

  1. Eclipse将android 类编译为jar类库
  2. 如何成为简历界的“老司机”?这些简历技能你get到了吗?
  3. Apache下部署Django 的样式问题
  4. 每天一道LeetCode-----判断一个数是否是happy number(每一位的平方和最终为1)
  5. Mac android studio升级时提示 :Connection failed. Please check your network connection .
  6. html怎样在一张图片里写字,用HTML代码在图片上写字
  7. 存放在外存上的数据关机后_小鑫话题 | 惊了!关机后SSD会丢数据?
  8. VMware中的三种网络模式-----NAT模式
  9. html如何与js链接,链接index.html client.js和server.js
  10. Flash cs4新增内容:三维效果
  11. 0基础学SQL-Task02 SQL基础查询与排序(共7节)
  12. 计算机专业保研面试备考:概率论
  13. 完整JAVA学习路线图,助您从JAVA小白变身秃顶大叔,迈向JAVA成神之路
  14. 大数据时代的“拼图者”
  15. SpringBoot中Redis报错:NOAUTH Authentication required.; nested exception is redis.clients.jedis.exceptio
  16. 如何免ROOT,实现安卓设备远程控制?
  17. JS中使用bignumber处理高精度小数 失去去精确度运算 bigNumber用法
  18. origin画图很多个重叠在一起的
  19. Arduino手动添加ESP32相关开发板(图文并茂)
  20. 安全审计与安全管理平台的区别与联系

热门文章

  1. LinuxKit for ARM64 - on packet.net
  2. spring session的生命周期
  3. 2017华南理工华为杯H bx值(容斥问题)
  4. 腾讯云自曝自家技术只值1分钱,这技术以后谁还敢用
  5. 实践总结 - 不可错过的Angular应用技巧
  6. SSIS常用的包—发送Email任务
  7. HTML 去调table表单里面td之间的间距
  8. 2019-2-15 日记
  9. 2019-1-7Xiaomi Mi5 刷全球版MIUI教程
  10. 51 Nod 阶乘后面0的数量