Linux设备驱动基础03:Linux字符设备驱动

article/2025/11/10 0:23:48

1 Linux文件系统与设备驱动

1.1 文件系统与设备驱动之间的关系

Linux中文件系统与设备驱动程序之间的关系如下图所示,

  1. 应用程序和VFS之间的接口是系统调用;VFS和文件系统以及设备文件之间的接口是file_operations结构体中的成员函数,该结构体包含对文件进行打开、关闭、读写和控制的一系列成员函数

  1. 字符设备上层没有类似磁盘的ext2等文件系统,所以字符设备的file_operations结构体需要由字符设备驱动程序提供,这正是字符设备驱动的核心

  1. 对于块设备,有两种访问方式,

① 不通过文件系统直接访问块设备(e.g. 直接操作磁盘设备文件/dev/mmcblkx)

此时块设备的file_operations结构体使用Linux内核实现的def_blk_fops

② 通过文件系统访问块设备

此时块设备的file_operations结构体由相应的文件系统提供,文件系统会将针对文件的读写转换为针对块设备原始扇区的读写,下图为ext4文件系统实现的file_operations结构体

说明:用户空间看不到设备驱动,能看到的只有和设备对应的文件

1.2 核心数据结构

1.2.1 inode结构体

inode结构体是Linux管理文件系统的最基本单位,其中包含了文件节点的各种信息,

说明1:关于设备号

① 如果与inode节点对应的文件是设备文件(字符设备或块设备),则会使用i_rdev字段记录设备号

② 设备号类型为dev_t,共32位。Linux内核设备号分为主设备号和次设备号,前者为dev_t的高12位,后者为dev_t的低20位。主设备号是与驱动对应的概念,同一类设备一般使用相同的主设备号;因为同一驱动可以支持多个同类设备,因此使用次设备号来标识同类的不同设备(序号一般从0开始)

③ Linux内核提供了如下3个宏用于构成和分解设备号,

通过imajor和iminor函数则可以从inode中获取主次设备号,

④ 通过ls -l命令可以查看设备文件的主次设备号

⑤ 使用cat /proc/devices命令可以查看系统中已经使用的字符设备和块设备的主设备号

⑥ 内核源码中的Documents/admin-guide/devices.txt文件描述了Linux设备号的分配情况

devices.txt

说明2:关于__randomize_layout宏

在定义inode结构体类型时,使用了__randomize_layout宏进行修饰,这是GCC的一个plugin提供的特性。该特性可以对结构体进行混淆。在编译时,结构体中的数据不会按照声明顺序存储,而是根据函数名以及随机化种子打乱存储顺序,目的是防止黑客程序计算出结构体中关键值的偏移并进行修改

相关内容可参考randomize layout

1.2.2 file结构体

file结构体代表一个打开的文件,系统中每个打开的文件在内核空间都会有一个对应的file结构体。file结构体由内核在打开文件时创建,并传递给在文件上进行操作的任何函数(也就是file_operations结构体中的成员函数);在文件的所有实例都关闭后(以进程为单位),内核会释放file结构体

说明1:进程打开文件的file结构体会记录在TCB的file_struct结构体中,不同进程打开同一个文件,会生成各自的file结构体

其中分配file结构体的操作如下图所示,

说明2:关于文件标志f_flags

文件标志f_flags可以由如下宏位或构成,可以在调用open函数打开文件时设置,也可以通过fcntl函数设置

说明3:关于private_data字段

私有数据指针private_data在设备驱动中被广泛使用,大多数情况下用于指向设备驱动自定义的设备结构体。例如在misc子系统中,就将private_data字段指向misc子系统定义的miscdevice设备结构体

1.2.3 file_operations结构体

file_operations结构体中的成员函数是字符设备驱动程序设计的主体内容,这些函数会在应用程序调用open / close / read / write等系统调用时最终被内核调用。需要注意的是,不同版本内核的file_operations结构体会有所不同

2 Linux字符设备驱动结构

2.1 cdev结构体

在Linux内核中,使用cdev结构体描述字符设备。其中关于dev和count字段的理解,详见后文分析

说明1:Linux内核的编程习惯是定义一个设备相关的结构体,该结构体包含所涉及的cdev、私有数据以及锁等信息。在chrdev_basic示例程序中,就是将cdev结构体和所持有的内存资源打包为一个设备结构体

说明2:使用cdev_alloc函数可以动态申请一个cdev结构体,该函数会对cdev结构体中的部分成员进行初始化

2.2 核心操作

2.2.1 分配注册设备号

在向系统注册字符设备之前,需要先调用register_chrdev_regionalloc_chrdev_region向系统申请设备号,其中,

  1. register_chrdev_region函数用于已知起始设备号的情况

  1. alloc_chrdev_region函数用于设备号未知,向系统动态申请未被占用的设备号的情况

说明1:关于连续注册分配设备号的个数count

① 上述2个函数中的参数count均表示要连续注册/分配设备号的个数,此处是指次设备号的连续

② 假设注册分配的主设备号为A,次设备号为B,那么将会连续注册分配从MKDEV(A, B)到MKDEV(A, B + count - 1)的设备号

说明2:返回指针函数如何返回错误码?

上述2个函数都会调用__register_chrdev_region函数实际分配设备号,而该函数的返回值为structchar_device_struct *类型。如果在失败时只是返回NULL,则只能体现失败,不能说明失败的具体原因。因此Linux内核提供了如下3个函数,用于实现在返回指针类型的函数中返回错误码

说明3:chrdev_basic示例程序通过模块参数控制是使用指定的主设备号,还是向系统动态申请设备号

说明4:注销设备号通过unregister_chrdev_region函数实现

说明5:__register_chrdev_region函数分析

通过对__register_chrdev_region函数的分析,可以理解字符设备有效主设备号的个数以及对主设备号的组织方式

从__register_chrdev_region函数的实现可见,

① 虽然设备号中的高12位表示主设备号,但是内核支持的字符设备最大主设备号为511

② 字符设备的主设备号被组织为255个entry的哈希表

说明6:与字符设备类似,内核支持的块设备最大主设备号也是511,并且也被组织为255个entry的哈希表

2.2.2 初始化cdev结构体

cdev_init函数用于初始化cdev结构体成员,并建立cdev结构体和file_operations结构体之间的联系

2.2.3 注册字符设备

在完成cdev结构体的初始化之后,就可以调用cdev_add函数向系统中注册字符设备

说明1:关于count参数

① 此处count字段的含义与分配注册设备号时的count参数是类似的,一个cdev结构体可以支持多个主设备号相同的字符设备,count字段就表示一个cdev结构体可以支持的次设备号连续的设备个数

② 假设注册的cdev结构体的主设备号为A,次设备号为B,那么该cdev结构体可以支持设备号从MKDEV(A, B)到MKDEV(A, B + count -1)的字符设备

③ 这种"支持"体现在应用程序打开上述设备号对应的设备文件时(他们对应不同的inode结构体),会索引到相同的cdev结构体

说明2:由于chrdev_basic示例程序中每个设备结构体都包含一个cdev结构体,因此需要将这些cdev结构体逐个注册到系统中

说明3:注销字符设备通过cdev_del函数实现

说明4:Linux内核还提供了register_chrdev函数,该函数可以一次性完成注册设备号、分配cdev结构体、初始化cdev结构体和注册字符设备,而且是将指定主设备号下的所有256个次设备都关联到一个cdev结构体

通过register_chrdev函数注册的字符设备可以通过unregister_chrdev函数注销,

说明5:字符设备驱动的结构如下图所示,

说明6:字符设备驱动核心数据结构之间的关系如下图所示,

2.3 示例验证

2.3.1 open/release函数实现

为了验证chrdev_basic示例程序中注册字符设备的行为,此处先给出file_operations结构体中open和release函数的实现

说明:globalmem_open函数中的inode->i_cdev就是指向与该文件节点关联的cdev结构体,而该cdev结构体又被包含在设备结构体中。因此,通过container_of宏就可以索引到相应的设备结构体。关于container_of宏的实现,可参考01. 概述 chapter 6.2

2.3.2 手动创建设备节点

  1. 加载chrdev_basic.ko,可见分配到的主设备号为234

  1. 通过cat /proc/devices命令可以验证主设备号234确实被示例驱动程序占用

  1. 此时尚未建立设备节点,需要通过mknod命令手动创建

mknod  设备节点路径 设备类型 主设备号  次设备号

  1. 运行如下测试用例,可见运行符合预期,不同的inode节点可以索引到相应的cdev结构体

2.3.3 自动创建设备节点

  1. 如果想要实现在加载驱动时自动创建设备节点,则需要依靠Linux设备驱动模型及udev机制,本节仅说明使用方法(核心是创建设备类,并在该设备类上创建设备)

下面给出class_createdevice_create的函数原型,

  1. 加载chrdev_audodev.ko,可见设备节点被自动创建,运行测试用例结果与之前相同

  1. 加载chrdev_audodev.ko后,会创建/sys/class/globalmem目录,并且创建相应的sysfs设备文件

2.3.4 多个字符设备使用一个cdev结构体

  1. 在chrdev_basic和chrdev_autodev示例程序中,都是将cdev结构体包含在设备结构体之中,因此需要使用多个cdev结构体。但是这些cdev结构体关联的file_operations结构体是相同的,因此可以改为多个设备共用一个cdev结构体,chrdev_onecdev示例程序中的相关修改如下(在chrdev_autodev示例程序基础上修改),

① 将cdev结构体移出设备结构体,将其定义为全局变量

② 在调用cdev_add函数注册字符设备时,将多个设备号与一个cdev结构体关联起来

③ 由于设备结构体中不再包含cdev结构体,所以在open函数中改为通过inode中记录的子设备号来索引相应的设备结构体

  1. 加载chrdev_onecdev.ko,可见设备节点被自动创建,运行测试用例结果与之前相同

3 file_operations结构体基础实现示例

3.1 open函数

3.1.1 函数原型与功能

open函数用于打开设备文件,通常在open函数中进行设备和数据结构的初始化

/** inode: 与设备文件关联的inode结构体* file: 打开设备文件后创建的file结构体* 返回值: 成功应返回0,出错应返回错误码*/int open(struct inode *inode, struct file *file);

说明1:inode结构体代表文件系统中的一个文件(此处是字符设备文件),file结构体代表一个打开的文件,二者在chrdev_open函数中实现关联

结合上文,设备驱动向内核注册cdev结构体,并且标识他所关联的设备号;打开设备文件时,内核通过inode结构体记录的设备号索引相应的cdev结构体

其中,chrdev_open函数是内核默认的字符设备打开函数,在该函数中会调用驱动中注册的file_operations.open函数

说明2:如果打开的设备节点没有和cdev结构体关联,根据上文分析,应该会返回-ENXTO。下面先手动创建/dev/globalmem2设备节点,该节点并没有关联的cdev结构体,因此打开失败

说明3:从chrdev_open函数中可见,如果字符设备驱动不实现open函数,此时默认文件打开成功

3.1.2 示例实现

说明:需要注意的是,一个进程中多次打开同一个设备节点,每次打开都会创建新的file结构体,并且每次都会调用到open函数。作为验证,运行如下测试用例,并且在驱动中打印file结构体指针。可见每次打开操作的inode结构体相同(表示打开的是同一个设备文件),但是file结构体不同

3.2 release函数

3.2.1 函数原型与功能

release函数用于关闭设备文件,通常在release函数中释放资源,关闭设备

/** inode: 与设备文件关联的inode结构体* file: 打开的设备文件* 返回值: 可以始终返回0,因为内核不会判断release函数的返回值* 注意区分open系统调用和此处release函数的返回值*/int release(struct inode *inode, struct file *file);

说明1:只有当file结构体的引用计数f_count为0时,才会调用到release函数

说明2:关于file结构体引用计数维护

内核提供了get_file函数用于增加file结构体引用计数,fput函数用于减少file结构体引用计数

3.2.2 示例实现

3.3 read函数

3.3.1 函数原型与功能

read函数用于从设备中读取数据

/** file: 打开的设备文件* buf: 存储读取数据的用户态buffer* size: 要读取的数据长度* ppos: 指向当前读写位置的指针* 返回值:成功时应返回读取到的字节数,出错时应返回错误码*/ssize_t read(struct file * file, char __user *buf, size_t size, loff_t *ppos)

说明1:read函数调用关系

从调用关系中可见,如果read函数未被实现,当用户进行read系统调用时,会返回-EINVAL

说明2:__user宏

① address_space表示的空间如下,

0:内核空间

1:用户空间

2:IO空间

3:CPU空间

② noderef表示所修饰的变量必须是非解引用的,也就是必须是一个地址值

3.3.2 示例实现

说明:copy_to_user函数

① copy_to_user函数用于完成从内核空间到用户缓冲区的拷贝,函数原型如下,

② copy_to_user函数的返回值为不能被拷贝的字节数,因此,如果完全拷贝成功,返回值为0

③ 内核空间虽然可以访问用户空间的缓冲区,但是在访问之前,一般需要先进行合法性检查,以确定传入的缓冲区确实属于用户空间

④ 如果要拷贝到用户空间的是简单类型(e.g. char / int / long),可以使用put_user函数(经常用于unlocked_ioctl函数实现)。put_user函数成功时返回0,出错时返回-EFAULT

3.4 write函数

3.4.1 函数原型与功能

write函数用于向设备发送数据

/** file: 打开的设备文件* buf: 存储写入数据的用户态buffer* size: 要写入的数据长度* ppos: 指向当前读写位置的指针* 返回值:成功时应返回写入的字节数,出错时应返回错误码*/ssize_t write(struct file *file, const char __user *buf, size_t size, loff_t*ppos);

说明:write函数调用关系

从调用关系中可见,如果write函数未被实现,当用户进行write系统调用时,会返回-EINVAL

3.4.2 示例实现

说明:copy_form_user函数

① copy_from_user函数用于完成从用户缓冲区到内核空间的拷贝,函数原型如下,

② copy_from_user函数的返回值为不能被拷贝的字节数,因此,如果完全拷贝成功,返回值为0

③ copy_from_user也会对用户空间的缓冲区进行合法性检查

④ 如果要拷贝到内核空间的是简单类型(e.g. char / int / long),可以使用get_user函数(也是经常用于unlocked_ioctl函数实现)。get_user函数成功时返回0,出错时返回-EFAULT

需要特别注意的是,传递给get_user的x是一个存储读取数值的内核态变量,而不是指针

3.5 llseek函数

3.5.1 函数原型与功能

llseek函数用于修改文件的当前读写位置,并返回新的读写位置

/** file: 打开的设备文件* offset: 文件偏移量* orig: 文件偏移量计算基准,* 可以是SEEK_SET(基于文件开头) /  SEEK_CUR(基于文件当前位置) / SEEK_END(基于文件结尾)* 返回值: 成功时应返回新的文件读写位置,出错时应返回错误码*/loff_t llseek(struct file *file, loff_t offset, int orig);

说明1:llseek函数调用关系

从调用关系中可见,如果llseek函数未被实现,当用户进行lseek系统调用时,最终会调用no_llseek函数,该函数会返回-ESPIPE,即该设备不支持seek操作

说明2:对于不支持seek操作的设备文件(e.g. pipe文件),有如下2种处理方式,

① 在设备驱动的open函数中调用noseekable_open函数,清除file.f_mode中的FMODE_LSEEK标志

② 在设备驱动中将file_operations.llseek函数指针设置为no_llseek函数

3.5.2 示例实现

3.5.3 read / write / llseek函数示例验证

运行如下测试用例,可见运行结果符合预期

说明1:测试用例中的lseek操作必不可少,因为write操作会修改文件的读写位置,需要lseek到文件的开始位置才能读取到write操作写入的数据

说明2:将copy_to/from_user改为memcpy会怎样?

经过验证修改后的驱动程序可以工作,但是此时不会检查用户态地址的合法性

3.6 unlocked_ioctl函数

3.6.1 函数原型与功能

unlocked_ioctl函数用于提供设备控制命令

/** file: 打开的设备文件* cmd: 控制命令* arg: 对应于控制命令的参数* 返回值: 成功时应返回0,出错时应返回错误码*/long unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg);

说明1:unlocked_ioctl函数调用关系

① 从调用关系中可见,如果unlocked_ioctl函数未被实现,当用户进行ioctl系统调用时,会返回-ENOTTY

② 函数名之所以称作unlocked_ioctl,是因为在调用过程中内核不会持有锁。如果需要锁机制,需要在设备驱动注册的unlocked_ioctl函数中实现

说明2:控制命令的构成

① 从使用的角度,控制命令只需要在设备驱动和应用程序之间约定好即可,但是简单的命令定义方式会导致不同的设备驱动拥有相同的命令码

② 传递控制命令的参数cmd本质上是一个4B整数,Linux内核建议以如下方式构建控制命令,并提供了相应的宏定义。其中数据传送的方式是从应用程序角度来看的,即_IO(无数据传输)、_IOCR(读)、_IOCW(写)和_IOCWR(双向)

③ 使用_IOR / _IOW / _IOWR宏定义控制命令时,size字段只需要传递相关参数类型即可,参数尺寸将由这些宏内部调用的_IOC_TYPECHECK宏计算

说明3:从do_vfs_ioctl函数可见,内核中预定义了一些控制命令(e.g.FIOCLEX)。如果设备驱动中包含了与预定义命令一样的命令码,这些命令会作为预定义命令被内核处理,而不是被设备驱动处理

说明4:关于控制命令参数

控制命令参数为insigned long类型,可以传递值,也可以传递用户态指针

3.6.2 示例实现

说明:由于unlocked_ioctl函数可能需要在用户态和内核态之间拷贝控制命令参数,因此内核中的一些框架会对其进行封装,以便统一处理。以V4L2框架为例,由video_usercopy函数实现控制命令的处理框架

需要注意的是,这种框架的实现严格依赖内核提供的控制命令定义方式,只有正确传递控制命令参数的大小,该处理框架才能正常工作

3.6.3 示例验证

运行如下测试用例,可见运行结果符合预期

3.7 mmap函数

3.7.1 函数原型与功能

mmap函数用于将设备内存映射到进程的虚拟地址空间中

/** file: 打开的设备文件* vma: 内核分配的虚拟存储区,也就是要映射到进程虚拟地址空间中的目标范围* 返回值: 成功时应返回0,出错时应返回错误码*/int mmap(struct file *file, struct vm_area_struct *vma);

说明:mmap函数调用关系

从调用关系中可见,如果mmap函数未被实现,当用户进行mmap系统调用时,会返回-ENODEV

3.7.2 示例实现

说明:关于mmap系统调用的实现,可参考Linux操作系统原理与应用04:内存管理 chapter 7

3.7.3 示例验证

运行如下测试用例,可见运行结果符合预期。需要注意的是,测试用例需要对映射后的内存进行读写操作,因此映射权限必须具有读写权限,而映射模式必须是共享模式(否则strcpy写入的数据,不会真正写入globalmem设备持有的内存中)

4 misc子系统分析

4.1 概述

  1. Linux中的大部分设备驱动都是以字符设备驱动的形式存在,但是内核的字符设备驱动框架提供的功能非常简单,着重于定义VFS与设备驱动的接口。因此在实际使用中,内核的不同子系统会根据需要,在字符设备的基础上实现一个框架来抽象这类设备的共性操作(e.g. misc子系统、V4L2子系统)

  1. misc子系统是在字符设备驱动基础上构建的最简单抽象,一般用于实现无法归类的杂项设备驱动。通过分析misc子系统,可以初步理解Linux内核中驱动分层设计的思想

4.2 miscdevice结构体

在Linux内核中,使用miscdevice结构体描述misc设备,

说明:miscdevice结构体中的parent字段指向misc设备在Linux设备模型中的父设备,该父设备一般是misc设备对应的硬件设备(一般体现为Linux设备模块中的platform设备)

4.3 核心操作

4.3.1 misc子系统初始化

misc_init函数为misc子系统的初始化函数,该函数以subsys_initcall(".initcall4.init")等级在系统初始化过程中被调用

说明1:misc_init函数中注册了/proc/misc文件,读取该文件可以获得当前系统中注册的misc设备

说明2:misc设备的主设备号为10,并且一个cdev结构体关联了从MKDEV(10, 0)到MKDEV(10, 255)的设备节点

通过misc子系统实现字符设备驱动,就可以避免为驱动定义一个专门的主设备号,从而减少对主设备号资源的浪费

说明3:misc_fops结构体是所有misc设备共用的设备文件操作函数集,其中只实现了open和llseek函数。之所以可以这么做,是因为后续会使用各个misc设备自己的file_operations结构体替换file结构体中的f_op指针指向,详见后文分析

说明4:misc_devnode设备注册在class结构体中,共Linux设备驱动模型devtmpfs使用

4.3.2 注册misc设备

misc_register函数用于向内核注册misc设备,并且利用Linux设备模块自动创建设备节点

说明1:misc_register函数是在misc类上创建设备,因此每个注册的设备会以一个目录的形式出现在/sys/class/misc/目录下

说明2:注册misc设备实例

在Linux内核的watchdog子系统中,当要注册的watchdog_device.id为0时,会注册watchdog_miscdev设备

说明3:注销misc设备通过misc_deregister函数实现

4.3.3 打开misc设备

打开misc设备节点时,会调用misc子系统注册的misc_open函数

说明1:虽然与misc子系统注册的cdev结构体关联的file_operations结构体中只实现了open和llseek函数,但是misc_open函数中将file结构体的f_op指针指向了miscdevice结构体中注册的file_operations结构体,所以可以通过新指向的file_operations结构体支持其他在设备文件上进行的系统调用

说明2:每次打开misc设备的open系统调用都会调用到misc_open函数,因为该函数所在的misc_fops结构体与misc子系统注册的cdev结构体关联

说明3:misc子系统核心数据结构之间的关系如下图所示,

4.4 示例实现

将上一章的示例程序修改为misc设备,需要对如下函数进行修改,

  1. 设备结构体

设备结构体中不再封装cdev结构体,而是miscdevice结构体

  1. 模块加载函数

在模块加载函数中不再注册字符设备,而是设置设备结构体中封装的miscdevice结构体,并且调用misc_register函数进行注册。可见使用misc子系统确实简化了字符设备驱动的实现

  1. 模块卸载函数

  1. open函数

在open函数实现中,通过misc_open函数传递的miscdevice结构体指针索引到设备结构体

4.5 示例验证

  1. 加载chrdev_misc.ko之后,

① /sys/class/misc/目录下生成globalmem的设备目录

② /dev目录下生成globalmem设备节点

  1. 运行如下测试用例,可见运行结果符合预期

说明:关于miscdevice结构体name字段的设置

① 上述示例实现中,2个miscdevice结构体的name字段是共用一个字符数组设置的

这点虽然在创建misc设备节点的过程中没有问题,但是在通过cat /proc/misc命令列出当前系统中的misc设备时,会触发段错误

② 之所以会发生段错误,是因为设置miscdevice结构体name字段的字符数组是在模块加载函数的栈中,这片内存会在模块加载函数执行完成后被释放。因此当cat /proc/misc命令要访问这段内存时,会触发段错误

③ 其实结合cat /proc/misc命令的打印方式,2个miscdevice结构体共用一个字符数组也是不合理的

④ 改为使用字符串字面值设置miscdevice结构体的name字段之后,cat /proc/misc命令可正常执行(当然也可以改为使用全局数组来设置name字段)

⑤ 这种直接使用字符串字面值设置name字段的方式比较缺乏灵活性,一种可行的方案是将存储misc设备的name的字符数组定义在设备结构体中。然后在模块加载函数中设置设备结构体中的字符数组,并使用该数组设置miscdevice结构体的name字段

经过验证,该方案是可行的,


http://chatgpt.dhexx.cn/article/gQ9XRRJq.shtml

相关文章

Linux 设备驱动程序(二)

系列文章目录 Linux 内核设计与实现 深入理解 Linux 内核 Linux 设备驱动程序(一) Linux 设备驱动程序(二) Linux 设备驱动程序(三) Linux 设备驱动程序(四) Linux设备驱动开发详解 …

Linux驱动——字符设备驱动

目录 一、代码编译环境 二、Linux驱动开发分类 三、字符设备驱动开发流程 1.编译驱动模块代码(Makefile) 2.驱动模块的加载与卸载 四、字符设备驱动编写 1.驱动模块 2. 字符设备驱动编写步骤 2.1 设备号的注册与注销 2.2 设备节点的注册与注销 …

【Linux设备驱动】设备驱动分类

Linux设备驱动分类 驱动分为有操作系统设备驱动和误操作系统的设备驱动。 有无操作系统设备驱动 无操作系统设备驱动 不是所有的计算机系统都一定要有操作系统,在许多情况下,操作系统都是不必存在的。对于功能比较单一、控制不复杂的系统&#xff0c…

Linux-设备驱动概述

文章目录 Linux设备驱动概述1. 设备驱动的作用2. 无操作系统的设备驱动3. 有操作系统时的设备驱动4. Linux设备驱动4.1 设备的分类及特点4.2 Linux设备驱动与整个软硬件系统的关系4.3 Linux设备驱动的重难点 5. 源代码阅读6. 设备驱动:LED驱动6.1 无操作系统的LED驱…

Linux设备驱动和设备匹配过程

Linux设备驱动和设备匹配过程 1. 设备驱动匹配简述2. 重点结构体介绍2.1 struct device2.2 struct platform_device2.3 struct platform_driver2.4 struct device_driver 3. device端发起匹配:3.1 流程图3.2 start_kernel时候解析设备树3.2.1 start_kernel3.2.2 set…

Linux设备驱动之字符设备驱动

一、linux系统将设备分为3类:字符设备、块设备、网络设备。 应用程序调用的流程框图: 三种设备的定义分别如下, 字符设备:只能一个字节一个字节的读写的设备,不能随机读取设备内存中的某一数据,读取数据需…

Linux设备驱动基础01:Linux设备驱动概述

目录 1. 设备驱动的作用 2. 有无操作系统时的设备驱动 2.1 无操作系统 2.1.1 硬件、驱动和应用程序的关系 2.1.2 单任务软件典型架构 2.2 有操作系统 2.2.1 硬件、驱动、操作系统和应用软件的关系 3. Linux设备分类 3.1 常规分类法 3.1.1 字符设备 3.1.2 块设备 3.…

Linux设备驱动模型

目录 一、linux设备驱动模型简介1、什么是设备驱动模型2、为什么需要设备驱动模型3、驱动开发的2个点 二、设备驱动模型的底层架构1、kobject2、kobj_type3、kset 三、总线式设备驱动组织方式1、总线2、设备3、驱动4、类5、总结 四、platform平台总线工作原理1、何为平台总线2、…

【linux内核分析与应用-陈莉君】字符设备驱动

目录 1.什么是字符设备 2.如何来描述字符设备 3 struct cdev与const struct file_operations之间的关系 4.struct file_operations源码 5.字符设备驱动框架 6.编写字符设备驱动的步骤 7.字符设备结构 8.字符设备驱动程序的注册 9.从系统调用到驱动程序 10.用户空间与内…

微信小程序云数据库使用讲解

第一步:注册开通 单击云开发 第二步:创建数据库 选择数据库,并点击号创建一个集合 输入名字 创建完毕后点击添加记录即可添加数据 数据库获取: 查询: 查询指令:

微信小程序云开发入门-数据库插入数据(包含批量)

一、前言 文章将介绍如何在微信小程序云开发中向云开发数据库插入数据(单条或批量)。 写法有好几种,文章将会一一进行对比,看看每种写法之间有何优缺点,如何让代码看起来更优雅。 为了更加贴合实际的开发逻辑&#xf…

【微信小程序】如何获取微信小程序云开发数据库的数据并渲染到页面?

前言 上一篇博客我把微信小程序云开发数据库操作(增删改查)的实现方法都已经分享出来啦,可以戳链接进去阅读哦 【微信小程序】小程序云开发实现数据库增删改查(小白速度Get起来!!一步步教你如何实现) 基于微信小程序…

开发一个可以查询并显示数据库内容的微信小程序

使用微信开发者工具可以创建云数据库,并通过代码可以查询并在客户端显示数据库的内容。 附:小程序一个功能页面有wxml(客户端呈现),js(功能函数),json,wxss(个性化处理),这些是局部的文件。还有全局的文件如…

微信小程序开发---连接云开发数据库,实现数据获取

之前几篇博客里面都详细交代了如何配置云函数,现在就讲一下关于云函数中数据库的使用,主要是讲如何从云开发平台的数据库中调取数据 我们直接来到需要调用数据库数据的页面的js文件,直接设置全局变量,来便于后续调用数据库 const …

微信小程序查询数据库

微信小程序云开发的官方例子: const db wx.cloud.database() //获取数据库的引用 const _ db.command //获取数据库查询及更新指令 db.collection("china") //获取集合china的引用.where({ //查询的条件指令wheregdp: _.gt(3000) …

微信小程序云数据库操作

微信小程序云数据库操作 1、云数据库简介1.1 数据类型Date地理位置Null 1.2 权限控制 2、云数据库操作2.1 查询数据2.1.1 通过collection.doc获取一条记录2.1.2 通过collection.get获取所有记录的数据2.1.3 通过document.get获取某一条记录的数据2.1.4 通过collection.count获取…

微信小程序取本地数据库数据(实测有图)

测试效果如下: 本实验主要分为如下几个步骤: 一、安装数据库 二、安装PHPApache 三、编辑微信小程序代码 前两项的简单介绍在如下连接: PHPApache 四、本文主要介绍第三项的内容 需要用到的文件如下: 1、新建微信小程序工程 2、…

微信小程序云开发(数据库)

开发者可以使用云开发开发微信小程序、小游戏,无需搭建服务器,即可使用云端能力。 云开发为开发者提供完整的云端支持,弱化后端和运维概念,无需搭建服务器,使用平台提供的 API 进行核心业务开发,即可实现快…

微信小程序云开发(云数据库篇)

微信小程序云开发[云数据库篇] 云数据库关系型数据库和 JSON 数据库对比数据类型数据库操作联表查询事务处理 云数据库 云开发提供了一个 JSON 数据库,顾名思义,数据库中的每条记录都是一个 JSON 格式的对象。 一个数据库可以有多个集合(相当…

微信小程序云开发 1 - 数据库

微信小程序云开发最重要的有两点: 1、云数据库; 2、云函数; 学会这两点基本就能够进行微信小程序的云开发; 首先,我们先看微信小程序云数据库的基本操作: 1)打开微 信开发者工具&#xff0…