Linux kernel diagram

Linux kernel diagram

内核

得益于Linux 一切皆文件的哲学,对于Linux Kernel,大家觉得好深奥、好牛!操作系统内核已经深入我们的日常工作、生活,我们应该去了解它、打破对它的恐惧,因为恐惧是对未知事物产生的一种情绪。平视它,了解清楚脉络,从而掌握它。小孩为什么总是学习东西很快?因为他们都是从形到意,先直观的摸到、看到、嗅到、吃到,先感受到"形",然后给他们阐述这些东西是什么含义,他们很快就接受了。我们研究内核框架,就是想从形意结合,先介绍一些探索内核的基础知识,构建好知识框架,后续的深造将会得心应手。

操作系统(简称为OS)是应用程序和硬件设备之间的桥梁,用户借助操作系统在计算设备上运行不同的应用程序。例如:Linux、Unix、macOS、Windows。内核的开发难在调试和排障,用户态的程序可以随意debug,出问题最多是panic、coredump,内核态出问题就是宕机,所有现场都丢失,只能通过日志、kdump等手段来排查,并且内核态要注意非常多的规范,比如内核态内存分配比用户态的谨慎的多。
在这里插入图片描述

如图所示:操作系统的基本组成

  • Bootloader:负责设备的启动,PC上类似UEFI/BISO固件。
  • Shell:Shell是一个用C语言编写的命令解释器程序,是Linux内核的一个外壳,负责外界与Linux内核的交互。
  • Kernel:操作系统的主要核心,负责管理系统资源。
    从硬件层面讲,内核是硬件和软件的中间层,将应用程序的请求传递给硬件,充当底层驱动程序管理硬件资源,负责将可用的共享资源(CPU时间、磁盘空间、网络等)分配到各个进程。
    从应用程序的层面讲,应用程序与硬件没有联系,只与内核由联系,内核是应用程序知道的层次中的最底层,在实际工作中内核抽象了相关细节。
  • Desktop Environment:用户与操作系统交互的环境。
  • Graphical server:图形服务器,它是操作系统的子系统,用于图形显示。
  • Applications:执行不同用户任务的程序集。
  • Daemons:后台服务。

内核策略

  • 微内核,实现操作系统组件之间的地址空间隔离,内核提供IPC,内存管理,任务调度的基本功能,其他的操作系统组件如文件系统,驱动程序等都在各自独立的地址空间执行。比如应用程序调用操作系统的系统调用,或者内核调用其他组件(比如文件系统,驱动程序)的功能都是通过IPC的方式。
  • 宏内核,内核的所有代码,包括子系统(如内存管理、文件管理、设备驱动程序等)都打包到一个文件中。内核中的每一个函数都可以访问内核中所有其他部分,同时支持模块的动态卸载等。Linux 内核就是基于这个策略实现的。

Linux 内核系统层次结构

Linux宏内核系统层次结构
最上面是用户(或应用程序)空间。这是用户应用程序执行的地方。用户空间之下是内核空间,Linux 内核正是位于这里。GNU C Library (glibc)也在这里。它提供了连接内核的系统调用接口,还提供了在用户空间应用程序和内核之间进行转换的机制。这点非常重要,因为内核和用户空间的应用程序使用的是不同的保护地址空间。每个用户空间的进程都使用自己的虚拟地址空间,而内核则占用单独的地址空间。
Linux 内核可以进一步划分成 3 层。最上面是系统调用接口,它实现了一些基本的功能,例如 read 和 write。系统调用接口之下是内核代码,可以更精确地定义为独立于体系结构的内核代码。这些代码是 Linux 所支持的所有处理器体系结构所通用的。在这些代码之下是依赖于体系结构的代码,构成了通常称为 BSP(Board Support Package)的部分。这些代码用作给定体系结构的处理器和特定于平台的代码。
Linux 内核实现了很多重要的体系结构属性。在或高或低的层次上,内核被划分为多个子系统。Linux 也可以看作是一个整体,因为它会将所有这些基本服务都集成到内核中。这与微内核的体系结构不同,后者会提供一些基本的服务,例如通信、I/O、内存和进程管理,更具体的服务都是插入到微内核层中的。每种内核都有自己的优点,不过这里并不对此进行讨论。
随着时间的流逝,Linux 内核在内存和 CPU 使用方面具有较高的效率,并且非常稳定。但是对于 Linux 来说,最为有趣的是在这种大小和复杂性的前提下,依然具有良好的可移植性。Linux 编译后可在大量处理器和具有不同体系结构约束和需求的平台上运行。一个例子是 Linux 可以在一个具有内存管理单元(MMU)的处理器上运行,也可以在那些不提供 MMU 的处理器上运行。

Linux 引导启动程序

Linux操作系统第一难点就是引导启动程序。

Linux中L1,L2,L3缓存和RAM的大小

uos@uos-PC:~/F00160/167-90-arch-nonarch-min/asm$ sudo lshw -C memory
  *-firmware                
       description: BIOS
       vendor: ZD-TECH
       physical id: 0
       version: ML5A-V3.2
       date: 04/16/2021
       size: 1MiB
       capacity: 8MiB
       capabilities: pci upgrade shadowing cdboot bootselect socketedrom int14serial usb netboot uefi
  *-memory
       description: System Memory
       physical id: 6
       slot: System board or motherboard
       size: 8GiB
       capabilities: ecc
       configuration: errordetection=ecc
     *-bank:0
          description: DIMM DDR4 Synchronous 3200 MHz (0.3 ns)
          product: SCC08GU03H3F1C-32AA
          vendor: UniIC
          physical id: 0
          serial: 00016761
          slot: DIMM1
          size: 8GiB
          width: 64 bits
          clock: 3200MHz (0.3ns)
     *-bank:1
          description: DIMM Synchronous [empty]
          physical id: 1
          slot: DIMM2
  *-cache:0
       description: L1 cache
       physical id: b
       size: 64KiB
       capacity: 64KiB
       capabilities: burst pipeline-burst synchronous internal write-back data
       configuration: level=1
  *-cache:1
       description: L2 cache
       physical id: c
       size: 256KiB
       capacity: 256KiB
       capabilities: burst pipeline-burst synchronous internal write-back data
       configuration: level=2
  *-cache:2
       description: L3 cache
       physical id: d
       size: 16MiB
       capacity: 16MiB
       capabilities: burst pipeline-burst synchronous internal write-back data
       configuration: level=3

终端、shell、bash的区别联系

  • 终端,即所谓的命令行界面,也可称为命令终端,用户输入shell命令用的窗口,类似Windows的DOS界面,作用是提供命令的输入输出环境。当打开一个终端,操作系统会将terminal终端和shell关联起来,当我们子terminal终端输入命令后,shell就负责解释命令。
  • shell就是用户和操作系统内核之间的壳(中介),GUI和CLI都算是shell,登录终端可以是Bash、Csh或者Dash。shell将用户输入的命令翻译为操作系统内核能处理的指令。
  • dash是ubuntu默认的shell。shell有很多种,目前最常用的是bash,dash除了不支持数组外,其实跟bash差别不大。

functions

在日常内核开发工作中,需要对内核函数进行Debug。可以简单的通过dmesg/printk查看,但是遇到问题逻辑复杂,优化面宽泛的时候,需要从上到下分析模块到模块之间的函数调用,以缩小问题范围、发现问题,这时候就需要借助Linux提供的静态(Trace Event)动态(各种Tracer)进行分析,例如:ftrace等。

user space interfaces

User-Space-to-Kernel Interface and Kernel-to-user-space communication.
用户空间的应用程序通过用户空间接口与内核建立通信,例如:读取系统gpiochip信息。

Linux kernel GPIO user space interface

gpiodetect命令用于列出系统上存在的所有gpiochip,以及它们的名称、标签和GPIO lines。
gpiodetect命令由libgpiod软件包提供,在Debian/Ubuntu 系统上可以使用如下命令进行安装

sudo apt install gpiod //非root需添加sudo,本文后续不再强调

从Linux 4.8 开始,不再推荐使用sysfs接口(/sys/class/gpio)操作GPIO,而是建议在用户空间使用字符设备节点,libgpiod就是一个用于操作GPIO字符设备的库,提供gpiodetect、gpioinfo、gpioget、gpioset、gpiofind和gpiomon命令方便开发者进行调试。

system

确认Linux内核当前支持那些文件系统?
cat /proc/filesystems 
nodev   sysfs
nodev   rootfs
nodev   ramfs
nodev   bdev
nodev   proc
nodev   cpuset
nodev   cgroup
nodev   cgroup2
nodev   tmpfs
nodev   devtmpfs
nodev   configfs
nodev   securityfs
nodev   sockfs
nodev   pipefs
nodev   hugetlbfs
nodev   rpc_pipefs
nodev   devpts
        ext3
        ext4
        ext2
        squashfs
        iso9660
nodev   nfs
nodev   nfs4
nodev   nfsd
nodev   autofs
nodev   overlay
        udf
        xfs
nodev   9p
nodev   mqueue
        btrfs
nodev   binfmt_misc
        vfat
        fuseblk
nodev   fuse
nodev   fusectl
第一列表示文件系统是否已经mount在一个块设备(block device)上,"nodev"表示该文件系统尚未被mount在一个块设备上,该列空白则表示文件系统被mount在一个块设备上;
第二列表示文件系统的名称。

内核文件系统的构成

Linux文件系统的构成
文件系统是位于内核的一个模块,vfs之下,块存储模块之上的一个位置。Linux 文件系统最为核心便是虚拟文件系统(VFS)机制,VFS 是著名的类 Unix 系统中 “一切皆文件” 概念的基础。也是 Linux 核心的一部分,负责与内核其它子系统打交道,VFS 又管理其它逻辑文件系统。
所以 VFS 是文件系统和 Linux 内核的接口,VFS 以统一数据结构管理各种逻辑文件系统,接受用户层对文件系统的各种操作。就像上面这张图中所示,VFS 其实是一个中间层,对上层调用者屏蔽了下层不同类型文件系统实现的差异,提供统一的读写访问接口。
VFS和FUSE

如上图所示,文件系统对外呈现文件存储实现,对下管理裸块设备,考虑到文件系统的需求是千变万化的,现存文件系统难以满足用户的特别需求,内核下定制开发文件系统难度较高,所以提供了一种手段,把IO路径导向用户态,由用户态程序捕获到IO,从而实现文件的存储,这个机制就叫FUSE机制。

VFS简介

虚拟文件系统(Virtual File System,简称VFS)是Linux内核的子系统之一,它为用户程序提供文件和文件系统操作的统一接口,屏蔽不同文件系统的差异和操作细节,VFS之所以能够衔接各种各样的文件系统,是因为它抽象了一个通用的文件系统模型,定义了通用文件系统都支持的、概念上的接口。新的文件系统只要支持并实现这些接口,并注册到Linux内核中,即可安装和使用。借助VFS可以直接使用open()、read()、write()这样的系统调用操作文件,而无须考虑具体的文件系统和实际的存储介质。Linux用户程序可以通过read() 来读取ext3、NFS、XFS等文件系统的文件,也可以读取存储在SSD、HDD等不同存储介质的文件,无须考虑不同文件系统或者不同存储介质的差异。
通过VFS系统,Linux提供了通用的系统调用,可以跨越不同文件系统和介质之间执行,极大简化了用户访问不同文件系统的过程。另一方面,新的文件系统、新类型的存储介质,可以无须编译的情况下,动态加载到Linux中。"一切皆文件"是Linux的基本哲学之一,不仅是普通的文件,包括目录、字符设备、块设备、套接字等,都可以以文件的方式被对待。实现这一行为的基础,正是Linux的虚拟文件系统机制。
虚拟文件系统总图

FUSE简介

FUSE,implementing filesystems in user space,在用户空间可定制实现文件系统是有需求的,即用户可通过fuse在用户空间来定制实现自己的文件系统。并且,用户空间下调试工具丰富,出问题不会导致系统崩溃,开发难度相对较低,开发周期较短。

system calls

内核空间与用户空间是程序执行的两种不同状态,通过系统调用、异常、硬件中断,能够实现从用户空间到内核空间的转移。一般情况下,用户进程是不能访问内核的。Linux内核设置了一套用于实现各子系统功能的接口,实现用户进程访问内核所在的内存空间、访问硬件资源等,这些接口称为系统调用(SCI)。
User Space and Kernel Space
如图所示,系统调用和普通函数是有区别的,虽然他们看起来非常相似,主要区别在于系统调用由操作系统内核实现,运行于内核态,普通函数由函数库或用户自己实现,运行于用户态。

syscall 内核空间

  • 系统调用的函数原型的指针:位于文件kernel/arch/xxx/kernel,指定了目标函数的入口地址。
  • 内核空间的系统调用号:内核中每个syscall都有对应的唯一系统调用号,系统调用号宏定义于文件kernel/include/uapi/asm-generic/unistd.h。
    例如:kill()格式
/* kernel/signal.c */
#define __NR_kill 129
__SYSCALL(__NR_kill, sys_kill)
  • 系统调用的函数声明:位于文件kernel/include/linux/syscalls.h,格式为 asmlinkage long sys_xxx(args …)。
  • 系统调用的函数实现:不同函数位于不同文件,比如kill()位于kernel/kernel/signal.c文件,格式为SYSCALL_DEFINEx(xxx …),宏定义SYSCALL_DEFINEx(xxx,…),展开后对应的方法则是sys_xxx,方法参数的个数x,对应与SYSCALL_DEFINEx,kill(int pid,int sig)方法共两个参数,则对应于方法SYSCALL_DEFINE2(kill,pid_t,pid,int,sig)。

syscall 用户空间

  • 用户空间的方法xxx,对应系统调用方法则是sys_xxx。例如:用户空间kill方法对应系统调用的sys_kill,从/kernel/include/uapi/asm-generic/unistd.h查看关键字sys_kill,kill对应的函数实现位于/kernel/signal.c文件,不同函数位于不同文件。
    系统调用kill添加了sys_前缀,声明sys_kill()函数,由于之前64位Linux存在CVE-2009-2009漏洞,就是32位参数存放在64位寄存器,修改符号扩展可能导致产生一个非法内存地址,从而导致系统崩溃或者提升权限。为了修复这个问题,把寄存器高位清零,但做起来比较困难,为了尽可能少的修改,将调用参数统一采用long型来接收,再强转为相应参数。

proc

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值