Press "Enter" to skip to content

Tag: Linux

内核IO子系统及其注册接口的系统论解析

无论是从事哪种逻辑级别编程的程序员,在编程过程中都会使用各种模块、框架或系统提供的函数接口。而一些接口也会被冠名某逻辑名字,说明其在系统的逻辑角色,其中对驱动开发人员最重要的接口叫【注册接口】[注1]。为什么说它重要呢?因为【注册接口】是驱动开发者作业的起点和重要内容之一。驱动开发者作业的另一项重要内容是对【系统使用接口】——【文件接口】file_operations的实现——对这些接口的标准抽象语义[注2]进行实际具体硬件转译。

注1:关于【注册】的确切语义,请看《系统接口的比较和再认识》。

注2:接口的标准抽象语义由标准文档定义,如POSIX,但完整的具体定义是什么,有待再究……

很多驱动开发的初学者面对内核的时候都有不知所措的感觉,从【某IO子系统】的注册接口为起点,不失为一个很好的策略。本文简要地分析所谓内核【IO子系统】以及尝试以最简单的IO子系统——字符IO子系统的注册接口为例子说明一下。

手工定制Linux系统

引子

陆续研究LFS(Linux from Scratch)有一段时了,发现一些问题,比如无论是官网教程,还是一些第三方的分析文章,都有一个共同点,描述过于的形而下,也就是太过于注重细节。过于实际和过于理论有同一样的缺点——不利提高学习效率[注]。另外,【形而下】风格的教程给人不通透的感觉,例如你是把东西做出来了,可是你很可能有几步是不解的,只是盲目照做。

到目前为止,我看官方教程没有提供足够的背景信息,因而它不是面向一般Linux普通用户,包括Linux下的应用开发者,它面向系统开发的程序员。如果定制Linux有客观真实的价值,并且定制OS是一项艰巨的任务,那么像这样的第三方补充信息还需要很多,本文只是以小结一下学习经验,对LFS教程补充一些【形而上】方面的信息,弥补一下学习市场对【自上而下】方式的缺失。

编译程序与操作系统的关系

和很多程序员一样,编译器到目前为止对我还存在一些让我迷惑的地方。比如编译器与操作系统的关系,编译器与CPU的关系,动态链接器从哪查找共享库等。让人困惑的原因有几,第一是编译器的功能角色特殊,编译器是生成程序的程序;第二是编译过程变得越来越复杂[注],一支编译器支持多种程序语言、支持共享库、编译优化,编译与链接可分开等;第三,操作系统的介入。本文试着从第三点——操作系统介入编译过程后对编译器的影响,看看操作系统与编译器有什么关系。

注:编译过程变复杂源于计算机应用变复杂。例如应用程序项目越来越庞大,功能越来越多;为了管理大项目,拆分源程序文件为多个;为了提高程序的性能,目标程序文件的链接期被延迟到运行前;为了程序的灵活安装和升级,引入各种脚本工具,如make,configure(shell)。

Linux内核的概念体系结构

2.3 Overview of the Kernel Structure

下图展示了Linux内核的五个子系统之间的关系,箭头由依赖子系统指向被依赖的子系统:

Figure 2.2: Kernel Subsystem Overview

从图中可以看出来,【进程调度子系统】是内核的核心子系统:其它子系统都对【进程调度子系统】有依赖,因为其它子系统都有暂停(suspend)和恢复(resume)进程的功能需要。通常,某子系统会在等待硬件操作而暂停进程,尔后在操作完成后恢复进程。例如,当进程发一条网络消息后,网络接口可能需要暂停进程,直到硬件成功地完成信息的发送。在消息被发出后,网络接口返回一个代码表示操作的成功与否,然后恢复进程。其它子系统(内存管理子系统、虚拟文件系统、进程互通信子系统)都有类似的原因而依赖于进程调度子系统。

使用Cscope阅读Linux源码

cscope是神马

cscope是什么东东?先看看官方定义:

“cscope is an interactive, screen-oriented tool that allows the user to browse through C source files for specified elements of code.”

简译并补充为:“cscope 是一支面向屏幕的(与面向行相对)交互式C源代码浏览程序。它可以对C程序源代码的元素(例如各种标号:变量,宏以及函数调用等)进行索引,提供简单的字符查询界面,用户可根据【元素名】对其定义及引用进行查询查看。”

以上是cscope的一个严格的完整定义,此定义表明cscope有两个主要功能,第一是对C源码文件进行[符号索引];第二是提供简单的查询接口。如果你一般只使用像VIM这样的高级工具编辑和阅读源码,完全可把cscope理解成生成源码[符号引用数据库]的工具。

系统级编程是什么?

System Programming

过去的Unix编程是没有系统不系统之分的。即便是开发 X Window也是在系统级(system-level)编程,看到系统的所有API。现代的操作系统编程有所谓[系统级编程],使用与[应用编程]不同的API(System programming API) 。

从编程的形式和耗费心力上,系统编程与应用编程没有本质区别,这也意味着一个经验丰富的应用程序员转向系统编程难度不大。系统编程与应用编程的不同在于:

  • 第一,系统编程更接近硬件;系统程序员必须熟悉硬件环境和操作系统环境;相对的,应用程序员更多是熟悉应用的环境;
  • 第二,系统编程使用的函数库和库函数调用方法与应用编程有一些不同。比如,在调用系统调用(syscall)时使用所谓的陷入方式,也就是软中断方式。

近年来,随计算应用深化,应用编程有远离系统编程的趋向。不过,这并不能说或者预言系统编程的末日的到来。因为,有人用Javascript或C#写应用就要有人写它的解释器和运行时。

此外,操作系统代码只能使用系统级编程。

引导程序再认识

在32位以上的现代计算机里,bootloader,中文叫【引导程序】,是一支将计算机从【开机重置态】或者叫【物理裸机态】过渡到【逻辑虚拟机态】的程序。这个引导过程叫Booting。所谓【物理裸机态】指机器开机时最基本的功能状态,它是机器可用的基本保证,由厂商定义和提供实现。【物理裸机态】可以理解为物理态,而【逻辑虚拟机态】则是逻辑态,逻辑态有一些的标准,比如32位处理器的保护模式。计算机的逻辑态是现代计算机应用的客观需要,比如桌面GUI应用、多进程、互联网等,但由于成本、硬件局限等原因,目前的计算机还不能设计成开机就达到逻辑态的能力要求。bootloader在一段时间内仍在计算机应用中担当重要角色。

Linux根文件系统结构再认识

尽管Linux的根文件系统在形式表现上是一体的(所有数据目录均为根目录下的子目录),但实际它们是多个不同的【逻辑主体】(为了实现不同的逻辑功能)组合在一起的,参见下图。这也解释了为什么有些节点(如/var)可以挂接到不同磁盘分区上。划分这些逻辑部分对清晰把握根文件系统结构,以及整个系统的行为都有很大的好处。在划分之前,先看看“文件系统结构标准”。