《linux系统内核设计与实现》-实现最简单的字符设备驱动

开发linux内核驱动需要以下4个步骤:

1 编写hello驱动代码

驱动代码如下 helloDev.c,这是一个最小、最简单的驱动,去掉了其他的不相干代码,尽量让大家能了解驱动本身。

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/slab.h>

#define BUFFER_MAX  (10)             // buff大小
#define OK          (0)
#define ERROR       (-1)

struct cdev *gDev;                   // 字符设备结构体指针
struct file_operations *gFile;       // 文件操作结构体指针
dev_t devNum;                        // 设备号
unsigned int subDevNum = 1;          // 注册设备的数量

int reg_major  = 232;                // 主设备号
int reg_minor =  0;                  // 次设备号
char *buffer;                        // 缓冲区

/**
 * printk 是内核中用于输出调试信息、错误信息和其他日志信息的函数。
 * 它将消息输出到内核日志缓冲区,这些日志可以通过 dmesg 命令查看
 * KERN_INFO 是一个宏:信息性消息
*/

// hello_open函数:打开文件
int hello_open(struct inode *p, struct file *f)
{
    printk(KERN_INFO "hello_open\r\n");
    return 0;
}

// hello_write函数
ssize_t hello_write(struct file *f, const char __user *u, size_t s, loff_t *l)
{
    printk(KERN_INFO "hello_write\r\n");
    return 0;
}

// hello_read函数
ssize_t hello_read(struct file *f, char __user *u, size_t s, loff_t *l)
{
    printk(KERN_INFO "hello_read\r\n");
    return 0;
}

int hello_init(void)
{
    devNum = MKDEV(reg_major, reg_minor);                   // 根据主次设备号,生成devNum,唯一标识设备
    // 把设备号注册到内核中。从devNum开始注册subDevNum个设备。
    if (OK == register_chrdev_region(devNum, subDevNum, "helloworld")) {
        printk(KERN_INFO "register_chrdev_region ok \n");
    } else {
        printk(KERN_INFO "register_chrdev_region error n");
        return ERROR;
    }
    
    printk(KERN_INFO " hello driver init \n");
    // 内核模块成功分配并初始化了一个字符设备结构
    gDev = kzalloc(sizeof(struct cdev), GFP_KERNEL);
    // 文件操作
    gFile = kzalloc(sizeof(struct file_operations), GFP_KERNEL);
    // 赋值。回调函数
    gFile->open = hello_open;
    gFile->read = hello_read;
    gFile->write = hello_write;
    gFile->owner = THIS_MODULE;
    // 建立联系:通过这两行代码,驱动程序完成了字符设备的初始化和注册,使得字符设备可以被用户进程打开、读取和写入。
    cdev_init(gDev, gFile);             // 初始化字符设备结构体gDev,并将其与文件操作结构体gFile关联起来。
    cdev_add(gDev, devNum, 1);          // 将初始化好的字符设备gDev 添加到内核字符设备层,使其成为一个有效的字符设备,可以被用户空间访问和操作
    return 0;
}

// 驱动退出函数
void __exit hello_exit(void)
{
    printk(KERN_INFO " hello driver exit \n");
    cdev_del(gDev);                                         // 删除字符设备
    kfree(gFile);                                           // 释放内存
    kfree(gDev);                                            // 释放内存
    unregister_chrdev_region(devNum, subDevNum);            // 注销设备号
    return;
}

module_init(hello_init);        // 声明驱动的入口函数。执行insmod的时候调用
module_exit(hello_exit);        // 声明驱动的退出函数。执行rmmod的时候调用
MODULE_LICENSE("GPL");          // 指定模块的许可证

有了驱动文件之后,还需要一个Makefile才能把驱动编译出来

2 编写makefile

ifneq ($(KERNELRELEASE),)
obj-m := helloDev.o
else
PWD := $(shell pwd)									# 获取当前目录路径,并赋值给变量 PWD
# KDIR:= /lib/modules/4.4.0-31-generic/build
# KDIR:= /home/winter/linux-4.9.229
KDIR := /lib/modules/`uname -r`/build				# 指定内核构建目录
all:
	make -C $(KDIR) M=$(PWD)
clean:
	rm -rf *.o *.ko *.mod.c *.symvers *.c~ *~
endif
# $(KERNELRELEASE) 是内核构建系统设置的变量,当通过内核构建系统调用此 Makefile 时,这个变量会被定义。
# -C $(KDIR):切换到内核构建目录
# M=$(PWD):指定模块源码目录为当前目录
# make -C $(KDIR) M=$(PWD):调用内核构建系统
# 这段 Makefile 通过条件分支来区分内核构建环境和用户构建环境,在用户环境中调用内核构建系统进行模块编译,并提供了清理编译生成文件的功能。

linux应用层程序在编译的时候,需要链接c运行时库和glibc库。那驱动需不需要呢?

3 编译和加载hello驱动

驱动也需要,但是驱动不能链接和使用应用层的任何lib库,驱动需要引用内核的头文件和函数。所以,编译的时候需要指定内核源码的地址。为了开发方便,也可以安装内核开发包,之后引用这个内核开发包的目录也可以。本例为:/lib/modules/4.4.0-31-generic/build

make

编译出来的驱动文件,名称为:helloDev.ko

接下来把这个驱动加载到内核

# 清零内核日志
dmesg -c
# 执行
insmod helloDev.ko
# 再看内核日志
dmesg

# 查看驱动
lsmod

# 卸载驱动
rmmod helloDev.ko
# 再查看驱动,无

4 编写应用程序测试hello驱动test.c

/*************************************************************************
 > File Name: test.c
 > Author: Winter
 > Created Time: 2024年05月17日 星期五 21时03分06秒
 ************************************************************************/

#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/select.h>


#define DATA_NUM   (64)
int main(int argc, char *argv[])
{
     int fd, i;
     int r_len, w_len;
     fd_set fdset;
     char buf[DATA_NUM]="hello world";
     memset(buf,0,DATA_NUM);
     fd = open("/dev/hello", O_RDWR);
     printf("%d\r\n",fd);
     if(-1 == fd) {
          perror("open file error\r\n");
          return -1;
     } else {
          printf("open successe\r\n");
     }
     
     w_len = write(fd,buf, DATA_NUM);
     r_len = read(fd, buf, DATA_NUM);
     printf("%d %d\r\n", w_len, r_len);
     printf("%s\r\n",buf);
     
     return 0;
}

编译执行。因为没有/dev/hello这个设备文件,所以打开失败

需要创建设备文件

mknod
man mknod
# 名字 类型 【主设备号  次设备号】
mknod [OPTION]... NAME TYPE [MAJOR MINOR]
mknod /dev/hello c 232 0

# 查看
ls -l /dev/hello

# 再执行测试程序
./test

有调用日志

这个返回值是驱动部分对应的返回值

5 一些命令

dmesg -c			# 清零内核日志
insmod helloDev.ko  # 插入(加载)Linux内核模块的命令
dmesg				# dmesg 命令用于查看和管理 Linux 内核的环形缓冲区中的消息
lsmod				# 查看驱动
rmmod helloDev.ko   # 卸载驱动

# 创建设备文件:名字 类型 【主设备号  次设备号】
mknod [OPTION]... NAME TYPE [MAJOR MINOR]
mknod /dev/hello c 232 0

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/779917.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

python函数和c的区别有哪些

Python有很多内置函数&#xff08;build in function&#xff09;&#xff0c;不需要写头文件&#xff0c;Python还有很多强大的模块&#xff0c;需要时导入便可。C语言在这一点上远不及Python&#xff0c;大多时候都需要自己手动实现。 C语言中的函数&#xff0c;有着严格的顺…

vulhub-activemq(CVE-2016-3088)

在 Apache ActiveMQ 5.12.x~5.13.x 版本中&#xff0c;默认关闭了 fileserver 这个应用&#xff08;不过&#xff0c;可以在conf/jetty.xml 中开启&#xff09;&#xff1b;在 5.14.0 版本后&#xff0c;彻底删除了 fileserver 应用。【所以在渗透测试过程中要确定好 ActiveMQ …

2024年世界人工智能大会(WAIC)各大佬的精彩发言

2024年世界人工智能大会&#xff08;WAIC&#xff09;在上海举行&#xff0c;受到了广泛关注和参与。以下是大会首日的主要观点和议题的总结&#xff1a; AI 应用落地&#xff1a;大会讨论了AI应用如何落地&#xff0c;即如何在当前阶段利用大模型技术实现实际应用。 AI 安全&…

nginx转发的问题

我在项目配置的时候遇到一个问题&#xff1a; 配置了域名转发&#xff0c;且配置了https nginx配置如下&#xff1a; server {listen 443 ssl;server_name yourdomain.com;ssl_certificate /path/to/your/certificate.crt;ssl_certificate_key /path/to/your/private.key;loca…

收银系统源码-线上商城预售功能

1.功能描述 预售&#xff1a;智慧新零售收银系统&#xff0c;线上商城营销插件之一&#xff0c;商品出售时可设置以支付定金或全款的方式提前预售&#xff0c;门店按订单量备货&#xff0c;降低压货成本&#xff1b; 2.适用场景 易损商品提前下单备货&#xff0c;如水果生鲜…

拼多多20240509实习生笔试

题目一 解题思路 分类讨论 情况一&#xff1a;5元汉堡也买不完。 情况二&#xff1a;5元汉堡能买完&#xff0c;非5元买不起。 情况三&#xff1a;都能买起&#xff0c;或还有剩余买原价汉堡。 题目二 解题思路 找规律&#xff0c;假设有...xy...&#xff0c;x在前。如果交换x…

KubeSphere 社区双周报|2024.06.21-07.04

KubeSphere 社区双周报主要整理展示新增的贡献者名单和证书、新增的讲师证书以及两周内提交过 commit 的贡献者&#xff0c;并对近期重要的 PR 进行解析&#xff0c;同时还包含了线上/线下活动和布道推广等一系列社区动态。 本次双周报涵盖时间为&#xff1a;2024.06.21-07.04…

nodejs实现:支付宝订单查询

nodejs实现&#xff1a;支付宝订单查询&#xff1b; 原生http请求&#xff0c;不使用三方库&#xff1b; 代码如下&#xff1a; const https require(https); const crypto require(crypto); const querystring require(querystring);// 支付宝公共参数 const PRIVATE_KE…

联想小新14Pro,误删了一个注册表,怎么办?

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

flask模块化、封装使用cache(flask_caching)

1.安装flask_caching库 pip install flask_caching 2.创建utils Python 软件包以及cache_helper.py 2.1cache_helper.py代码 from flask_caching import Cachecache Cache()class CacheHelper:def __init__(self, app, config):cache.init_app(app, config)staticmethoddef…

常见的Java运行时异常

常见的Java运行时异常 1、ArithmeticException&#xff08;算术异常&#xff09;2、ClassCastException &#xff08;类转换异常&#xff09;3、IllegalArgumentException &#xff08;非法参数异常&#xff09;4、IndexOutOfBoundsException &#xff08;下标越界异常&#xf…

【python】python母婴数据分析模型预测可视化(数据集+论文+PPT+源码)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

AiPPT的成功之路:PMF付费率与增长策略

如果要给 2023 年的 AI 市场一个关键词&#xff0c;那肯定是“大模型”&#xff0c;聚光灯和大家的注意力、资金都投向了那些大模型公司&#xff1b;而如果要给 2024 年的 AI 市场一个关键词&#xff0c;则一定是 PMF&#xff08;产品市场契合&#xff09;。如果没有 PMF&#…

VuePress 的更多配置

现在&#xff0c;读者应该对 VuePress、主题和插件等有了基本的认识&#xff0c;除了插件&#xff0c;VuePress 自身也有很多有用的配置&#xff0c;这里简单说明下。 ‍ ‍ VuePress 的介绍 在介绍了 VuePress 的基本使用、主题和插件的概念之后&#xff0c;我们再来看看官…

Oracle RAC 19c 打补丁至最新版本-19.23.0.0.0

实验环境-我是从19.0.0.0直接打到19.23.0.0.0&#xff0c;适合刚部署好的集群打补丁直接到最新版本。 查看当前环境 查询集群中运行的 Oracle Clusterware 软件的 activex 版 查询本地节点上二进制文件中存储的 Oracle Clusterware 软件的版本 查询本地服务器上 OHAS 和 Oracle…

windows无法访问github

##一、如果发现windows无法访问github时 一般就是我们的dns出现了问题&#xff0c;此时我们需要更换一个dns访问 ##二、解决方法 首先我们访问ip查询地址&#xff0c; https://ipchaxun.com/github.com/ 可更换下面历史ip进行测试&#xff0c;在windows的cmd里面输入ping git…

【C++】开源:命令行解析库CLI11配置与使用

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍命令行解析库CLI11配置与使用。 无专精则不能成&#xff0c;无涉猎则不能通。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一下&#x…

苹果清理软件:让你的设备焕然一新

随着时间的推移&#xff0c;无论是Mac电脑还是iOS设备&#xff0c;都可能会因为积累的垃圾文件、缓存、未使用的应用和其他冗余数据而开始表现出性能下降。这不仅会占用宝贵的存储空间&#xff0c;还可能影响设备的响应速度和电池寿命。幸运的是&#xff0c;有多种苹果清理软件…

Zabbix监控软件

目录 一、什么是Zabbix 二、zabbix监控原理 三、zabbix 安装步骤 一、什么是Zabbix ●zabbix 是一个基于 Web 界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案。 ●zabbix 能监视各种网络参数&#xff0c;保证服务器系统的安全运营&#xff1b;并提供灵活的…

使用labelme中的AI多边形(AI-polygon)标注 win版exe Create AI-Polygon闪退

这里写目录标题 虚拟环境创建labelme虚拟环境下载AI标注模型win Labelme.exe Create AI-Polygon闪退问题也用如下方法解决 win Labelme.exe Create AI-Polygon闪退问题也用如下方法解决愉快地使用labelme的AI标注工具 虚拟环境 创建labelme虚拟环境 创建基础环境并激活 cond…