BestCentOS

BestCentOS.com
CentOS系统网——精选每一篇高品质的技术干货
  1. 首页
  2. 开源快讯
  3. 正文

深入linux设备驱动程序内核机制 哈喽,我是老吴,继续记录我的学习心得

2023年9月18日 159点热度

内核驱动开发_深入linux设备驱动程序内核机制_linux内核驱动开发前景

哈喽,我是老吴,继续记录我的学习心得。

一、保持专注的几个方法二、Linux字符设备驱动隐情(1)

正文目录:

1. 什么是字符设备驱动?
2. 快速体验字符设备驱动和应用程序 (超简单的 demo)
3. 字符设备在内核里的抽象
    3.1 字符设备核心代码概览
    3.2 对字符设备进行抽象: struct cdev
    3.3 对字符设备的操作进行抽象:struct file_operations
4. 更多值得学习的知识点
5. 相关参考

写作目的:

测试环境:

1.哪些是字符设备驱动?

linux内核驱动开发前景_深入linux设备驱动程序内核机制_内核驱动开发

2.快速体验字符设备驱动和应用程序(超简单的demo)

1)字符设备驱动(chrdev_drv.c):

字符设备的打开和读函数:

static struct cdev chr_dev; // 字符设备抽象
static dev_t ndev;          // 设备号
static int chr_open(struct inode *nd, struct file *filp)
{
    printk("chr_open, major=%d, minor=%dn", MAJOR(nd->i_rdev), MINOR(nd->i_rdev));
    return 0;
}

static ssize_t chr_read(struct file *filp, char __user *u, size_t sz, loff_t *off)
{
    printk("In chr_read()n");
    return 0;
}

static int chr_release(struct inode *nd, struct file *filp)
{
    printk("In chr_release()n");
    return 0;
}

文件操作函数集:

struct file_operations chr_ops =
{

    .owner = THIS_MODULE,
    .open = chr_open,
    .read = chr_read,
    .release = chr_release,
};

模块加载和卸载:

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=%dn", 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...n");

    cdev_del(&chr_dev);
    unregister_chrdev_region(ndev, 1);
}

2)访问字符设备的应用程序:

int main()
{
    int ret;
    char buf[32];
    int fd = open("/dev/chr_dev", O_RDONLY|O_NDELAY);
    if(fd < 0)
    {
        printf("open file %s failed!n", CHR_DEV_NAME);
        return -1;
    }
    read(fd, buf, 32);
    close(fd);

    return 0;
}

3)不用完全理解代码的含意,直接看运行疗效:

# 编译驱动程序
$ make KERNELDIR=XXX/linux ARCH=arm CROSS_COMPILE=arm-linux-

# 编译应用程序
$ arm-linux-gcc chrdev_app.c -o chrdev_app

# 加载驱动模块
$ insmod chrdev_drv.ko
demo_init():major=242, minor=0

# 手动创建字符设备文件节点
$ mknod /dev/chr_dev c 242 0

# 运行应用程序
$ ./chrdev_app
chr_open, major=242, minor=0
In chr_read()
In chr_release()

从里面测运行结果可知,应用程序调用chrdev_app.c/open()会造成驱动程序chrdev_drv.c/structfile_operationschr_ops->open()被调用,read操作也是类似。

内核是怎样实现上述功能的?

带着这个困扰来了解字符设备驱动的框架,才不会迷失在内核里各类复杂的代码细节里。

3字符设备在内核里的具象3.1字符设备核心代码概览

在深入阅读各类代码之前,先整体地概览一遍将会涉及到的程序文件,找出核心主干,能有效地防止深陷纷扰的代码细节中。

1)分解Csource文件,fs/char_dev.c(Linux-4.14):

作用:

char_dev.c是字符设备驱动框架的核心实现文件,它坐落fs目录中,说明了字符设备驱动和文件系统是紧密联系在一起的。

内容(以重要性排序):

1>public函数:

// 1. 字符设备子系统初始化
void __init chrdev_init(void) 

// 2. struct cdev 管理相关
void chrdev_show(struct seq_file *f, off_t offset) 
void cdev_put(struct cdev *p) 
void cd_forget(struct inode *inode) 
int cdev_add(struct cdev *p, dev_t dev, unsigned count) 
void cdev_set_parent(struct cdev *p, struct kobject *kobj) 
int cdev_device_add(struct cdev *cdev, struct device *dev) 
void cdev_device_del(struct cdev *cdev, struct device *dev) 
void cdev_del(struct cdev *p) 
struct cdev *cdev_alloc(void) 
void cdev_init(struct cdev *cdev, const struct file_operations *fops)

// 3. 设备号管理相关
__register_chrdev_region(unsigned int major, unsigned int baseminor, 
__unregister_chrdev_region(unsigned major, unsigned baseminor, int minorct) 
int register_chrdev_region(dev_t from, unsigned count, const char *name) 
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, 
int __register_chrdev(unsigned int major, unsigned int baseminor, 
void unregister_chrdev_region(dev_t from, unsigned count) 
void __unregister_chrdev(unsigned int major, unsigned int baseminor,

2>public变量:

const struct file_operations def_chr_fops = {
 .open = chrdev_open,
 .llseek = noop_llseek,
};

3>private变量:

static struct char_device_struct {
 struct char_device_struct *next;
 unsigned int major;
 unsigned int baseminor;
 int minorct;
 char name[64];
 struct cdev *cdev;  /* will die */
} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];

static struct kobj_map *cdev_map; 
static struct kobj_type ktype_cdev_default;
static struct kobj_type ktype_cdev_dynamic;

4>private函数:

static inline int major_to_index(unsigned major) 
static int find_dynamic_major(void) 
static struct kobject *cdev_get(struct cdev *p) 
static int chrdev_open(struct inode *inode, struct file *filp) 
static void cdev_purge(struct cdev *cdev) 
static struct kobject *exact_match(dev_t dev, int *part, void *data) 
static int exact_lock(dev_t dev, void *data) 
static void cdev_unmap(dev_t dev, unsigned count) 
static void cdev_default_release(struct kobject *kobj) 
static void cdev_dynamic_release(struct kobject *kobj) 
static struct kobject *base_probe(dev_t dev, int *part, void *data)

2)分解Cheader文件,include/linux/cdev.h(Linux-4.14):

作用:

包含字符设备驱动相关结构体的定义、以及一些字符设备核心API的申明。

内容:

1>structcdev结构体:

struct cdev {
 struct kobject kobj;
 struct module *owner;
 const struct file_operations *ops;
 struct list_head list;
 dev_t dev;
 unsigned int count;
} __randomize_layout;

3.2对字符设备进行具象:structcdev

编撰字符设备驱动程序就是为了管理和控制字符设备,Linux内核将字符设备具象为一个数据结构:structcdev。这个结构体虽然从Linux-2.6到如今Linux-5.8都没有发生变化。

1)structcdev成员简介:

目前没必要完全理解那些成员的作用,有个大约印象就好:

2)两种方法创建structcdev对象:

这儿用对象一词,是为了引导你们用面向对象的思维来看待Linux内核的设计。

面向过程还是面向对象linux之家,不应当和语言绑定在一起,应当理解为2种不同的编程思维。人脑是很美妙的,在不同的场景,只要你乐意,它能够应用不同的思维方法来解决问题。设计Linux内核代码的神牛们,堪称是各个都是面向对象编程的大牛,练习编程就应当练习对事物的具象能力,C程序员若果认为自己缺少这方面的能力,不如学习一下Java编程。

静态定义:

static struct cdev chr_dev;

动态分配:

struct cdev *my_cdev = cdev_alloc();

cdev_alloc()除了会为structcdev对象分配显存空间深入linux设备驱动程序内核机制,都会对该对象进行必要的初始化:

struct cdev *cdev_alloc(void)
{
 struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);
 if (p) {
  INIT_LIST_HEAD(&p->list);
  kobject_init(&p->kobj, &ktype_cdev_dynamic);
 }
 return p;
}

一个值得注意的点:

我搜索了一下内核源码,发觉大多数驱动都选择静态定义structcdev,这样更简单省事。

3)某个真实字符硬件的具象:

数据结构structcdev作为字符设备的具象,仅仅是为了满足Linux内核对字符设备驱动程序框架结构设计的须要。

现实中,一个具体的字符硬件设备的数据结构的具象常常要复杂得多,在这些情况下structcdev经常作为一种内嵌的成员变量出现在实际设备的数据结构中。

比如drvier/watchdog/watchdog_dev.c:

struct watchdog_core_data {
 struct kref kref;
 struct cdev cdev;
 struct watchdog_device *wdd;
 struct mutex lock;
 unsigned long last_keepalive;
 unsigned long last_hw_keepalive;
 struct delayed_work work;
 unsigned long status;  /* Internal status bits */
};

4)初始化cdev对象:

深入linux设备驱动程序内核机制_内核驱动开发_linux内核驱动开发前景

void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
 memset(cdev, 0, sizeof *cdev);
 INIT_LIST_HEAD(&cdev->list);
 kobject_init(&cdev->kobj, &ktype_cdev_default);
 cdev->ops = fops;
}

cdev_init()最重要的作用就是将structcdev对象和structfile_operations对象绑定在一起。

一些值得注意的点:

3.3对字符设备的操作进行具象:structfile_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 *);
 ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
 ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
    
    [...]

 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 (*fasync) (int, struct file *, int);
 int (*lock) (struct file *, int, struct file_lock *);

 [...];

} __randomize_layout;

鉴于大多数人的注意力难以在一篇文章里上集中太久,更多的内容将置于旁边的文章里。建议你们可以先自行阅读相关书籍,不是自己理解到的东西是消化不了的。

4.更多值得学习的知识点5.相关参考三、思考技术,也思索人生

学习技术,更要学习怎么生活。

你和我各有一个苹果,假如我们交换苹果的话,我们还是只有一个苹果。但当你和我各有一个看法,我们交换看法的话,我们就都有两个看法了。

对嵌入式系统(Linux、RTOS、OpenWrt、Android)和开源软件感兴趣,想和更多人相互交流学习深入linux设备驱动程序内核机制,请关注公众号:嵌入式Hacker,一上去学习吧。

无论是关注或转发,还是打赏,都是对作者莫大的支持。认为文章对你有价值的话,不妨点个在看和点赞哦。

欢迎加入我的陌陌群:先加我,我拉你进群,暗号(我最棒)。

祝诸位工作顺利,家庭幸福linux下socket编程,财源滚滚~~~

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: linux系统 字符
最后更新:2023年9月18日

CentOS系统网

每日更新,欢迎收藏♥ 不积跬步无以至千里,加油,共勉。

点赞
< 上一篇
下一篇 >

CentOS系统网

每日更新,欢迎收藏♥
不积跬步无以至千里,加油,共勉。

最新 热点 随机
最新 热点 随机
Linux集群:原理与实战经验 华数TV电脑版安装说明及常见问题汇总 Linux系统中DNS配置文件查看和编辑指南 华数TV电视版:4K画质堪比影院:杜比音效、1080P Linux系统强制卸载的必要性和方法 百问网制作好了完备的Ubuntu镜像 解密LinuxTomcat启动失败 Linux和Windows两个方面如何执行一个定时任务? 探索Linux:命令行查看系统版本经验分享 跨境电商ERP源码的重要性以及如何选择合适的源码 掌握BusyBox:安装和使用技巧 linux内核与驱动 2016上海事业单位医疗招聘:杂项设备文件操作集分析 acm linux 如何参与编程竞赛并融入社区? (Linux基础知识)内核中定时器的数据类型分析 Linux操作系统安装指南:步骤与注意事项 某台服务器网卡的流量总结及解决办法总结! CentOS如何切换到图形界面? 基于iftop的iftop命令监控监控网卡的应用 centos login CentOS登录:轻松踏入Linux世界 suroot命令的用途、安全性和常见问题及用途
关于linux服务器搭建有什么方法?3个相关介绍成功安装Linux的3个简单的步骤来安装OSLinux系统结构GNU/Linux不同的发行版都存在深入探索Linux系统源码Windows、Linux和Unix是当今最流行的操作系统?centos unknown host CentOS主机找不到的解决方法mac安装部署mongoDB社区版-Ubuntu-20.04版本有三种方法Linux内存管理大揭秘Linux内核的GNU操作系统是什么?关键词Linux发行版关于linux系统学习的一些事儿,你了解多少?关于Linux设备驱动开发理论、框架与实例的说明Linux系统中安装中文语言包教程linux开发教程 Marvell88MC200及88MW300:C语言三剑客关于Linux操作系统的一些常见问题及解决办法Linux系统中的文件传输协议解析拷贝Linux系统的文件到U盘的解决方法(图文)Linux系统中如何修改语言环境为中文嵌入式软件开发人员如何学习C语言学习书籍?Linuxvi编辑器顺利退出技巧Linux系统服务器安装的全过程及准备工作详解!
联想B465C笔记本电脑安装问题解决方案 linux的关机命令和区别和各自的用法和解决办法 什么是嵌入式设备?设备驱动程序会是什么样子? Linux安装:分区要点一网打尽 RedHatEnterpriseLinux网络操作系统的应用进行了详细讲解 VisualStudioTools插件将添加对开发和调试Linux项目的支持 php开源cms系统的特点及特点后台分析:Php系统 红旗Linux桌面版60:安装更简单、更顺利 linux如何安装mysql Linux上安装MySQL,八招教你! U盘启动安装Linux,开启开源之旅! 文件描述符与索引节点的管理文件类型有哪些? centos firefox 升级 CentOS升级Firefox,安全又稳定! 精选Linux网络视频播放器对比 如何在CentOS上安装和配置KVM及WebVirtMgr及相关组件 Linux系统编码格式大揭秘 简信CRM:免费开源CRM系统的优缺点? 提高磁盘利用率,Linux必备的日常操作清单 使用jrnl记录日志,让你在新一年更加高效的环境 关于Linux内核工作的一些事儿,你了解多少? Linux系统压缩神器,速度快效果好
标签聚合
软件 linux系统 目录 操作 内核 linux服务器 应用 安装 文件 命令
书籍
课程
技术群
技术干货大合集↓
  • 2023年12月 / 10篇
  • 2023年11月 / 91篇
  • 2023年10月 / 125篇
  • 2023年9月 / 119篇
  • 2023年8月 / 124篇
  • 2023年7月 / 119篇
  • 2023年6月 / 120篇
  • 2023年5月 / 122篇
  • 2023年4月 / 69篇
友情链接:

Linux书籍 | Linux命令 | Linux系统 | RHCE红帽认证 | Linux软件 | Linux教程 | CentOS系统 | Linux内核 | Linux服务器 | Linux大神 | IT资源

COPYRIGHT © 2024 BestCentOS.com ALL RIGHTS RESERVED.

京ICP备14023444号-3