字符设备驱动

这里写代码片内核在fs.h文件中为驱动人员提供了一个现成的注册字符设备的接口register_chrdev,并在这里定义了它

static inline int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops)                
{
    return __register_chrdev(major, 0, 256, name, fops);
}

这个注册接口需要三个参数从左到右依次是主设备号、字符设备的名字、字符设备的文件操作函数集,这个接口又调用了另一个函数,首先说明一点,在内核中一共有256个主设备号

int __register_chrdev(unsigned int major, unsigned int baseminor,unsigned int count, const char *name,const struct file_operations *fops)                 
{
    struct char_device_struct *cd;
    struct cdev *cdev;
    int err = -ENOMEM;
    cd = __register_chrdev_region(major, baseminor, count, name);
    if (IS_ERR(cd))
        return PTR_ERR(cd);
    cdev = cdev_alloc();
    if (!cdev)
        goto out2;
    cdev->owner = fops->owner;
    cdev->ops = fops;
    kobject_set_name(&cdev->kobj, "%s", name);
    err = cdev_add(cdev, MKDEV(cd->major, baseminor), count);
    if (err)
        goto out;
    cd->cdev = cdev;
    return major ? 0 : cd->major;
}

这个函数比上一个多了两个参数,baseminor是该字符设备的起始次设备号,count表示该设备占用多少个次设备号,在这里起始次设备号为0,次设备号个数为256,在内核中是通过一个字符设备驱动的主设备号和次设备号来对它们进行区别和管理,因此内核为字符设备驱动提供了注册设备号的接口,如上先是调用__register_chrdev_region(major, baseminor, count, name);注册设备号

static struct char_device_struct *
__register_chrdev_region(unsigned int major, unsigned int baseminor, int minorct, const char *name)           
{
    struct char_device_struct *cd, **cp;
    int ret = 0;
    int i;
    cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);
    if (cd == NULL)
        return ERR_PTR(-ENOMEM);
    mutex_lock(&chrdevs_lock);
    /* temporary */
    if (major == 0) {//如果主设备号为0,则自动分配主设备号
        for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i--) {
            if (chrdevs[i] == NULL)
                break;
        }
        if (i == 0) {
            ret = -EBUSY;
            goto out;
        }
        major = i;
        ret = major;
    }
    cd->major = major;
    cd->baseminor = baseminor;
    cd->minorct = minorct;
    strlcpy(cd->name, name, sizeof(cd->name));
    i = major_to_index(major);
    for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)//判断该主设备号的次设备号是否已被使用
        if ((*cp)->major > major ||
            ((*cp)->major == major &&
             (((*cp)->baseminor >= baseminor) ||
              ((*cp)->baseminor + (*cp)->minorct > baseminor))))
            break;
    /* Check for overlapping minor ranges.  */
    if (*cp && (*cp)->major == major) {
        int old_min = (*cp)->baseminor;
        int old_max = (*cp)->baseminor + (*cp)->minorct - 1;
        int new_min = baseminor;
        int new_max = baseminor + minorct - 1;
        /* New driver overlaps from the left.  */
        if (new_max >= old_min && new_max <= old_max) {
            ret = -EBUSY;
            goto out;
        }
        /* New driver overlaps from the right.  */
        if (new_min <= old_max && new_min >= old_min) {
            ret = -EBUSY;
            goto out;
        }
    }
    cd->next = *cp;
    *cp = cd;
    mutex_unlock(&chrdevs_lock);
    return cd;
}

进来这个函数先分配一个结构struct char_device_struct *cd,在内核中用这么一个结构来描述一个设备驱动,这个结构在内核的描述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];

可见在描述这个结构的同时也定义了一个用来管理字符设备驱动的指针数组chrdevs,CHRDEV_MAJOR_HASH_SIZE为255,这就跟最大次设备号255对应上了,即一个主设备号最多只能有256个次设备号。然后判断主设备号是否为0,如果是0则从从字符设备管理指针数组中找出空的一项,并把该项的序号作为主设备号,再就是判断该次设备号在该主设备号中是否已被使用,未使用返回该字符设备的描述结构表示为该设备驱动分配设备号成功,看回上面,除了调用__register_chrdev_region分配设备号外,又分配另外一个结构struct cdev,该结构用来跟字符设备驱动的文件操作函数集进行绑定用的,并调用cdev_add添加进去,如果注册成功返回该驱动的主设备号。由上可知,注册一个字符设备驱动大致分为两步:
1、调用__register_chrdev_region注册设备号
2、分配一个struct cdev *结构,初始化该结构,并调用cdev_add添加进去
在最上面可以到,调用register_chrdev注册的字符设备驱动,虽然主设备号可以自动分配或者手动赋值,但是对于注册的该设备驱动,起始次设备号固定为0,占用256个次设备号,在实际应用中,往往不需要占用这么多的次设备号,以造成系统资源的浪费,因此内核又提供了两个接口供外部调用来自己注册设备号int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name)、int register_chrdev_region(dev_t from, unsigned count, const char *name),前者用来自动分配一个设备号,后者用来注册一个自己定义的设备号。在linux应用层中,是通过设备文件来对设备进行操作,因此只是注册设备显然是不行的,另外还需要生成一个设备文件来供上层应用访问。在linux中,创建一个设备驱动文件可调用struct device *device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, …)来创建一个设备文件,可见创建一个设备文件需要struct class *class结构、它的parent、设备号、自定义的一个数据结构(可以为空)、最后还可以传一个可变参数进来,内核提供了创建struct class *class结构的接口class_create。到此编写一个字符设备驱动程序归结为以下步骤:
1、注册一个设备号(可用接口alloc_chrdev_region,register_chrdev_region)
2、主设备号跟文件操作函数集的绑定(cdev_add) 注:以上两步可用register_chrdev代替(次设备号固定)
3、创建一个struct class结构(class_create)
4、创建设备驱动文件(device_create)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SQLAlchemy 是一个 SQL 工具包和对象关系映射(ORM)库,用于 Python 编程语言。它提供了一个高级的 SQL 工具和对象关系映射工具,允许开发者以 Python 类和对象的形式操作数据库,而无需编写大量的 SQL 语句。SQLAlchemy 建立在 DBAPI 之上,支持多种数据库后端,如 SQLite, MySQL, PostgreSQL 等。 SQLAlchemy 的核心功能: 对象关系映射(ORM): SQLAlchemy 允许开发者使用 Python 类来表示数据库表,使用类的实例表示表中的行。 开发者可以定义类之间的关系(如一对多、多对多),SQLAlchemy 会自动处理这些关系在数据库中的映射。 通过 ORM,开发者可以像操作 Python 对象一样操作数据库,这大大简化了数据库操作的复杂性。 表达式语言: SQLAlchemy 提供了一个丰富的 SQL 表达式语言,允许开发者以 Python 表达式的方式编写复杂的 SQL 查询。 表达式语言提供了对 SQL 语句的灵活控制,同时保持了代码的可读性和可维护性。 数据库引擎和连接池: SQLAlchemy 支持多种数据库后端,并且为每种后端提供了对应的数据库引擎。 它还提供了连接池管理功能,以优化数据库连接的创建、使用和释放。 会话管理: SQLAlchemy 使用会话(Session)来管理对象的持久化状态。 会话提供了一个工作单元(unit of work)和身份映射(identity map)的概念,使得对象的状态管理和查询更加高效。 事件系统: SQLAlchemy 提供了一个事件系统,允许开发者在 ORM 的各个生命周期阶段插入自定义的钩子函数。 这使得开发者可以在对象加载、修改、删除等操作时执行额外的逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值