1.Linux内核
1.1 Linux内核的任务
- 从技术层面讲,内核是硬件和软件之间的一个中间层,作用是将应用层序的请求传递给硬件,并充当底层驱动程序,对系统中的各种设备和组件进行寻址。
- 从应用程序的角度讲,应用程序与硬件没有联系,只与内核有联系,内核是应用程序知道的层次中的最底层。在实际工作中内核抽象出了相关的细节。
- 从资源管理的角度讲,内核是一个资源管理程序。负责将可用的共享资源(如CPU时间,磁盘空间,网络连接等)分配到各个系统进程。
- 从系统层面将,内核相当于是一个库,提供了一组面向系统的命令。系统调用相对于应用程序来说,就像调用普通函数一样。
1.2 Linux系统层次结构
- Linux系统结构中位于最上层的是用户(或应用程序)空间。这是用户应用程序执行的地方。用户空间之下的是内核空间,Linux内核正是位于此。
1)GNU C Library(glibc)也在此。提供了 连接内核的系统调用接口,还提供了在用户空间应用程序和内核之间进行转换的机制
2)内核和用户空间的应用程序使用的是不同的保护地址空间。每个用户空间的进程都使用自己的虚拟地址空间(进程是资源分配的独立单位),而内核则占用单独的地址空间。 - Linux内核可进一步分为3层,最上面的是系统调用接口,它实现了一些基本功能,例如:read,write。系统调用接口之下是内核代码,可以更加精确地定义为独立于体系机构的内核代码(Linux所支持的所有处理器体系结构所通用的)。在这些代码之下是依赖于体系结构的代码,构成了BSP(Board Support Package)的部分——是嵌入式系统开发中的核心组件,充当硬件与操作系统之间的桥梁。它提供底层硬件驱动、配置及接口。
- 内核被划分为多个子系统。Linux也可以看作是一个整体,因为它会将所有这些基本服务都集成在内核中。这与微内核的体系结构不同,后者会提供一些基本的服务,例如通信,I/O,内存和进程管理等
2. Linux内核体系结构
Linux内核的主要组件是:系统调用接口,进程管理,内存管理,虚拟文件系统,网络堆栈,设备驱动程序,硬件架构的相关代码。
2.1 系统调用接口(SCI,system call interface)
该层提供了某些机制执行从用户空间到内核的函数调用。该接口依赖于体系结构,甚至在相同的处理器族内也是如此。SCI层实际是一个非常有用的函数调用多路复用和多路分解服务。
2.2 进程管理(PM, process management)
- 进程管理的重点是进程的执行。在Linux并不区分线程和进程,代表了单独的处理器虚拟化(线程代码、数据、堆栈和CPU寄存器)。内核通过SCI提供了一个应用程序编程接口(API)来创建一个新进程(fork, exec 或 Portable Operating System Interface),停止进程(kill, exit)。并在它们之间进行通信和同步(signal)
- 进程管理还包括处理活动进程之间共享CPU的需求。内核实现了一种新型的调度算法,不管有多少个线程正在竞争CPU,这种算法都可以在固定时间内进行操作——O(1)调度程序,这个名字就表示它调度多个线程所使用的时间和调度一个线程所使用的时间是相同的。同样也可以支持多处理器。
2.3 内存管理(MM, memory management)
内核所管理的另外一个重要资源是内存。内存是按照所谓的内存页方式进行管理的(大部分体系来说都是 4KB)。Linux包括了管理可用内存的方式,以及物理和虚拟映射所使用的硬件机制。Linux的内存管理不止4KB缓冲区,而是对4KB缓冲区提供一种抽象,如 Slab分配器 ——通过分层管理(伙伴系统 + Slab)和对象重用,高效解决了小内存块的分配问题。这种内存管理的方式使用4KB缓冲区为基数,然后从中分配结构并跟踪内存页使用情况。这样就允许根据系统需要来动态调整内存使用 (页面交换算法)
2.4 虚拟文件系统(VFS,virtual file system)
虚拟文件系统是Linux内核中文件系统的通用的接口抽象。VFS在SCI和内核所支持的文件系统之间提供了一个交换层
2.5 网络堆栈
网络堆栈在设计上遵循模拟协议本身的分层体系结构,如IP协议是传输协议TCP下面的核心网络层协议。TCP之上的协议是 socket层,通过SCI进行调用,socket层网络子系统的标准API,它为各种网络协议提供了一个用户接口。从原始帧访问到IP协议数据单元(PDU),再到UDP,socket层提供了一种标准化的方式进行管理连接,并在各个终点之间移动数据。
2.6 设备驱动程序(大部分代码)
设备驱动程序是用于管理硬件设备并与之交互的核心组件。它们充当用户空间与硬件之间的桥梁,使应用程序无需直接操作底层硬件即可使用设备功能。
2.7 依赖体系结构的代码
3. Linux驱动的paltform机制
Linux的paltform机制将本身的资源注册进内核,由内核统一管理,在驱动程序中使用这些资源时通过platform_device提供的标准接口进行申请并使用。这样提供了驱动和资源管理的独立性,并且拥有较好的可移植性和安全性。
platform机制分为以下三个步骤:
1)总线注册阶段
内核启动初始化main.c文件中的kernal_init()→do_basic_setup()→driver_init()→platform_bus_init()→bus_register(&platform_bus_type),由此注册了一条platform总线(虚拟总线,platform_bus)。
2)驱动注册阶段
Platform_driver_register()→driver_register()→bus_add_driver()→driver_attach()→bus_for_each_dev(),对在每个挂在虚拟的platform bus的设备作__deriver_attach()→driver_probe_device(),判断drv→bus→match()是否执行成功,此时通过指针执行platform_match→strncmp(pdev→name,drv→name,BUS_ID_SIZE),如果相符就调用really_probe(实际就是执行相应设备的platform_driver→probe(platform_device)。)开始真正的探测,如果probe成功,则绑定设备到该驱动。
从上面可以看出,platform机制最后还是调用了bus_register(),device_add(),driver_register()这三个关键的函数。
关键结构体:
struct platform_device {const char* name; //设备名称,要与platform_driver的name一样,这样总线才能匹配成功u32 id; //id号,插入总线下相同name的设备编号(一个驱动可以有多个设备),如果只有一个设备填-1struct device dev; //内嵌的具体的device结构体,其中成员platform_data,是个void *类型,可以给平台driver提供各种数据(比如:GPIO引脚等等)u32 num_resources; //资源数量,struct resource * resource; //资源结构体,保存设备的信息 };
struct resource {resource_size_t start; //起始资源,如果是地址的话,必须是物理地址resource_size_t end; //结束资源,如果是地址的话,必须是物理地址const char *name; //资源名unsigned long flags; //资源的标志//比如IORESOURCE_MEM,表示地址资源, IORESOURCE_IRQ表示中断引脚... ...struct resource *parent, *sibling, *child; //资源拓扑指针父、兄、子,可以构成链表 };
涉及到的函数如下(在dev设备的入口出口函数中用到)
int platform_device_register(struct platform_device * pdev);//注册dev设备 int platform_device_unregister(struct platform_device * pdev);//注销dev设备
platform机制的好处:
1. 提供了platform_bus_type类型的总线,把那些不是总线型的soc设备都添加到这条虚拟总线上。使得,总线——设备——驱动的模式可以得到普及。
2. 提供了platform_device和platform_driver类型的数据结构,将传统的device和driver数据结构嵌入其中,并且加入resource成员,以便于和Open Firmware这种动态传递设备的新型bootloader和kernal接轨。
参考引用:linux_kernel_wiki/文章/Linux内核架构和工作原理.md at main · 0voice/linux_kernel_wiki