Linux系统调用过程详解:应用程序调用驱动过程

Linux下应用程序调用驱动程序过程:

(1)加载一个驱动模块(.ko),产生一个设备文件,有唯一对应的inode结构体

                a、每个设备文件都有一个对应的’inode‘结构体,包含了设备的主次设备号,是设备的唯一标识。

                b、驱动加载至内核,初始化时通常会调用’register_chrdev()‘或’register_blkdev()‘(字符设备和块设备),注册设备号和关联文件操作file_operation。

                c、有了设备号后,用class_create()和 device_create()创建设备文件 ,device_create()该函数需要一个有效设备号

                d、当设备文件创建后,文件系统会为其创建和初始化一个’inode‘结构体,包含文件类型、权限、所有者信息以及重要的设备号

(2)应用层调用open函数打开设备文件(该函数是在应用层中写好的),对于上层open调用到内核时,发生一次软中断,从用户空间进入到内核空间

(3)open会调用到sys_open(内核系统调用函数),sys_open根据文件的地址(文件的路径),找到设备文件对应的struct inode结构体描述的信息,得知要操作的设备类型(字符设备还是块设备),还会分配一个struct file结构体。

  • sys_open:

                内核级的系统调用函数,用于打开文件和设备。系统调用是用户空间和内核空间之间的接口,通过系统调用可从用户态切换至内核态。

  • sys_open函数的基本操作:

                a、解析用户提供的文件名,确定文件位置和权限(通常为文件的路径名,如/dev/设备名)

                b、检查文件访问权限

                c、分配并初始化一个文件描述符(对该文件操作的引用,非负的整数,是该文件表条目在文件表中的索引)

                d、将文件描述符返回给用户空间

  • struct file:
    • 内核中的重要结构体,表示一个已打开的文件(信息)。
    • 包含相关的文件状态信息,如当前偏移量、文件操作指针file_operation(包含对文件读写的操作函数指针)、'inode'结构体指针
    • 由sys_open创建,内核为打开的文件创建一个新的'struct file'实例

(4)根据struct inode结构体内记录的主次设备号,在驱动链表(管理所有设备驱动)里面,找到字符设备驱动。

        这一步也是后续为了给创建的file结构体内的f_ops成员,文件操作指针赋值

  • 驱动链表:
    • Linux内核中用于管理设备驱动的关键数据结构,是一个链表或哈希表结构,其中包含了所有注册的设备驱动。
    • 字符设备的驱动链表中,每个成员(设备)确实是通过 struct cdev 结构体来表示的。
    • 字符设备驱动通过调用 cdev_add() 函数将自己的 cdev 结构体添加到内核的设备表中(这通常是在调用 register_chrdev_region() 或 alloc_chrdev_region() 注册设备号之后进行的。

(5)每个字符设备都有一个struct cdev结构体。描述了字符设备的所有信息,其中最重要的一项是字符设备的操作函数接口

(6)找到struct cdev结构体后,linux内核就会将struct cdev结构体所在内存空间首地址记录在struct inode结构体i_cdev成员中,将struct cdev结构体中记录的函数操作接口地址记录在strct file结构体的f_ops成员中

(7)执行xxx_open驱动函数。

流程架构参考自:最全Linux驱动开发全流程详细解析(持续更新)-CSDN博客

inode结构体组成

变量赋值的来源

这些变量的值大多是由文件系统、内核的设备管理子系统,或驱动自身在注册设备时设置的。

例如:

  • i_mode(文件模式):通常由驱动设置以标识设备文件的类型(字符设备或块设备)
  • i_rdev(实际设备ID):包含主设备号和次设备号(高12位和低20位),由register_chrdev()或register_blkdev()设置

文件系统中对于'inode'

在创建和管理'inode'结构体中起到核心作用,当一个文件系统被挂载时,它会初始化其管理的所有 inode,并负责管理它们的生命周期。文件系统会定义自己的'inode'操作方法('inode_operation'结构体,也是inode结构体的成员变量),包括:

        a、创建 (create):当新文件被创建时,文件系统会分配和初始化一个 inode 结构体。这包括设置文件类型、权限、所有者等。

        b、查找 (lookup):在访问文件时,文件系统需要通过路径查找相应的 inode。

        c、删除 (delete):当文件被删除时,文件系统负责清理 inode 结构体,并可能将其空间释放回 inode 缓存。

文件描述符:
用于访问文件和设备的一个抽象表示。文件描述符本质上是一个非负整数,当一个程序打开一个文件(包括设备文件)时,操作系统返回的文件描述符作为未来所有对该文件的操作的引用。

文件描述符的形式和功能

  • 形式:文件描述符是一个简单的整数值。在一个进程的上下文中,每打开一个文件或设备,内核就会分配最低未被使用的文件描述符。
  • 功能:文件描述符提供了一个通用的方法来引用所有类型的文件,无论它们是普通的磁盘文件、目录、链接还是设备(如键盘、硬盘、网络设备等)。

文件描述符的操作

当一个文件或设备被打开时,内核会创建一个文件表条目。文件描述符就是这个文件表条目在文件表中的索引。

文件表条目包括:文件位置、当前偏移量、访问权限、指向具体文件操作的指针等。

  • 基于文件描述符的标准操作:

                a、打开操作open(pathname, flags, ... ):打开一个已存在的文件或创建一个新文件,并返回一个文件描述符

                b、读操作 (read(fd, buffer, size)):从由 fd 指定的文件或设备中读取数据。

                c、写操作 (write(fd, buffer, size)):向由 fd 指定的文件或设备写入数据。

                d、输入输出控制操作 (ioctl(fd, command, ...)):对特定的设备执行控制和配置操作。

                e、文件控制操作(fcntl(fd, command, ...)):改变已打开的文件性质。

                f、关闭操作 (close(int fd)):关闭文件描述符。

file结构体组成

struct file主要成员解释

  • f_path:包含文件的路径和文件系统信息,struct path 结构体包括一个指向 dentry(目录项)和 vfsmount(文件系统挂载点)的指针。
  • f_inode:指向与文件关联的 inode 结构体的指针,它存储了文件的大部分元数据。
  • f_op:指向文件操作表的指针,这个表包含了各种文件操作相关的函数指针,如 readwriteopenrelease 等。
  • f_lock:自旋锁,用于保护文件相关的敏感操作。
  • f_count:原子类型,表示文件描述符的引用计数,用于文件描述符的复制和释放管理。
  • f_flags:打开文件时使用的标志,如 O_RDONLYO_NONBLOCK 等。
  • f_mode:文件模式,指定文件的访问模式,如只读、只写等。
  • f_pos:当前文件的偏移量,表示下一次读/写的起始位置。
  • f_owner:文件所有者的信息,常用于信号发送。
  • f_mapping:指向文件的地址空间结构,用于文件映射和内存管理。

驱动链表的设备匹配

当一个应用程序尝试通过系统调用(open())访问一个字符设备时,内核在驱动链表中找到相应的设备驱动的步骤:

1、设备号解析

应用程序请求打开一个设备文件(/dev/example)时,内核首先获取该设备文件的'inode'结构体,从中读取主次设备号

  • 主设备号:用于识别该设备的驱动程序
  • 次设备号:用于识别该驱动程序控制的特定设备实例(控制的是哪个设备)

2、驱动链表搜索

内核中维护了一个包含所有已注册字符设备的列表(register_chrdev()),每个注册设备通过一个'struct cdev'实例表示,并链接到全局链表中。内核遍历这个链表,比较每个'struct cdev'的设备号

  • 链表遍历:从链表头部开始,内核遍历每个'cdev'条目
  • 匹配主设备号:内核检测每个'cdev'的主设备号是否与请求的主设备号匹配

3、文件操作结构体关联

一旦找到匹配的'cdev'结构体,内核将使用该结构体中的'file_operations'指针。

  • 设备操作:应用程序通过系统调用请求的操作(如读取或写入数据)将由file_operations结构体中相应的函数处理。

4、f_op指针设置

内核在打开文件时会创建file struct结构体,但此时部分成员未设置,包括f_op指针(file_operation的指针)。一旦找到正确的驱动,f_op指针会被设置为指向该驱动的file_operations结构体。

后续的操作(如再次读写文件)都会通过这些函数处理(file_operation中的函数)。

Linux设备号:

Linux中每个设备都有一个设备号,由主设备号和次设备号组成。主设备号对应一个具体的驱动,次设备号表示使用这个驱动的各个设备。Linux中提供了一个名为dev_t的数据类型表示设备号,定义在include/linux/types.h中。

1、dev_t类型

  • _u32类型别名为_kernel_dev_t;
  • _kernel_dev_t别名为dev_t
  • 所以dev_t 是__u32 类型的(即unsigned int)

  • _u32:32位的无符号整形unsigned int。

该32位数据构成了主设备号和次设备号,高12位为主设备号,低20位为次设备号,所以系统中主设备号范围为0~4095。

2、设备操作函数:

  • MINORBITS:次设备号位数,20位。
  • MINORMASK:次设备号掩码
  • MAJOR(dev):从dev_t中获取主设备号,将dev_t右移20位
  • MINOR(dev):从dev_t中获取次设备号,取dev_t低20位的值
  • MKDEV(ma,mi):将给定的主设备号和次设备号的值组合成dev_t类型的设备号。(主设备号左移20位)

相关推荐

  1. dubbo服务调用过程

    2024-05-11 06:58:15       28 阅读
  2. 创建存储过程,与存储过程调用

    2024-05-11 06:58:15       52 阅读
  3. 程序:windows下C++调用打印机过程简介

    2024-05-11 06:58:15       57 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-05-11 06:58:15       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-05-11 06:58:15       106 阅读
  3. 在Django里面运行非项目文件

    2024-05-11 06:58:15       87 阅读
  4. Python语言-面向对象

    2024-05-11 06:58:15       96 阅读

热门阅读

  1. 微服务全局异常处理

    2024-05-11 06:58:15       31 阅读
  2. 结合场景,浅谈深浅度拷贝

    2024-05-11 06:58:15       30 阅读
  3. Spring Boot + Logback 实现日志记录写入文件

    2024-05-11 06:58:15       31 阅读
  4. vueConfig

    2024-05-11 06:58:15       28 阅读
  5. MES系统助力离散制造行业智能制造升级

    2024-05-11 06:58:15       33 阅读
  6. Django 和 Spring Boot

    2024-05-11 06:58:15       33 阅读
  7. 微信原生小程序封装网络请求wx.request

    2024-05-11 06:58:15       32 阅读
  8. mysql(一)

    2024-05-11 06:58:15       31 阅读
  9. 未来趋势系列 篇一:AI主题全景分析和股票梳理

    2024-05-11 06:58:15       25 阅读