1 Linux字符设备架构是如何实现的-德赢Vwin官网 网
0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Linux字符设备架构是如何实现的

电子设计 来源:电子设计 作者:电子设计 2020-12-24 18:12 次阅读

一、Linux设备分类

Linux系统为了管理方便,将设备分成三种基本类型:

字符设备块设备网络设备字符设备:

字符(char)设备是个能够像字节流(类似文件)一样被访问的设备,由字符设备驱动程序来实现这种特性。字符设备驱动程序通常至少要实现open、close、read和write的系统调用。

字符终端(/dev/console)和串口(/dev/ttyS0以及类似设备)就是两个字符设备,它们能很好的说明“流”这种抽象概念。

字符设备可以通过文件节点来访问,比如/dev/tty1和/dev/lp0等。这些设备文件和普通文件之间的唯一差别在于对普通文件的访问可以前后移动访问位置,而大多数字符设备是一个只能顺序访问的数据通道。然而,也存在具有数据区特性的字符设备,访问它们时可前后移动访问位置。例如framebuffer就是这样的一个设备,app可以用mmap或lseek访问抓取的整个图像。

在/dev下执行ls -l ,可以看到很多创建好的设备节点:

字符设备文件(类型为c),设备文件是没有文件大小的,取而代之的是两个号码:主设备号5 +次设备号1 。

块设备:

和字符设备类似,块设备也是通过/dev目录下的文件系统节点来访问。块设备(例如磁盘)上能够容纳filesystem。在大多数的Unix系统中,进行I/O操作时块设备每次只能传输一个或多个完整的块,而每块包含512字节(或2的更高次幂字节的数据)。

Linux可以让app像字符设备一样地读写块设备,允许一次传递任意多字节的数据。因此,块设备和字符设备的区别仅仅在于内核内部管理数据的方式,也就是内核及驱动程序之间的软件接口,而这些不同对用户来讲是透明的。在内核中,和字符驱动程序相比,块驱动程序具有完全不同的接口。

块设备文件(类型为b):

网络设备:

任何网络事物都需要经过一个网络接口形成,网络接口是一个能够和其他主机交换数据的设备。接口通常是一个硬件设备,但也可能是个纯软件设备,比如回环(loopback)接口。

网络接口由内核中的网络子系统驱动,负责发送和接收数据包。许多网络连接(尤其是使用TCP协议的连接)是面向流的,但网络设备却围绕数据包的传送和接收而设计。网络驱动程序不需要知道各个连接的相关信息,它只要处理数据包即可。

由于不是面向流的设备,因此将网络接口映射到filesystem中的节点(比如/dev/tty1)比较困难。

Unix访问网络接口的方法仍然是给它们分配一个唯一的名字(比如eth0),但这个名字在filesystem中不存在对应的节点。内核和网络设备驱动程序间的通信,完全不同于内核和字符以及块驱动程序之间的通信,内核调用一套和数据包相关的函数socket,也叫套接字。

查看网络设备使用命令ifconfig:

二、字符设备架构是如何实现的?

在Linux的世界里面一切皆文件,所有的硬件设备操作到应用层都会被抽象成文件的操作。我们知道如果应用层要访问硬件设备,它必定要调用到硬件对应的驱动程序。Linux内核中有那么多驱动程序,应用层怎么才能精确的调用到底层的驱动程序呢?

在这里我们字符设备为例,来看一下应用程序是如何和底层驱动程序关联起来的。必须知道的基础知识:

1.在Linux文件系统中,每个文件都用一个struct inode结构体来描述,这个结构体里面记录了这个文件的所有信息,例如:文件类型,访问权限等。

2.在Linux操作系统中,每个驱动程序在应用层的/dev目录下都会有一个设备文件和它对应,并且该文件会有对应的主设备号和次设备号。

3.在Linux操作系统中,每个驱动程序都要分配一个主设备号,字符设备的设备号保存在struct cdev结构体中。

struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;//接口函数集合
struct list_head list;//内核链表
dev_t dev; //设备号
unsigned int count;//次设备号个数
};
4.在Linux操作系统中,每打开一次文件,Linux操作系统在VFS层都会分配一个struct file结构体来描述打开的这个文件。该结构体用于维护文件打开权限、文件指针偏移值、私有内存地址等信息。

注意:

常常我们认为struct inode描述的是文件的静态信息,即这些信息很少会改变。而struct file描述的是动态信息,即在对文件的操作的时候,struct file里面的信息经常会发生变化。典型的是struct file结构体里面的f_pos(记录当前文件的位移量),每次读写一个普通文件时f_ops的值都会发生改变。

这几个结构体关系如下图所示:

通过上图我们可以知道,如果想访问底层设备,就必须打开对应的设备文件。也就是在这个打开的过程中,Linux内核将应用层和对应的驱动程序关联起来。

1.当open函数打开设备文件时,可以根据设备文件对应的struct inode结构体描述的信息,可以知道接下来要操作的设备类型(字符设备还是块设备)。还会分配一个struct file结构体。

2.根据struct inode结构体里面记录的设备号,可以找到对应的驱动程序。这里以字符设备为例。在Linux操作系统中每个字符设备有一个struct cdev结构体。此结构体描述了字符设备所有的信息,其中最重要一项的就是字符设备的操作函数接口。

3.找到struct cdev结构体后,Linux内核就会将struct cdev结构体所在的内存空间首地记录在struct inode结构体的i_cdev成员中。将struct cdev结构体的中记录的函数操作接口地址记录在struct file结构体的f_op成员中。

4.任务完成,VFS层会给应用层返回一个文件描述符(fd)。这个fd是和struct file结构体对应的。接下来上层的应用程序就可以通过fd来找到strut file,然后在由struct file找到操作字符设备的函数接口了。

三、字符驱动相关函数分析*

* cdev_init() - initialize a cdev structure
* @cdev: the structure to initialize
* @fops: the file_operations for this device

* Initializes @cdev, remembering @fops, making it ready to add to the
* system with cdev_add().

void cdev_init(struct cdev *cdev, const struct file_operations *fops)
功能:
初始化cdev结构体
参数
@cdev cdev结构体地址
@fops 操作字符设备的函数接口地址
返回值:


* register_chrdev_region() - register a range of device numbers
* @from: the first in the desired range of device numbers; must include
* the major number.
* @count: the number of consecutive device numbers required
* @name: the name of the device or driver.

* Return value is zero on success, a negative error code on failure.

int register_chrdev_region(dev_t from, unsigned count, const char *name)
功能:
注册一个范围()的设备号
参数:
@from 设备号
@count 注册的设备个数
@name 设备的名字
返回值:
成功返回0,失败返回错误码(负数)

* cdev_add() - add a char device to the system
* @p: the cdev structure for the device
* @dev: the first device number for which this device is responsible
* @count: the number of consecutive minor numbers corresponding to this
* device

* cdev_add() adds the device represented by @p to the system, making it
* live immediately. A negative error code is returned on failure.

int cdev_add(struct cdev *p, dev_t dev, unsigned count)
功能:
添加一个字符设备到操作系统
参数:
@p cdev结构体地址
@dev 设备号
@count 次设备号个数
返回值:
成功返回0,失败返回错误码(负数)

* cdev_del() - remove a cdev from the system
* @p: the cdev structure to be removed

* cdev_del() removes @p from the system, possibly freeing the structure
* itself.

void cdev_del(struct cdev *p)
功能:
从系统中删除一个字符设备
参数:
@p cdev结构体地址
返回值:

static inline int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops)
功能:
注册或者分配设备号,并注册fops到cdev结构体,
如果major>0,功能为注册该主设备号,
如果major=0,功能为动态分配主设备号。
参数:
@major : 主设备号
@name : 设备名称,执行 cat /proc/devices显示的名称
@fops : 文件系统的接口指针
返回值
如果major>0 成功返回0,失败返回负的错误码
如果major=0 成功返回主设备号,失败返回负的错误码

该函数实现了对cdev的初始化和注册的封装,所以调用该函数之后就不需要自己操作cdev了。

相对的注销函数为unregister_chrdev

static inline void unregister_chrdev(unsigned int major, const char *name)

审核编辑:符乾江


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表德赢Vwin官网 网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Linux
    +关注

    关注

    87

    文章

    11292

    浏览量

    209309
  • 应用层
    +关注

    关注

    0

    文章

    46

    浏览量

    11500
  • Struct
    +关注

    关注

    0

    文章

    31

    浏览量

    10864
收藏 人收藏

    评论

    相关推荐

    基于risc-v架构的芯片与linux系统兼容性讨论

    一定的兼容性挑战,但并非不可克服。通过针对RISC-V架构进行编译和优化Linux内核、确保硬件接口兼容、支持平台特性、提供适用的编译工具链和二进制格式以及构建完整的生态系统等措施,可以实现
    发表于 11-30 17:20

    Linux网络协议栈的实现

    请求并与底层的网络硬件进行交互。本文将深入探讨 Linux 网络协议栈的架构实现,涵盖数据包处理流程、关键模块、协议栈层次以及性能优化等方面。
    的头像 发表于 09-10 09:51 298次阅读
    <b class='flag-5'>Linux</b>网络协议栈的<b class='flag-5'>实现</b>

    labview字符串如何转换为16进制字符

    在LabVIEW中,将字符串转换为16进制字符串是一个常见的需求,尤其是在处理数据通信和硬件接口时。LabVIEW提供了多种方法来实现这一转换,包括使用内置函数、编写VI(Virtual
    的头像 发表于 09-04 15:54 2288次阅读

    labview中如何实现字符串换行

    1. 字符串换行的基本概念 在LabVIEW中,字符串换行通常指的是在字符串中插入换行符,使得字符串在显示或输出时能够自动换行。这在创建用户界面或处理文本数据时非常有用。 2.
    的头像 发表于 09-04 15:47 1611次阅读

    labview中如何实现字符串选择输出

    在LabVIEW中实现字符串选择输出是一项常见的任务,它涉及到字符串处理、条件判断和用户界面设计等多个方面。由于LabVIEW是一种图形化编程语言,其编程方式与传统的文本编程语言有所不同,因此
    的头像 发表于 09-04 15:44 891次阅读

    linux系统的设备驱动一般分几类

    Linux系统的设备驱动是操作系统与硬件设备之间的桥梁,负责实现操作系统与硬件设备之间的通信和控制。Li
    的头像 发表于 08-30 15:13 409次阅读

    Linux设备驱动程序分类有哪些

    Linux设备驱动程序是操作系统与硬件设备之间的桥梁,负责实现硬件设备与操作系统之间的通信和控制。Lin
    的头像 发表于 08-30 15:11 527次阅读

    linux驱动程序如何加载进内核

    Linux系统中,驱动程序是内核与硬件设备之间的桥梁。它们允许内核与硬件设备进行通信,从而实现对硬件设备的控制和管理。 驱动程序的编写 驱
    的头像 发表于 08-30 15:02 439次阅读

    Linux 驱动开发与应用开发,你知道多少?

    一、Linux驱动开发与应用开发的区别开发层次不同:Linux驱动开发主要是针对硬件设备进行编程,处于操作系统内核层,直接与硬件交互,为上层应用提供设备访问的接口。
    的头像 发表于 08-30 12:16 745次阅读
    <b class='flag-5'>Linux</b> 驱动开发与应用开发,你知道多少?

    虹科干货 | 多设备协同无忧:Linux环境下PCAN固定设备ID/通道分配指南

    插入顺序的影响。继往期文章介绍了Windows环境下的实现方法后,本文将详细介绍如何在Linux环境中CharDev和NetDev两种模式下,通过PCAN实现设备ID和通道的固定分配,
    的头像 发表于 06-11 15:03 453次阅读
    虹科干货 | 多<b class='flag-5'>设备</b>协同无忧:<b class='flag-5'>Linux</b>环境下PCAN固定<b class='flag-5'>设备</b>ID/通道分配指南

    ArmSoM系列板卡 嵌入式Linux驱动开发实战指南 之 字符设备驱动

    字符设备驱动 本章,我们将学习字符设备使用、字符设备驱动相关的概念,理解
    的头像 发表于 04-10 09:53 1049次阅读
    ArmSoM系列板卡 嵌入式<b class='flag-5'>Linux</b>驱动开发实战指南 之 <b class='flag-5'>字符</b><b class='flag-5'>设备</b>驱动

    微信Linux原生版在龙架构平台终端成功启动运行

    近日,在腾讯微信团队、龙芯中科与国产操作系统厂商的共同努力下,微信Linux原生版在龙架构平台终端成功启动运行,并在操作系统厂商应用商店上架分发,为用户带来全新的龙架构平台使用体验。
    的头像 发表于 03-19 16:32 869次阅读
    微信<b class='flag-5'>Linux</b>原生版在龙<b class='flag-5'>架构</b>平台终端成功启动运行

    arm架构和x86架构区别 linux是x86还是arm

    ARM架构和x86架构是两种不同的计算机处理器架构,它们在体系结构、指令集、应用领域等方面有着明显的区别。Linux操作系统则具有广泛的适配性,可以运行在各种
    的头像 发表于 01-30 13:46 1.8w次阅读

    Linux内核中信号详解

      1 信号的角色 1.1 x86/64架构信号定义 1.2 ARM架构信号定义 1.3 RISC-V架构信号定义 1.4 信号的系统调用 1.5 信号工作原理 2 信号的响应行为 3 POSIX
    的头像 发表于 01-13 09:40 1366次阅读
    <b class='flag-5'>Linux</b>内核中信号详解

    Linux内核内存管理架构解析

    内存管理子系统可能是linux内核中最为复杂的一个子系统,其支持的功能需求众多,如页面映射、页面分配、页面回收、页面交换、冷热页面、紧急页面、页面碎片管理、页面缓存、页面统计等,而且对性能也有很高
    的头像 发表于 01-04 09:24 652次阅读
    <b class='flag-5'>Linux</b>内核内存管理<b class='flag-5'>架构</b>解析