【Linux】内核结构及驱动认知

【Linux】内核结构及驱动认知

文章目录

  • 【Linux】内核结构及驱动认知
    • Linux内核的结构
    • 内核结构的层次
    • 内核设计理念
    • Linux内核结构框图
    • 图解Linux系统架构
    • 驱动认知
      • 为什么要学习Linux驱动
      • 文件名与设备号
        • 主设备号和次设备号
      • open函数打通上层到底层硬件的详细过程

Linux内核是操作系统的核心组件,负责管理系统资源和硬件设备,提供基本的功能支持。它的主要职责包括进程管理、内存管理、设备驱动、文件系统和网络功能。内核与用户空间的应用程序直接交互,并控制系统的硬件。

Linux内核的结构

Linux内核的结构可以分为几个主要部分,每个部分都有不同的职责:

  1. 进程管理(Process Management)

    • 调度器(Scheduler):负责管理和分配CPU时间给不同的进程和线程。调度策略包括时间片轮转、优先级调度等。
    • 进程/线程管理:处理进程的创建、终止、上下文切换等。
  2. 内存管理(Memory Management)

    • 虚拟内存(Virtual Memory):提供进程隔离,通过分页或分段机制将物理内存映射到虚拟地址空间。
    • 内存分配:管理内存的分配和释放,包括页表管理和内存回收(例如垃圾回收)。
  3. 文件系统(File System)

    • 文件操作:提供创建、删除、读写文件的功能。
    • 虚拟文件系统(VFS):为不同类型的文件系统提供统一的接口,使得不同文件系统能够被一致地访问。
  4. 设备驱动(Device Drivers)

    • 设备管理:处理硬件设备的驱动程序,包括输入设备、存储设备、网络设备等。
    • 中断处理:响应硬件中断,执行相应的设备驱动程序。
  5. 系统调用(System Calls)

    • 用户空间与内核空间的接口:提供应用程序访问内核功能的接口。例如,文件操作、进程控制、内存管理等系统调用。
  6. 网络栈(Network Stack)

    • 网络协议:实现网络通信协议,如TCP/IP,处理网络数据包的传输、路由等功能。
    • 网络接口:管理网络设备和网络连接。

内核结构的层次

  1. 内核空间(Kernel Space)

    • 核心(Core):包括内核的主要代码,负责资源管理和系统调用。
    • 模块(Modules):内核可以在运行时加载或卸载的可选部分,例如设备驱动和文件系统支持。
  2. 用户空间(User Space)

    • 系统库(Libraries):如glibc,提供用户程序调用内核功能的接口。
    • 应用程序(Applications):用户在系统中运行的各种程序。

内核设计理念

  • 单内核(Monolithic Kernel):Linux内核是单内核的,这意味着所有核心功能(进程管理、内存管理、文件系统、设备驱动等)都运行在内核空间中。这种设计有助于提高性能,但也增加了内核的复杂性。

  • 模块化(Modularization):虽然Linux内核是单内核的,但它支持模块化设计,即可以在运行时加载和卸载模块。这使得内核可以支持各种硬件和功能而无需重新编译。

  • 跨平台(Portability):Linux内核被设计为可以在多种硬件架构上运行,包括x86、ARM、PowerPC等。

  • 开源(Open Source):Linux内核是开源的,意味着任何人都可以查看、修改和分发内核的源代码。这促进了社区的贡献和内核的快速发展。

这些特点和结构使得Linux内核在性能、灵活性和可扩展性方面非常强大,适用于从嵌入式设备到大型服务器的各种平台。

Linux内核结构框图

对内核结构框图有个总体的把握,有助于理解为什么驱动要这样写,为什么写的应用程序所用的C库接口能够产生这么多的事情。

【Linux】内核结构及驱动认知_第1张图片

函数库就像一个“黑匣子”,提供了一系列API支配内核运作,但你不知道内核发生了什么。

内核是一个很厉害的超级逻辑,把硬件底层的东西抽象化,对用户来说只需要调API就好了,根本不需要管寄存器,协议,总线…(单片机会去直接操作),这些全部由操作系统做好。动不动写个操作系统是不现实的。能把字符设备,块设备的设备驱动吃透,已经是一个很厉害的工程师了。

图解Linux系统架构

【Linux】内核结构及驱动认知_第2张图片

  1. 最内层是硬件,最外层是用户应用,比如浏览器等等。硬件是物质基础,而应用提供服务。
  2. 为了方便调用内核,Linux将内核的功能接口制作成系统调用(system call)。用户不需要了解内核的复杂结构,就可以使用内核。系统调用是操作系统的最小功能单位。一个操作系统,以及基于操作系统的应用,都不可能实现超越系统调用的功能。
  3. 系统调用提供的功能非常基础,所以使用起来很麻烦。一个简单的给变量分配内存空间的操作,就需要动用多个系统调用。Linux定义一些库函数(library routine)来将系统调用组合成某些常用的功能。上面的分配内存的操作,可以定义成一个库函数,比如常用的malloc。

驱动认知

驱动程序(Driver)是操作系统和硬件设备之间的桥梁。它负责将操作系统的通用请求转换为硬件设备能够理解和执行的具体指令。

例如,打印机驱动程序将打印任务转换为打印机能理解的格式。驱动程序确保硬件设备能够与操作系统和应用程序正确通信,处理设备的输入和输出操作。

Linux驱动程序是用于在Linux操作系统中管理和控制硬件设备的软件模块。它们通常包括:

  1. 字符设备驱动:管理字符设备,如串口和终端。这些设备以字符为单位进行数据传输。
  2. 块设备驱动:处理块设备,如硬盘和SSD,这些设备以块为单位进行数据传输。
  3. 网络设备驱动:管理网络接口卡,处理数据包的发送和接收。
  4. 输入设备驱动:处理输入设备,如键盘和鼠标,将输入转换为系统可以理解的事件。

这些驱动程序可以作为内核模块加载或嵌入到内核中,提供对硬件的支持和功能扩展。

以下为关于Linux驱动的一些认知:

  1. 硬件与操作系统的交互: 操作系统本身并不了解所有硬件设备的细节。驱动程序提供了一个标准的接口,使得操作系统能够与硬件设备进行通信,而无需了解设备的底层细节。

  2. 功能和作用

  • 设备驱动: 控制和管理硬件设备,如打印机、图形卡、网络适配器等。
  • 文件系统驱动: 提供对不同文件系统的支持,例如 FAT、NTFS、ext4 等。
  • 虚拟设备驱动: 创建虚拟设备,如虚拟磁盘、虚拟网络设备等。
  • 字符设备驱动和块设备驱动: 用于字符设备(如终端)和块设备(如硬盘)的控制。
  • 网络设备驱动: 管理网络接口卡和网络协议栈的通信。
  1. 驱动模型
  • Monolithic Kernel 模型: 驱动程序直接链接到内核,与内核一起运行。

  • Microkernel 模型: 驱动程序运行在用户空间,与内核通过 IPC 进行通信。

  1. 编写和调试
  • 语言: 驱动程序通常使用 C 语言编写。
  • 调试: 驱动的调试比应用程序更具挑战性,通常需要使用特定的调试工具。
  1. 加载和卸载
  • 加载: 操作系统在启动时或需要设备支持时加载相应的驱动。
  • 卸载: 驱动可在不需要时从内存中卸载。
  1. 稳定性和性能
  • 稳定性: 驱动程序的稳定性对系统整体稳定性至关重要。
  • 性能: 驱动程序的效率影响系统性能,因此编写高效的驱动程序很重要。
  1. 硬件抽象层(Hardware Abstraction Layer,HAL):
  • 驱动程序通常包括硬件抽象层,用于将硬件的具体实现细节隐藏起来,使得上层软件能够以标准化的方式与硬件交互。
  1. 硬件和操作系统的变化
  • 驱动程序需要不断适应硬件和操作系统的变化。随着技术的进步和新硬件的推出,驱动程序需要进行更新和优化。

驱动程序是操作系统的关键组成部分之一,它们的正确性和性能直接影响到系统的稳定性和功能。在编写和维护驱动程序时,开发人员需要深入了解硬件和操作系统的工作原理。

为什么要学习Linux驱动

学习写驱动有许多重要的原因,尤其是对于嵌入式系统和底层硬件交互的开发者。以下是一些学习写驱动的关键原因:

  1. 硬件控制: 驱动是与硬件设备进行交互的桥梁。通过学习写驱动,你可以更好地理解和掌握如何控制硬件,包括传感器、执行器、通信设备等。
  2. 跨平台兼容性: 编写通用的驱动可以使你的代码跨平台兼容,不依赖于特定的硬件或平台。这对于未来在不同系统上进行开发是至关重要的。
  3. 定制化需求: 在一些特殊的应用场景中,可能需要对硬件进行定制化的开发。学会写驱动使你能够满足特定硬件需求,而不仅仅依赖于现有的库和工具。
  4. 系统优化和性能: 编写优化的驱动可以提高系统的性能。对于嵌入式系统或对性能要求较高的应用,了解并优化驱动是至关重要的。
  5. 学习系统内部原理: 编写驱动需要深入了解操作系统的内部原理,包括内存管理、中断处理、设备树等。这有助于提升对系统整体运行机制的理解。
  6. 适应不同的开发环境: 随着技术的发展,可能需要在不同的硬件平台上进行开发。通过学习写驱动,你能够适应不同的开发环境,不仅限于某个特定的开发板或平台。
  7. 理解底层通信协议: 编写驱动通常涉及与硬件之间的低层通信,这使你能够深入了解通信协议、寄存器配置等底层细节。
  8. 开发嵌入式系统: 对于嵌入式系统的开发者来说,掌握驱动编写是至关重要的,因为这些系统通常需要直接与硬件进行交互。

总的来说,学习写驱动提供了更深层次的系统开发技能,使开发者能够更全面、更灵活地应对各种硬件和开发场景。

树莓派开发使用厂家提供的wiringPi库,开发简单。
但未来做开发时,不一定都是用树莓派,没有wiringPi库可以用。但只要能运行Linux,linux的标准C库一定有。
学会根据标准C库编写驱动,只要能拿到linux内核源码,拿到芯片手册,电路图…就能做开发。

文件名与设备号

在 Linux 中,一切皆为文件的概念是核心思想之一。设备文件在 /dev 目录下,通过文件名和设备号来标识和区分硬件设备。以下是一些相关的概念和解释:

  1. 文件名: 文件名是用户空间应用程序访问设备的方式之一。通过打开 /dev 目录下的特定文件,应用程序可以与相应的设备进行交互。例如,/dev/sda 可以代表系统上的第一个硬盘设备。

  2. 设备号: 每个设备文件都与一个设备号相关联。设备号分为主设备号和次设备号两部分。

    • 主设备号: 用于区分不同种类的设备,它指定了设备驱动程序的入口点。
    • 次设备号: 用于区分同一类型下的多个设备,指定具体的设备实例。
    • 设备号的管理: 设备号是通过 mknod 命令手动创建的,但通常由系统自动创建和管理。系统会为已注册的设备驱动程序分配设备号,这些信息存储在内核的设备链表中。
  3. 设备链表: 内核中存在一个设备链表,用于管理已注册的设备驱动程序。这个链表包含了每个设备驱动程序的信息,包括主设备号、设备名称、设备操作函数等。

  4. 加载驱动程序: 驱动程序通过 insmod 或 modprobe 命令加载到内核中。加载后,内核会将该驱动程序的信息加入设备链表中。

  5. 用户空间的操作: 当用户空间的应用程序调用 open 等系统调用时,内核会根据文件名找到相应的设备号,并查找设备链表以确定相应的设备驱动程序。这样,用户空间的应用程序就可以通过文件名访问硬件设备。

总体来说,文件名和设备号的组合提供了一种抽象的方式,使用户空间的应用程序能够以标准的文件 I/O 操作方式访问硬件设备,而无需了解底层硬件细节。这种抽象化的设计提高了系统的可移植性和灵活性。

在目录/dev下都能看到鼠标,键盘,屏幕,串口等设备文件,硬件要有相对应的驱动,那么open怎样区分这些硬件呢?
依靠文件名与设备号

【Linux】内核结构及驱动认知_第3张图片

crw-rw----+ 1 root video    81,   0 Dec 14 12:17 video0
    
这是一个设备文件的详细信息,解释如下:
crw-rw----+: 文件的类型和权限。这是一个字符设备文件,权限是读写,额外的加号表示文件有扩展属性。
1: 这表示有一个硬链接指向该文件。
root: 文件的所有者是 root 用户。
video: 文件的所属组是 video 组。
81, 0: 这是设备文件的主设备号和次设备号。在这个例子中,主设备号是 81,次设备号是 0。
Dec 14 12:17: 文件的最后修改时间是在 1214 日的 12:17。
video0: 文件的名称是 video0。
    
 这个设备文件属于 video 组,具有读写权限。主设备号为 81,次设备号为 0,表明它是一个视频设备。
主设备号和次设备号

设备号又分为:主设备号用于区别不同种类的设备;次设备号区别同种类型的多个设备。驱动插入到链表的位置(顺序)由设备号检索

内核中存在一个驱动链表,管理所有设备的驱动。 驱动开发无非以下两件事:

  • 编写完驱动程序,加载到内核
  • 用户空间open后,调用驱动程序(驱动程序就是操作寄存器来驱动IO口,单片机51,32就是这种操作)

open函数打通上层到底层硬件的详细过程

open 函数是用户空间应用程序通过系统调用访问文件的入口。打通上层到底层硬件的过程涉及几个步骤,主要包括:

  1. 用户空间调用: 应用程序在用户空间通过标准库函数 open 调用打开文件。该调用提供了文件路径和一些标志位参数。
int fd = open("/dev/video0", O_RDWR);
  1. 系统调用: open 函数触发系统调用,导致应用程序从用户空间切换到内核空间。在内核空间,调用处理程序开始执行。
  2. 查找文件: 内核通过文件系统层次结构查找指定的文件。在这个例子中,是查找 /dev/video0 这个设备文件。
  3. 获取文件对象: 找到文件后,内核会创建一个文件对象(file structure)来代表该文件。这个文件对象包含了一系列与文件相关的信息,例如文件的位置、访问模式、设备号等。
  4. 权限检查: 内核会检查应用程序对文件的访问权限。如果应用程序没有足够的权限,open 将失败。
  5. 分配文件描述符: 成功后,内核为应用程序分配一个文件描述符,这个描述符是一个整数,用于标识文件对象。应用程序将使用这个描述符进行后续的文件操作。
  6. 调用文件操作函数: 对于设备文件,内核会调用相应的设备驱动程序中的文件操作函数。例如,open 操作可能触发设备驱动程序的 open 函数。
  7. 设备操作: 设备驱动程序中的 open 函数执行与设备相关的初始化或其他必要的操作。这可能包括分配设备资源、配置硬件等。
  8. 返回文件描述符: 如果所有步骤成功,open 系统调用返回文件描述符给应用程序,表示文件打开成功。

通过这个过程,open 函数从用户空间向内核发起请求,内核负责管理文件系统和与设备相关的硬件,最终建立起用户空间应用程序与底层硬件的连接。

用户空间调用open
用户空间调用open(比如open(“/dev/pin4”,O_RDWR))产生一个软中断(中断号是0x80),进入内核空间调用sys_call,这个sys_call在内核里面是汇编的,用Source Insight搜索不到。

sys_calll真正调用的是sys_open(属于VFS层虚拟文件系统,因为磁盘的分区和引脚分区不一样,为了实现上层统一化),根据你的设备名比如pin4去到内核的驱动链表,根据其主设备号与次设备号找到相关驱动函数。

调用驱动函数里面的open,这个open就是对寄存器的操作,从而设置IO口引脚电平。这件事对于单片机来说特变容易,就两句话搞定:

sbit pin4 = P1^4;
pin4 = 1;

【Linux】内核结构及驱动认知_第4张图片

你可能感兴趣的:(Linux系统编程,linux,arm开发,运维)