导出符号表和字符设备驱动

news2024/10/5 14:24:52

目录

1. 导出符号表

1.1. 应用场景:驱动B想要使用驱动A的函数

1.2. 函数解析

1.3. 撰写提供者.c文件

1.4. 撰写提供者makefile文件

1.5. 执行makefile文件生成Module.symvers

1.6. 撰写调用者.c文件

1.7. 撰写调用者的makefile

1.8. 调用验证

2. 字符设备驱动(重点)

2.1. 应用场景:通过应用层读写设备驱动触发内核层操作

2.2. 函数解析

2.3. 撰写驱动.c文件

2.4. 撰写驱动.c的makefile文件

2.5. 执行make命令

2.6. 光有驱动是不行的,需要撰写应用层的.c文件

2.7. 手动创建设备文件


1. 导出符号表

1.1. 应用场景:驱动B想要使用驱动A的函数

1.2. 函数解析

函数原型:EXPORT_SYMBOL_GPL(FUNCTION NAME);
功能:导出符号表函数
参数:
	FUNCTION NAME 需要导出的函数名

1.3. 撰写提供者.c文件

hello.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
//撰写提供函数
int add(int a,int b)
{
	return (a+b);
}
//使用导出符号表函数
EXPORT_SYMBOL_GPL(add);
static int __init hello_init(void)
{
return 0;
}
static void __exit hello_exit(void)
{
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

1.4. 撰写提供者makefile文件

hello文件的Makefile

KERNELDIR:=/lib/modules/$(shell uname -r)/build
#KERNELDIR:=/home/hq/temp/kernel-3.4.39/
PWD:=$(shell pwd)
all:
	make -C $(KERNELDIR) M=$(PWD) modules
clean:
	make -C $(KERNELDIR) M=$(PWD) clean
obj-m:=add.o

1.5. 执行makefile文件生成Module.symvers

执行make命令

我们可以看到在生成一个

vi Module.symvers 使用vi命令我们可以看到文件信息

第一个参数是add的函数的地址

1.6. 撰写调用者.c文件

add.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
extern int add(int a,int b);//外部引用

static int __init hello_init(void)
{
return 0;
}

static void __exit hello_exit(void)
{
//此处调用了add函数
printk("add函数调用:%d\n",add(10,20));
}

module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

1.7. 撰写调用者的makefile

add文件的Makefile

KERNELDIR:=/lib/modules/$(shell uname -r)/build
#KERNELDIR:=/home/hq/temp/kernel-3.4.39/
PWD:=$(shell pwd)
all:
	make -C $(KERNELDIR) M=$(PWD) modules
clean:
	make -C $(KERNELDIR) M=$(PWD) clean
obj-m:=add.o

1.8. 调用验证

验证分析:分析hello.c源代码得出拆卸hello.ko文件会调用add函数

》1.安装add.ko(提供者)文件

若是不安装add.ko文件在安装hello.ko时候会出现add.o未定义的情况

》2.安装hello.ko(调用者)文件

》3.根据分析拆卸会调用add函数

2. 字符设备驱动(重点)

2.1. 应用场景:通过应用层读写设备驱动触发内核层操作

2.2. 函数解析

》1.函数原型:int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops)
函数功能:注册一个字符设备驱动
参数:
	major:主设备号	大于0为此设备号 等于0为默认分配设备号
	name: 设备名字
	fops:  操作方法的结构体
返回值:
	由major决定
    major>0,成功返回0,失败返回错误码(负数)
    major=0,成功返回主设备号,失败返回错误码
》2.函数原型:void unregister_chrdev(unsigned int major, const char *name)
函数功能:注销一个字符设备驱动
参数:
	major:主设备号
	name: 设备名字
	无返回值

关于fops结构体,我们只用到几个就可

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 *);
	ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
	ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
	int (*readdir) (struct file *, void *, filldir_t);
	unsigned int (*poll) (struct file *, struct poll_table_struct *);
	long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
	long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
	int (*mmap) (struct file *, struct vm_area_struct *);
	int (*open) (struct inode *, struct file *);
	int (*flush) (struct file *, fl_owner_t id);
	int (*release) (struct inode *, struct file *);
	int (*fsync) (struct file *, loff_t, loff_t, int datasync);
	int (*aio_fsync) (struct kiocb *, int datasync);
	int (*fasync) (int, struct file *, int);
	int (*lock) (struct file *, int, struct file_lock *);
	ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
	unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
	int (*check_flags)(int);
	int (*flock) (struct file *, int, struct file_lock *);
	ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
	ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
	int (*setlease)(struct file *, long, struct file_lock **);
	long (*fallocate)(struct file *file, int mode, loff_t offset,
			  loff_t len);
};

2.3. 撰写驱动.c文件

//写一个字符设备驱动
#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/fs.h> //不要忘记添加头文件

unsigned int major =0; //主设备号
#define NAME "hello"//定义设备文件名

//2.填充函数和结构体
//定义read函数
ssize_t mycdev_read (struct file *file, char __user *ubuf, size_t size, loff_t * offs)
{
printk("hello read\n");
return 0;
}
//定义写函数
ssize_t mycdev_write (struct file *file, const char __user *ubuf, size_t size, loff_t *offs)
{
printk("hello write\n");
return 0;
}
//定义打开函数
int mycdev_open(struct inode *inode, struct file *file)
{
printk("hello open\n");
return 0;
}
//定义关闭函数
int mycdev_release (struct inode *inode, struct file *file)
{
printk("hello close\n");
return 0;
}

//填充设备文件结构体
struct file_operations fops={
.open=mycdev_open,
.write=mycdev_write,
.release=mycdev_release,
.read=mycdev_read,
};

static int __init hello_init(void)
{

//1.注册设备

major= register_chrdev(major, NAME, &fops);
if(major<0)
{
printk("register_chrdev error\n");
return major; //出错的话major为负值
}

return 0;
}

static void __exit hello_exit(void)
{
//3.拆卸的时候注销设备
unregister_chrdev(major, NAME);

}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

2.4. 撰写驱动.c的makefile文件

KERNELDIR:=/lib/modules/$(shell uname -r)/build
#KERNELDIR:=/home/hq/temp/kernel-3.4.39/
PWD:=$(shell pwd)
all:
	make -C $(KERNELDIR) M=$(PWD) modules
clean:
	make -C $(KERNELDIR) M=$(PWD) clean
obj-m:=hello.o

2.5. 执行make命令

2.6. 光有驱动是不行的,需要撰写应用层的.c文件

在驱动同级目录下创建test.c文件

写入应用层程序

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>

char buf[128]={0};
int main(int argc,const char *argv[])
{
  int fd;
  fd=open("./hello",O_RDWR);
  if(fd==-1)
  {
    perror("open error");
    return -1;
  }
  write(fd,buf,sizeof(buf));
  read(fd,buf,sizeof(buf));
  close(fd);
  return 0;
}

使用gcc进行编译,生成a.out文件

2.7. 手动创建设备文件

》1.使用 sudo insmod xxxx.ko 安装驱动

》2.使用 cat /proc/devices 查看主设备号

》3.使用 sudo mknod <你的文件名字> c/b(c字符设备 b块设备) 主设备号 次设备号

可以手动创建设备文件

》4.修改设备文件的权限

sudo chmod 0777 hello 修改权限

》5.运行应用层的可执行程序,我的是a.out文件

能看到最后结果

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

springCloud对接kafka+websockt消息中心

1.网关没有配置message和websockt的路由 2.message启动报错&#xff0c;线上zookeeper启动失败导致 3.message配置文件参数读取不到&#xff0c;原因&#xff1a;message_dev.yml 正确名称 message-dev.yml 4.线上websockt地址连接失败&#xff0c;原因&#xff1a;白名单没…

Cisco MPLS VPN Option C2

无RR 一、拓扑 环境:AR1-AR8各有loopback0接口分别是1.1.1.1 2.2.2.2 二、配置步骤 1、配置AS100和AS200的底层网络&#xff0c;这里使用OSPF配置 2、配置AS内使用LDP协议分发标签 3、ASBR之间建立EBGP邻居关系&#xff0c;相对端通告路由时携带标签&#xff0c;互联…

确保消息不会丢失

现在主流的消息队列产品都提供了非常完善的消息可靠性保证机制&#xff0c;完全可以做到在消息传递过程中&#xff0c;即使发生网络中断或者硬件故障&#xff0c;也能确保消息的可靠传递&#xff0c;不丢消息。 绝大部分丢消息的原因都是由于开发者不熟悉消息队列&#xff0c;没…

第一章:软件工程师必备的硬件基础

目录 1、软件工程师需要具备的知识 2、计算机的组成 3、操作系统 4、BIOS的相关知识 1、软件工程师需要具备的知识 问题一&#xff1a;运维工程师、实施工程师是啥&#xff1f; 运维工程师负责服务的稳定性&#xff0c;确保服务可以不间断地为用户提供服务。 实施工程师负…

CmakeList使用笔记

cmake是一个跨平台、开源的构建系统。它是一个集软件构建、测试、打包于一身的软件。它使用与平台和编译器独立的配置文件来对软件编译过程进行控制。 Cmake的所有语句都写在一个CMakeLists.txt的文件中&#xff0c;Cmake运行之后就会产生我们想要的makefile文件&#xff0c;然…

【Jquery】Jquery实现页面嵌套到客户项目框架里面,不需要登录,获取cookie并直接展示首页:

文章目录 一、效果图:二、实现思路:三、实现代码: 一、效果图: 二、实现思路: 需求&#xff1a;嵌套到别的客户项目框架里面&#xff0c;不需要登录直接展示首页 实现&#xff1a;在打开页面前&#xff0c;获取登录cookie&#xff0c;然后再打开页面 三、实现代码: <!DOCTYP…

63、基于51单片机数字频率计NE555数码管显示系统设计(程序+原理图+Proteus仿真+参考论文+开题报告+任务书+元器件清单等)

摘 要 近年来随着计算机在社会领域的渗透和大规模集成电路的发展&#xff0c;单片机的应用正在不断地走向深入&#xff0c;由于它具有功能强&#xff0c;体积小&#xff0c;功耗低&#xff0c;价格便宜&#xff0c;工作可靠&#xff0c;使用方便等特点&#xff0c;因此越来越…

世界名酒商城元宇宙 中国4大“惨败酒

大家平时买白酒都怎么选择呢&#xff1f;一般都选择平时广告做得多&#xff0c;耳熟能详的大品牌&#xff0c;或者是直接听导购的买酒&#xff0c;毕竟那么贵的价格酒不可能不好&#xff0c;实际上这种买酒方式虽然不能说错吧&#xff0c;但是极容易错过很多好酒。 白酒市场上就…

自动化测试,UI测试和接口测试的基本概念以及指令

今天跟大家介绍UI测试、接口测试、单元测试主要内容 UI测试【Selenium】 UI测试是最接近软件真实用户使用行为的测试类型。通常是模拟真实用户使用软件的行为&#xff0c;即模拟用户在软件界面上的各种操作&#xff0c;并验证这些操作对应的结果是否正确。 这是最基本的一些S…

清华发布 KoLA 评测集,分4个认知层级评测LLM,GPT-4竟不是第一?

作者 | Python 预训练语言模型&#xff08;PLM&#xff09;刷GLUE&#xff0c;SuperGLUE&#xff0c;甚是常见&#xff1b;那ChatGPT等大语言模型&#xff08;LLM&#xff09;刷什么榜呢&#xff1f;现在常用的榜单&#xff0c;例如MMLU评测了57个学科知识&#xff0c;Big-Benc…

CodeForces..一条绳上的蚂蚱.[简单].[ifelse]

题目描述&#xff1a; 题目解读&#xff1a; 给定整数x和k&#xff0c;从0开始到达x&#xff0c;且每次移动的值&#xff0c;不能被k整除。 输出到达目标点x的最小移动次数和每次移动的值。 解题思路&#xff1a; 相当于在数轴上移动到目标点&#xff0c;且每次移动的数值不…

WPS数据清洗+R语言读取文件画频数分布直方图

R语言是一门好语言&#xff0c;但很多人在读取文件中数据时会遇到问题。比如我遇到的问题就是从文件中读取数据后&#xff0c;数据无法用于画图。 检索了N篇博文&#xff08;抱歉我实在无法一一列举30篇博文&#xff09;后&#xff0c;终于看到曙光&#xff0c;事实告诉我学任…

最新版CleanMyMacX4.13.6发布了,它值得买吗?

Clean My Mac X是Mac上一款美观易用的系统优化清理工具&#xff0c;也是小编刚开始用Mac时的装机必备。垃圾需要时时清&#xff0c;电脑才能常年新。Windows的垃圾清理工具选择有很多&#xff0c;但是Mac的清理工具可选择的就很少。 最新版CleanMyMacX4.13.6发布此版本有哪些亮…

2023年衣物洗护市场行业分析(京东天猫数据分析)

近年来&#xff0c;受消费者习惯的推动&#xff0c;衣物洗护用品市场不断发展&#xff0c;洗护用品行业的市场规模也不断增长。 根据鲸参谋电商数据分析平台的相关数据显示&#xff0c;今年1月份至4月份&#xff0c;天猫平台上衣物洗护相关产品的销量为7300万&#xff0c;产品销…

TC8:SOMEIPSRV_OPTIONS_05-07

SOMEIPSRV_OPTIONS_05: Reserved field of the IPv4 Endpoint Option 目的 IPv4 Endpoint Option的Reserved字段应静态设置为0x00 这是第二个Reserved字段 测试步骤 DUT CONFIGURE:启动具有下列信息的服务Service ID:SERVICE-ID-1Instance数量:1Tester:客户端-1发送SOME/I…

数字广东:共建区块链开源生态,实现高水平科技自立自强

近日&#xff0c;在2023年第1季社区Task挑战赛中&#xff0c;众多开发者为FISCO BCOS开源项目及周边组件贡献了丰富的代码和教程。其中&#xff0c;作为金链盟开源工作组成员&#xff0c;数字广东网络建设有限公司科技发展部的数字信任中心团队参与了共建。 数字广东网络建设…

【深度学习】5-1 与学习相关的技巧 - 参数的更新(Momentum,AdaGrad, Adam )

神经网络的学习的目的是找到使损失函数的值尽可能小的参数。这是寻找最优参数的问题&#xff0c;解决这个问题的过程称为最优化。 但是神经网络的最优化问题非常难。这是因为参数空间非常复杂&#xff0c;无法轻易找到最优解。而且&#xff0c;在深度神经网络中&#xff0c;参…

直击面试现场:你对MySQL的数据类型了解有多少?

前言 隔着玻璃门&#xff0c;看着面试官缓缓走来&#xff0c;头上飘着几根白发&#xff0c;在行走中随风摇曳&#xff0c;看的让人有一种想帮他薅下来的冲动。 这次面试的岗位是数据库数据类型&#xff0c;面试官坐下来冲着面试者沐风晓月呵呵一笑&#xff0c; “来啦”&…

广工击败清华,CGTN Sports 是这样说的

6 月 18 日晚上&#xff0c;被很多人不看好的弱旅广东工业大学&#xff0c;击败了豪门清华大学&#xff0c;拿下 CUBAL 的总冠军。 CGTN Sports Scene 是这样报道的&#xff1a; &#x1f3c6; 1st ever CUBAL championship in school history 校史上第一个 CUBAL 冠军 CUBA…

uniapp——Android 异常: failed to connect to localhost/127.0.0.1

bug解决——携带出现&#xff1a; Waiting to navigate to: /pages/……, do not operate continuously: /pages/…… failed to connect to localhost/127.0.0.1 解决方法&#xff1a; 我的报错&#xff1a;主要是failed to connect to localhost/127.0.0.1引发的Waiting …