博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
linux内核之字符设备驱动图解
阅读量:6229 次
发布时间:2019-06-21

本文共 3320 字,大约阅读时间需要 11 分钟。

hot3.png

字符设备驱动分层结构图

         下图展示的是字符设备在打开(open)时的调用细节流程图,主要关注点是file_operations结构体的绑定操作。

         

字符设备在内核空间的组织结构图

      在 2.4 的内核我们使用 register_chrdev(0, "hello", &hello_fops) 来进行字符设备设备节点的分配,这种方式每一个主设备号只能存放一种设备,它们使用相同的 file_operation 结构体,也就是说内核最多支持 256 个字符设备驱动程序。

      在 2.6 的内核之后,新增了一个 register_chrdev_region 函数,它支持将同一个主设备号下的次设备号进行分段,每一段供给一个字符设备驱动程序使用,使得资源利用率大大提升,同时,2.6 的内核保留了原有的 register_chrdev 方法。在 2.6 的内核中这两种方法都会调用到 __register_chrdev_region 函数。

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];

      内核中的每一个字符设备驱动程序都由一个 char_device_struct 结构体来描述,包含主设备号、起始次设备号、次设备号个数等信息。

      内核使用 chrdevs 这个指针数组来管理所有的字符设备驱动程序,数组范围 0-255 ,看上去好像还是只支持 256 个字符设备驱动程序,其实并不然,每一个 char_device_struct 结构都含有一个 next 指针,它可以指向与其主设备号相同的其它字符设备驱动程序,它们之间主设备号相同,各自的次设备号范围相互不重叠。

                101804_si9o_2896894.png

字符设备驱动模板

#include 
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* 1. 确定主设备号 */ static int major; static int hello_open(struct inode *inode, struct file *file) { printk("hello_open\n"); return 0; } /*读函数*/static ssize_t hello_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos){ char data[] = "hello world." /*读数据到用户空间:内核空间->用户空间交换数据*/ if (copy_to_user(buf, (void*)data, sizeof(data))) { ret = - EFAULT; } return ret;} /* 2. 构造file_operations */ static struct file_operations hello_fops = { .owner = THIS_MODULE, .open = hello_open, .read = hello_read,}; #define HELLO_CNT 2 static struct cdev hello_cdev; static struct class *cls; static int hello_init(void) { dev_t devid; /* 3. 告诉内核 */ #if 0 // 老方法 /* (major, 0), (major, 1), ..., (major, 255)都对应hello_fops */ major = register_chrdev(0, "hello", &hello_fops); #else // 新方法 if (major) { devid = MKDEV(major, 0); /* (major,0~1) 对应 hello_fops, (major, 2~255)都不对应hello_fops */ register_chrdev_region(devid, HELLO_CNT, "hello"); } else { /* (major,0~1) 对应 hello_fops, (major, 2~255)都不对应hello_fops */ alloc_chrdev_region(&devid, 0, HELLO_CNT, "hello"); major = MAJOR(devid); } cdev_init(&hello_cdev, &hello_fops); cdev_add(&hello_cdev, devid, HELLO_CNT); #endif cls = class_create(THIS_MODULE, "hello"); device_create(cls, NULL, MKDEV(major, 0), NULL, "hello0"); /* /dev/hello0 */ device_create(cls, NULL, MKDEV(major, 1), NULL, "hello1"); /* /dev/hello1 */ return 0; } static void hello_exit(void) { device_destroy(cls, MKDEV(major, 0)); device_destroy(cls, MKDEV(major, 1)); class_destroy(cls); cdev_del(&hello_cdev); unregister_chrdev_region(MKDEV(major, 0), HELLO_CNT); } module_init(hello_init); module_exit(hello_exit); MODULE_LICENSE("GPL");

 下面是Makefile

ifneq ($(KERNELRELEASE),)     obj-m:=hello.oelse     KERNELDIR?=/lib/modules/$(shell uname -r)/build     PWD:=$(shell pwd)default:     $(MAKE) -C $(KERNELDIR) M=$(PWD) modulesendif

下面可以写一个应用程序测试一下:

#include 
int main(){ FILE *fp = NULL; char buf[4096]; /*打开设备文件*/ fp = fopen("/dev/hello0","r+"); if (fp == NULL) { return -1; } /*读设备*/ fread(buf, sizeof(buf), 1, fp); /*检测结果*/ printf("buf: %s\n",buf); return 0; }

 

 

转载于:https://my.oschina.net/fileoptions/blog/951984

你可能感兴趣的文章
原理剖析(第 006 篇)Semaphore工作原理分析
查看>>
Java基础查漏补缺:(开篇)为什么要在即将找工作的时候还在看Java基础
查看>>
VXWORKS关于任务创建的几个函数概述
查看>>
破解candy crush过程
查看>>
corosync+pacemaker+drbd构建web高可用集群
查看>>
年计划,技术儿告诉你怎么做?
查看>>
VCT-Virtual Cable Test-虚拟电缆检测
查看>>
Java ibatis调用存储过程出现阻塞
查看>>
例解三层交换原理
查看>>
java-第十一章-类的无参方法-实现菜单的级联效果
查看>>
如何检测集群中每台主机的状态
查看>>
时针、分针在一昼夜 24 小时内重合多少次?
查看>>
PHP 5 常量
查看>>
第44讲:Scala中View Bounds代码实战及其在Spark中的应用源码解析
查看>>
react的style里面不支持important的解决办法
查看>>
JS基本问题
查看>>
我的第一篇博客
查看>>
php版本之殇
查看>>
IDEA 葵花宝典
查看>>
IDEA 问题汇总
查看>>