【Linux技术】探究linux内核,超详细解析子系统

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介:

Perface


   前面已经写过一篇嵌入式linux内核的五个子系统,概括性比较强,也比较简略,现在对其进行补充说明。

   仅留此笔记,待日后查看及补充!



Linux内核的子系统


   内核是操作系统的核心。Linux内核提供很多基本功能,如虚拟内存、多任务、共享库、需求加载、共享写时拷贝(Copy-On-Write)以及网络功能等。增加各种不同功能导致内核代码不断增加。

   Linux内核把不同功能分成不同的子系统的方法,通过一种整体的结构把各种功能集合在一起,提高了工作效率。同时还提供动态加载模块的方式,为动态修改内核功能提供了灵活性。



系统调用接口


   用户程序通过软件中断后,调用系统内核提供的功能,这个在用户空间和内核提供的服务之间的接口称为系统调用。


   系统调用是Linux内核提供的,用户空间无法直接使用系统调用。在用户进程使用系统调用必须跨越应用程序和内核的界限。Linux内核向用户提供了统一的系统调用接口,但是在不同处理器上系统调用的方法各不相同。Linux内核提供了大量的系统调用,现在从系统调用的基本原理出发探究Linux系统调用的方法。


   这是在一个用户进程中通过GNU C库进行的系统调用示意图,系统调用通过同一个入口点传入内核。以i386体系结构为例,约定使用EAX寄存器标记系统调用。

   当加载了系统C库调用的索引和参数时,就会调用0x80软件中断,它将执行system_call函数,这个函数按照EAX寄存器内容的标示处理所有的系统调用。经过几个单元测试,会使用EAX寄存器的内容的索引查system_call_table表得到系统调用的入口,然后执行系统调用。从系统调用返回后,最终执行system_exit,并调用resume_userspace函数返回用户空间。

   linux内核系统调用的核心是系统多路分解表。最终通过EAX寄存器的系统调用标识和索引值从对应的系统调用表中查出对应系统调用的入口地址,然后执行系统调用。

   linux系统调用并不单层的调用关系,有的系统调用会由内核进行多次分解,例如socket调用,所有socket相关的系统调用都与_NR_socketcall系统调用关联在一起,通过另外一个适当的参数获得适当的调用。



进程管理子系统


   当用户使用系统提供的库函数进行进程编程,用户可以动态地创建进程,进程之间还有等待,互斥等操作,这些操作都是由linux内核来实现的。linux内核通过进程管理子系统实现了进程有关的操作,在linux系统上,所有的计算工作都是通过进程表现的,进程可以是短期的(执行一个命令),也可以是长期的(一种网络服务)。linux系统是一种动态系统,通过进程管理能够适应不断变化的计算需求。

   在用户空间,进程是由进程标示符(PID)表示的。从用户角度看,一个PID是一个数字值,可以唯一标识一个进程,一个PID值在进程的整个生命周期中不会更改,但是PID可以在进程销毁后被重新使用。创建进程可以使用几种方式,可以创建一个新的进程,也可以创建当前进程的子进程。

在linux内核空间,每个进程都有一个独立的数据结构,用来保存该进程的ID、优先级、地址的空间等信息,这个结构也被称做进程控制块(Process Control Block)。所谓的进程管理就是对进程控制块的管理。

   linux的进程是通过fork()函数系统调用产生的。调用fork()的进程叫做父进程,生成的进程叫做子进程。子进程被创建的时候,除了进程ID外,其它数据结构与父进程完全一致。在fork()系统调用创建内存之后,子进程马上被加入内核的进程调试队列,然后使用exec()系统调用,把程序的代码加入到子进程的地址空间,之后子进程就开始执行自己的代码。

   在一个系统上可以有多个进程,但是一般情况下只有一个CPU,在同一个时刻只能有一个进程在工作,即使有多个CPU,也不可能和进程的数量一样多。如果让若干的进程都能在CPU上工作,这就是进程管理子系统的工作。linux内核设计了存放进程队列的结构,在一个系统上会有若干队列,分别存放不同状态的进程。一个进程可以有若干状态,具体是由操作系统来定义的,但是至少包含运行态、就绪态和等待3种状态,内核设计了对应的队列存放对应状态的进程控制块。

   当一个用户进程被加载后,会进入就绪态,被加入到就绪态队列,CPU时间被轮转到就绪态队列后,切换到进程的代码,进程被执行,当进程的时间片到了以后被换出。如果进程发生I/O操作也会被提前被换出,并且存放到等待队列,当I/O请求返回后,进程又被放入就绪队列。

   linux系统对进程队列的管理设计了若干不同的方法,主要的目的是提高进程调试的稳定性。



内核管理子系统


   内存是计算机的重要资源,也是内核的的重要部分。使用虚拟内存技术的计算机,内存管理的硬件按照分页方式管理内存。分页方式是把计算机系统的物理内存按照相同大小等分,每个内存分片称作内存页,通常内存页大小是4KB。Linux内核的内存管理子系统管理虚拟内存与物理内存之间的映射关系,以及系统可用内存空间。

   内存管理要管理的不仅是4KB缓冲区。Linux提供了对4KB缓冲区的抽象,例如slab分配器。这种内存管理模式使用4KB缓冲区为基数,然后从中分配结构,并跟踪内存页使用情况,比如哪些内存页是满的,哪些页面没有完全使用,哪些页面为空。这样就允许该模式根据系统需要来动态调整内存使用。

   在支持多用户的系统上,由于内存占用的增大,容易出现物理内存被消耗尽的情况。为了解决物理内存被耗尽的问题,内存管理子系统规定页面可以移出内存并放入磁盘中,这个过程称为交换。内存管理的源代码可以在./linux/mm中找到



虚拟文件系统


   在不同格式的文件分区上,程序都可以正确地读写文件,并且结果是一样的。有时在使用linux系统的时候发现,可以在不同类型的文件分区内直接复制文件,对应用程序来说,并不知道文件系统的类型,甚至不知道文件的类型,这就是虚拟文件系统在背后做的工作。虚拟文件系统屏蔽了不同文件系统间的差异,向用户提供了统一的接口。

   虚拟文件系统,即VFS(Virtual File System)是Linux内核中的一个软件抽象层。它通过一些数据结构及其方法向实际的文件系统如ext2,vfat等提供接口机制。通过使用同一套文件 I/O 系统调用即可对Linux中的任意文件进行操作而无需考虑其所在的具体文件系统格式;更进一步,文件操作可以在不同文件系统之间进行。

   在linux系统中,一切都可以被看做是文件。不仅普通的文本文件、目录可以当做文件进行处理,而且字符设备、块设备、套接字等都可以被当做文件进行处理。这些文件虽然类型不同,但是却使用同一种操作方法。这也是UNIX/Linux设计的基本哲学之一。

   虚拟文件系统(简称VFS)是实现“一切都是文件”特性的关键,是Linux内核的一个软件层,向用户空间的程序提供文件系统接口;同时提供了内核中的一个抽象功能,允许不同类型的文件系统存在。VFS可以被理解为一种抽象的接口标准,系统中所有的文件系统不仅依靠VFS共存,也依靠VFS协同工作。

   为了能够支持不同的文件系统,VFS定义了所有文件系统都支持的、最基本的一个概念上的接口和数据结构在实现一个具体的文件系统的时候,需要向VFS提供符合VFS标准的接口和数据结构,不同的文件系统可能在实体概念上有差别,但是使用VFS接口时需要和VFS定义的概念保持一致,只有这样,才能实现对用户的文件系统无关性。VFS隐藏了具体文件系统的操作细节,所以,在VFS这一层以及内核其他部分看来,所有的文件系统都是相同的。

   对文件系统访问的系统调用通过VFS软件层处理,VFS根据访问的请求调用不同的文件系统驱动的函数处理用户的请求。文件系统的代码在访问物理设备的时候,需要使用物理设备驱动访问真正的硬件。



网络堆栈


   编写网络应用程序,使用socket通过TCP/IP协议与其他机器通信,和前面介绍的内核子系统相似,socket相关的函数也是通过内核的子系统完成的,担当这部分任务的是内核的网络子系统,有时也把这部分代码称为“网络堆栈”。

   Linux内核提供了优秀的网络处理能力和功能,这与网络堆栈代码的设计思想是分不开的,Linux的网络堆栈部分沿袭了传统的层次结构,网络数据从用户进程到达实际的网络设备需要四个层次。

   实际上,在每层里面还可以分为好多层次,数据传输的路径是按照层次来的,不能跨越某个层次。

   linux网络子系统对网络层次采用了类似面向对象的设计思路,把需要处理的层次抽象为不同的实体,并且定义了实体之间的关系和数据处理流程。

   可以看出,linux内核网络子系统定义了4个实体:

   网络协议

   网络协议可以理解为一种语言,用于网络中不同设备之间的通信,是一种通信的规范。

   套接字

   套接字是内核与用户程序的接口,一个套接字对应一个数据连接,并且向用户提供了文件I/O,用户可以像操作文件一样在数据连接上收发数据,具体的协议处理由网络协议部分处理。套接字是用户使用网络的接口。

    设备接口

   设备接口是网络子系统中软件和硬件的接口,用户的数据最终是需要通过网络硬件设备发送和接收的,网络设备千差万别,设备驱动也不尽相同,通过设备接口屏蔽了具体设备驱动的差异。

   网络缓冲区

   网络缓冲区也称为套接字缓冲区(sk_buff),是网络子系统中的一个重要结构。网络传输数据存在许多不定因素,除了物理设备对传输数据的限制(例如MMU),网络受到干扰、丢包、重传等,都会造成数据的不稳定,网络缓冲区通过对网络数据的重新整理,使业务处理的数据包是完整的。网络缓冲区是内存中的一块缓冲区,是网络系统与内存管理的接口。



设备驱动


   随着现代计算机外部设备的不断增加,起来越多的设备被开发出来,计算机总线的发展也很迅速,操作系统的功能也在不断提升,系统软件越来越复杂,对于外部设备的访问已经不能像DOS年代那样直接访问设备的硬件了,几乎所有的设备都需要设备驱动程序。现代操作系统几乎都提供了具体硬件无关的设备驱动接口,这样的好处是屏蔽了具体设备的操作细节,用户通过操作系统提供的接口就可以访问设备,而具体设备的操作细节由设备驱动完成,驱动程序开发人员只需要向操作系统提供相应接口即可。

   与其他的操作系统对设备进行复杂的分类不同,linux内核把设备分成3类:块设备、字符设备和网络设备。这是一种抽象的分类方法,从设备的特性抽象出了3种不同的数据读写方式。

   块设备

   块设备的概念是一次I/O操作可以操作多个字节的数据,数据读写有缓冲,当读写缓冲满以后才会传送数据,比如硬盘可以一次读取一个扇区的数据,同时,块设备支持随机读写操作,可以从指定的位置读写数据;

   字符设备

   字符设备的访问方式是线性的,并且可以按照字节的方式访问,比如串口设备,可以按照字符读写数据,但是只能按照顺序操作,不能指定某个地址访问;

   网络设备

   网络设备与前面的两种方式相比,比较特殊,内核专门把这类驱动单独划分出来,网络设备可以通过套接口读写数据。

   Linux内核对设备按照主设备号和从设备号的方法访问,主设备号描述控制设备的驱动程序,从设备号区分同一个驱动程序的不同设备。也就是说,主设备号和设备驱动程序对应,代表某一类型的设备,从设备号和具体设备对应,代表同一类的设备编号。如使用IDE接口的两个硬盘,主设备号都相同,但是从设备号不同。Linux提供了mknod命令创建设备驱动程序的描述文件。Linux内核这种主从设备号的分类方法可以很好的管理设备。


   上图为用户程序从外部设备请求数据的流程,可以看出,用户进程访问外部设备是通过设备无关软件进行的,设备无关软件是内核中的各种软件抽象层如VFS。当用户向外部设备发起数据请求时,通过设备无关软件会调用设备的相应驱动程序,驱动程序通过总线或者寄存器访问外部硬件设备,发起请求,驱动程序会在初始化的时候向系统的中断向量表注册一个中断处理程序,外部硬件有请求返回的时候会发出中断信号,内核会调用响应的中断处理程序,中断处理程序从硬件的寄存器读取返回的数据,然后转交给内核中的设备服务程序,由设备服务程序把数据交给设备无关的软件,最终到达用户进程。

   linux的设备驱动涉及其他子系统,如内存管理、中断管理、硬件寄存器和总线访问等,此外,大多数的驱动程序为了使用方便被设计成模块,还需要设计到内核模块的处理。

   驱动的编写和调试是一个复杂的事情,驱动的代码占用了linux内核代码量的一半以上。



依赖体系结构的代码


   Linux内核支持众多体系结构,内核把与设备无关的代码放在arch目录,对应的头文件放在include/asm-<体系名称>目录下。这样的划分代码结构清晰,同时提高了代码的复用率。在arch目录里,每个子目录对应一种体系结构,存放这种体系结构对应的代码,如果代码较多会单独建立一个目录,例如arch/arm目录下,有一个kernel目录,存放的是kernel目录中在arm体系结构上特有的函数或者实现方法;在arch/i386目录存放了Intel X86体系结构的代码,不仅有kernel目录,而且还有多个目录,例如mm目录包含了x86体系上内存管理的实现方法,math-emu包含了x86体系上浮点数模拟的实现等。读者在阅读内核代码的时候可以从一个体系结构代码入手,对不同体系结构移植代码的主要工作是arch里面的代码。



本文转自infohacker 51CTO博客,原文链接:http://blog.51cto.com/liucw/1203700

相关文章
|
5天前
|
算法 Linux 调度
深入理解Linux内核调度器:从基础到优化####
本文旨在通过剖析Linux操作系统的心脏——内核调度器,为读者揭开其高效管理CPU资源的神秘面纱。不同于传统的摘要概述,本文将直接以一段精简代码片段作为引子,展示一个简化版的任务调度逻辑,随后逐步深入,详细探讨Linux内核调度器的工作原理、关键数据结构、调度算法演变以及性能调优策略,旨在为开发者与系统管理员提供一份实用的技术指南。 ####
24 4
|
7天前
|
缓存 资源调度 安全
深入探索Linux操作系统的心脏——内核配置与优化####
本文作为一篇技术性深度解析文章,旨在引领读者踏上一场揭秘Linux内核配置与优化的奇妙之旅。不同于传统的摘要概述,本文将以实战为导向,直接跳入核心内容,探讨如何通过精细调整内核参数来提升系统性能、增强安全性及实现资源高效利用。从基础概念到高级技巧,逐步揭示那些隐藏在命令行背后的强大功能,为系统管理员和高级用户打开一扇通往极致性能与定制化体验的大门。 --- ###
27 9
|
6天前
|
缓存 负载均衡 Linux
深入理解Linux内核调度器
本文探讨了Linux操作系统核心组件之一——内核调度器的工作原理和设计哲学。不同于常规的技术文章,本摘要旨在提供一种全新的视角来审视Linux内核的调度机制,通过分析其对系统性能的影响以及在多核处理器环境下的表现,揭示调度器如何平衡公平性和效率。文章进一步讨论了完全公平调度器(CFS)的设计细节,包括它如何处理不同优先级的任务、如何进行负载均衡以及它是如何适应现代多核架构的挑战。此外,本文还简要概述了Linux调度器的未来发展方向,包括对实时任务支持的改进和对异构计算环境的适应性。
24 6
|
7天前
|
缓存 Linux 开发者
Linux内核中的并发控制机制:深入理解与应用####
【10月更文挑战第21天】 本文旨在为读者提供一个全面的指南,探讨Linux操作系统中用于实现多线程和进程间同步的关键技术——并发控制机制。通过剖析互斥锁、自旋锁、读写锁等核心概念及其在实际场景中的应用,本文将帮助开发者更好地理解和运用这些工具来构建高效且稳定的应用程序。 ####
26 5
|
7天前
|
算法 Unix Linux
深入理解Linux内核调度器:原理与优化
本文探讨了Linux操作系统的心脏——内核调度器(Scheduler)的工作原理,以及如何通过参数调整和代码优化来提高系统性能。不同于常规摘要仅概述内容,本摘要旨在激发读者对Linux内核调度机制深层次运作的兴趣,并简要介绍文章将覆盖的关键话题,如调度算法、实时性增强及节能策略等。
|
7天前
|
缓存 运维 网络协议
深入Linux内核架构:操作系统的核心奥秘
深入Linux内核架构:操作系统的核心奥秘
24 2
|
7天前
|
存储 消息中间件 算法
深入探索操作系统的心脏——内核机制解析
本文旨在揭示操作系统核心——内核的工作原理,通过剖析其关键组件与机制,为读者提供一个清晰的内核结构图景。不同于常规摘要的概述性内容,本文摘要将直接聚焦于内核的核心概念、主要功能以及其在系统管理中扮演的角色,旨在激发读者对操作系统深层次运作原理的兴趣与理解。
|
5天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
16 2
|
1月前
|
缓存 Java 程序员
Map - LinkedHashSet&Map源码解析
Map - LinkedHashSet&Map源码解析
67 0
|
1月前
|
算法 Java 容器
Map - HashSet & HashMap 源码解析
Map - HashSet & HashMap 源码解析
52 0