linux内核去掉设备驱动,linux内核之字符设备驱动图解

字符设备驱动分层结构图

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

704aaa76ffd368c050e6b5810fc75531.png

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

在 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 指针,它可以指向与其主设备号相同的其它字符设备驱动程序,它们之间主设备号相同,各自的次设备号范围相互不重叠。

04aab4f86b0e6e2bc4f5b17dd9879271.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)

{

ssize_t ret;

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.o

else

KERNELDIR?=/lib/modules/$(shell uname -r)/build

PWD:=$(shell pwd)

default:

$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

endif

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

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

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值