linux 字符设备分析

字符设备特性:

1、字符读写:字符设备处理的是字节流数据

2、顺序访问:字符设备顺序访问,不支持随机读写

3、无缓冲区:字符设备通常是无缓冲区,读写一般都是实时的,也可以带缓冲,和底层实现有关

常见的字符设备:

串口,鼠标,键盘

字符设备使用

字符设备实现流程:

请参照内核源码中任意字符设备实现即好,主要分为以下三个部分

1、创建并注册字符设备,比如调用register_chrdev 函数 

2、class 类创建 class_create

3、device 创建 device_create

后面两部分主要是创建sysfs 文件系统模型,以便于 mdev 机制 查询sys/class 中的dev 设备并调用mknod 自动创建dev 节点。

本文不做 mdev 机制和syfs 分析,这块留待后续进行

字符设备接口

源码实现文件路径
fs/char_dev.c

include/linux/fs.h

字符设备注册接口:

向char_device_struct 发起注册

指定设备号注册     

register_chrdev_region(dev_t from, unsigned count,const char * name)

自动分配设备号

alloc_chrdev_region(dev_t *dev,unsigned baseminor,unsigned count,const char name)

此两个接口底层都依赖于__register_chardev_region

向cdev_map 发起注册

cdev 结构体初始化,并绑定ops 

cdev_init(struct cdev* cdev,const struct file_operationos* fops)

向cdev 注册到cdev_map 中的hash 表中

cdev_add(struct cdev*p ,dev_t dev,unsigned count)

字符设备卸载

其中 查找匹配 ,必须是主设备号 major ,baseminor ,minorct  一致

unregister_chardev_region(dev_t from,unsigned count)

unregister_chardev(unsigned int major,const char* name)

device 方式加入到cdev ,暂时未分析

cdev_device_add(struct cdev* cdev,struct device*dev)

字符设备管理结构分析

设备号含义和构成

设备号有主设备号和次设备号组成,主设备号表示某一个具体驱动,次设备号代表使用此驱动的各个设备

dev_t 表示设备号,高12bit 代表主设备号,低20 代表次设备好

问题:其中管理字符设备有两套结构进行管理,一个是char_dev_struct ,另外一个是kobj_map,其数据结构都是采用hash 结构进行管理,为什么需要两套呢,原因暂未分析,猜测可能和兼容有关

字符管理结构chardev,主要管理设备号的分配和使用,后续真实使用的都是cdev_map

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

字符设备管理结构 cdev_map,实现对cdev 的管理

struct kobj_map {

    struct probe {

        struct probe *next;

        dev_t dev;

        unsigned long range;

        struct module *owner;

        kobj_probe_t *get;

        int (*lock)(dev_t, void *);

        void *data;

    } *probes[255];

    struct mutex *lock;

};

字符设备结构cdev

struct cdev {

    struct kobject kobj;

    struct module *owner;

    const struct file_operations *ops;

    struct list_head list;

    dev_t dev;

    unsigned int count;

};

字符设备和应用层的绑定

        节点创建

1、使用mknod 创建节点,其中内部创建字符设备文件,inode 节点,完成一下内容绑定

        inode->f_op = &def_chr_fops;     

        node->i_rdev = rdev;

          用于调用vfs 虚拟文件系统层 open 时候 ,打开对应类型(字符)设备的ops,def_chr_fops         通过设备号信息(dev_t )在字符设备管理结构中找到具体设备的操作的fops

具体mknod分析流程参考一下博客,比较详细

open 函数调用流程如下

        vfs_open

          ----  chrdev_open

           -----  cdev->ops->open

        本文只分析 chrdev_open 函数的行为,从c 函数系统调用到 vfs_open 到 char_open 流程本次不做分析,可以以参考此博客:Linux中open命令实现原理以及源码分析-CSDN博客

        chrdev_open 函数,代码如下,具体流程主要分为一下几个步骤

        1、查看inode->i_cdev  节点是否挂载了cdev 设备,挂载了直接取出cdev->ops ,挂载到file->fop 上,调用 filp->f_op->open 真实字符设备打开函数,打开设备。如果inode->i_cdev 节点是null 则根据设备号i_rdev 在cdev_map 中找到对应的cdev 字符设备,并将其挂载到 i_devices 链表上,接着用cdev 中的ops 替换 filp 中的f_op ,并打开字符设备

      

/*
 * Called every time a character special file is opened
 */
static int chrdev_open(struct inode *inode, struct file *filp)
{
	const struct file_operations *fops;
	struct cdev *p;
	struct cdev *new = NULL;
	int ret = 0;

	spin_lock(&cdev_lock);
	p = inode->i_cdev;
	if (!p) {
		struct kobject *kobj;
		int idx;
		spin_unlock(&cdev_lock);
		kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);
		if (!kobj)
			return -ENXIO;
		new = container_of(kobj, struct cdev, kobj);
		spin_lock(&cdev_lock);
		/* Check i_cdev again in case somebody beat us to it while
		   we dropped the lock. */
		p = inode->i_cdev;
		if (!p) {
			inode->i_cdev = p = new;
			list_add(&inode->i_devices, &p->list);
			new = NULL;
		} else if (!cdev_get(p))
			ret = -ENXIO;
	} else if (!cdev_get(p))
		ret = -ENXIO;
	spin_unlock(&cdev_lock);
	cdev_put(new);
	if (ret)
		return ret;

	ret = -ENXIO;
	fops = fops_get(p->ops);
	if (!fops)
		goto out_cdev_put;

	replace_fops(filp, fops);
	if (filp->f_op->open) {
		ret = filp->f_op->open(inode, filp);
		if (ret)
			goto out_cdev_put;
	}

	return 0;

 out_cdev_put:
	cdev_put(p);
	return ret;
}

  

创建

参考资料:

https://zhuanlan.zhihu.com/p/577974561

Linux中mknod命令实现原理以及源码分析-CSDN博客

Linux中mknod命令实现原理以及源码分析-CSDN博客

        

  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值