轻松与Linux内核交流

时间:2022-10-30 02:56:50

Linux通过对系统调用的封装,为用户提供了多种内核接口,通过这些接口,Linux将内核信息输出到用户空间,并可以读取用户空间对内核的配置,从而方便的进行系统地动态管理。

作为使用虚拟内存技术的操作系统,Linux的用户应用程序通过系统调用来嵌入内核,存取内核数据并请求内核服务。系统调用是内核和用户之间数据交换的唯一途径。

然而用户空间应用不一定必须通过直接调用系统调用的方法来获取内核数据,因为Linux通过对系统调用的封装,为用户提供了多种内核接口,如基于虚拟文件系统的 sysfs、procfs、debugfs,以及基于socket编程的netlink和genetlink等等。通过这些接口,Linux将内核信息输出到用户空间,并可以从用户空间读取用户对内核的配置,从而方便的实现内核与用户的数据交换。选择合适的内核接口可以使用户空间应用的开发工作事半功倍。本文将对Linux提供的这些接口机制进行简要的介绍,使读者了解并可以选择合适的接口来轻松地与Linux内核交流。

基于虚拟文件系统的内核接口

Linux的文件系统实现了一种抽象文件模型――虚拟文件系统(Virtual File System)。简单来说,它是一个文件系统的软件抽象层,向下支持内核中多个不同文件系统的同时存在,向上为用户程序提供统一接口。

虚拟文件系统本身抽象了不同文件系统共同部分,对用户屏蔽了具体的操作,使得用户不用再去关心文件所属的文件系统的问题,实现了各个文件系统的良好兼容。当一个最新推出的文件系统普遍被采用时,Linux借助虚拟文件系统的强大功能,可以毫不费力的实现新文件系统在本地的组织运行,同时能不干扰其他已经装配在本地的其他文件系统。

统一的接口使得用户可以使用同样的方式访问各种不同格式的文件系统,支持不同的文件系统使得内核编程非常灵活,这样的特点促使Linux产生了多种基于虚拟文件系统的内核接口机制,如sysfs、procfs、debugfs等等。

本章将对这几种基于虚拟文件系统的内核接口做详细的介绍。

1.sysfs

Sysfs是Linux2.6内核的新的特性,它是一种把内核对象(Kernel Object)、内核对象的属性(Attribute)以及它们之间的关系开放给用户的虚拟文件系统。它主要应用于系统和设备控制。

通过sysfs,Linux将系统中的内核对象(多为设备)转化为sysfs下的目录,将内核对象的属性转化为文件,从而将内核对象的拓扑结构转化为一个简单的文件系统直观地展现在用户面前。用户通过操作文件,可以对内核变量进行读操作和有选择的写操作。

Sysfs的根目录下主要有:block,bus,class,firmware,module,platform、power和devices等。Block目录下为系统中每个注册的块设备建立了一个目录,这些目录下又包含了对应于块设备的各个分区的目录。Bus目录下显示的为系统中的各种总线类型,每一个总线类型又包括两个子目录,device和driver,分别代表该总线类型下存在的设备和与该总线类型的设备所绑定的驱动。Class目录显示的是系统中的设备按照功能归类后的设备结构图,其中每一类设备都有其相同的设备属性和编程接口。Firmware目录下主要为一些底层子系统(比如ACPI,EDD,EFI,等等)。Power主要用于能耗管理。Platform下存放的是直接与处理器总线相连的平台设备及其驱动的信息。最主要的是Device目录,它反映的是系统真实的设备拓扑结构。device下的文件组织结构是内核中的各设备及其层次架构的映射。

图1 Linux中父子关系的内核对象的描述

图1列出了Linux对父子关系的内核对象(设备)拓扑结构的描述。在硬件结构中这种父子关系的设备十分常见,如pci总线和pci设备,i8042控制器和PS/2鼠标、键盘等等。驱动模型(driver model)通过sysfs虚拟文件系统将设备的拓扑关系完整地展现在用户空间。

图2给出了父子关系的内核对象(设备)在sysfs中的文件组织结构。由图2可以看出作为Linux的内核接口,sysfs虚拟文件系统能够清晰地将内核对象(Kernel Object),内核对象的属性(Attribute)以及它们之间的关系开放给用户空间,同时它为Linux在用户空间实现动态的设备管理(udev)提供了良好的基础。

2.procfs

procfs虚拟文件系统在开发之初主要用于提供有关系统中进程的信息,但是由于其方便易用,内核中的其他模块也开始使用它作为对用户空间的接口。在sysfs虚拟文件系统出现之前,procfs已经得到了广泛的应用。

procfs主要用于获取系统信息以及在运行时改变特定内核参数(sysctl)。系统信息主要包括两部分:进程相关的信息和内核数据。

Procfs为每一个系统中运行的进程在proc下创建了一个以进程号命名的子目录。在该目录下的文件输出当前进程的相关信息。例如,当前系统中运行着进程号为10的进程,读取/proc/10/status文件即可获得该进程的状态信息。

与进程相关信息类似,/proc根目录下的文件输出的是内核的运行数据。如“cat /proc/interrupts”输出系统当前的中断号的分配情况以及它们各自的响应次数,“cat /proc/cpuinfo”输出详细的处理器硬件信息,“/proc/cmdline”则给出了内核命令行参数。

与前面这些只读的系统信息文件相比,/proc/sys不仅可以提供系统信息,它同时允许用户来修改内核的参数。/proc/sys下的文件可以用来监控Linux内核的行为。我们可以通过procfs不需要从新编译内核或重启而在运行时改变Linux内核行为。通过修改/proc/sys可以优化系统,但在使用时必须十分小心,错误的操作会导致系统崩溃。Kernel_source/Documentation/filesystem/procfs.txt详细列出了procfs接口及其所代表的意义和用法,如果想使用这些内核接口,不妨去读一下源码,以确保自己能够进行正确操作。

Procfs做为Linux的内核接口被广泛使用,但需要注意的是,正是由于procfs中充斥着各式各样的系统信息,其混乱的结构也为人所诟病。现在很多procfs下的系统信息经重新设计后被逐步移植到sysfs中,而procfs也将逐渐被sysfs所取代。

3.debugfs

Debugfs是由Greg Kroah-Hartman开发的一个小型的虚拟文件系统。

与sysfs和procfs主要用于向用户输出系统信息,并在用户空间动态修改内核运行参数的功能不同。Debugfs目的在于将有用的调试信息输出到用户空间从而判断系统行为为何出现异常。

该文件系统小且易用,可以在配置内核时选择是否构建到内核中。在不编译它的情况下,使用它的内核部分不需要做任何改动。

图2 父子关系的内核对象(设备)在sysfs中的文件组织结构

Debugfs的使用非常简单,根据debugfs提供的内核编程接口在debugfs中创建路径和文件,并选择输出或读入相应格式的调试信息即可,如布尔型数据,8位、16位、32位、64位无符号整数型数据,或者二进制数据流。

相比与我们常用的printk来输出需要的调试信息,debugfs有着以下好处:首先,操作灵活,通常内核开发人员只在某些特定情形下需要调试信息。通过debugfs我们可以在适当的时间去读取,而prink只能无条件的持续输出。其次可以在用户空间通过修改一些信息来控制内核行为,这也是printk无法帮助我们实现的。

虽然通过sysfs和procfs也可以达到这样的效果,但综合衡量而言,debugfs无疑是最好的。这是因为:一、procfs的混乱的组织结构不允许我们再添加这些功能,况且与procfs相比,debugfs只需要很少的代码;二、sysfs中已经存在了大量的调试信息,但是sysfs设计的初衷是为了实现在用户空间更加方便的系统管理,而且sysfs中每个文件只能对应于内核对象的一个属性,这样就使得它在输出复杂的数据结构信息时变得非常困难。相比之下,debugfs作为专门为向用户空间输出调试信息而设计的虚拟文件系统,它的方便简洁易用性使得它成为输出内核调试信息的首选。

基于socket编程的netlink

除了基于虚拟文件系统的Linux内核接口, netlinksocket也用来做内核和用户的双向数据传输。它不仅可以灵活方便的应用于内核与用户空间的通讯,同时还可以用于用户之间以及内核内部的相互通讯。

Netlink分为多种不同的协议,每种协议用来处理特定的服务。在最近的内核(如2.6.21)中已经存在了19种不同的netlink协议,应用领域更是多种多样,如:Linux防火墙、socket监视、netfilter日志、SELinux事件通知等等,具体应用可以参考kernel_source/include/linux/netlink.h。

netlink协议族是Linux内核中网络的一个固定部分, 一旦在内核配置中选了网络支持netlink就会被编入内核而不能单独去掉,它是Linux所特有的一种非常灵活的通信机制。netlink的实现源码在net/netlink目录下,主要是net/netlink/af_netlink.c文件。

用户使用标准的socket用户编程接口就可以使用 netlink 提供的强大功能。如socket()用于创建一个 netlink socket, bind()用于把一个打开的 netlink socket与netlink源socket地址绑定在一起。sendmsg()和recvmsg()分别用于收/发netlink的消息,而close()则是关闭一个netlink的socket。内核则需要使用专门的内核编程接口来使用netlink。

与sysfs和procfs相比,netlink有自己独特的特点,这些特点决定了netlink作为Linux的内核接口,其更适用于内核和用户的通信。

在内核与用户态应用之间传递的消息保存在socket缓存队列中,发送消息只是把消息保存在接收者的socket的接收队列,而不需要等待接收者收到消息,若使用其他同步通信机制传送大量数据时会影响调度力度。

链 接

OSD(Operating System Distributor)

OSD是指操作系统发行商,例如Red Hat、SuSE等。这些公司负责集成,测试,销售Linux,并且提供技术支持来获取利润。当前的趋势是他们会采用双产品线的策略:分为“consumer release” 和 “Enterprise release”。Consumer release普遍采用了比较新的技术和代码,而企业版则采用的是比较保守的策略。这样做的好处是在新的企业版之前,那些新的代码和技术已经被大量的普通用户测试过了,该出现的问题都得到了充分曝光,并的到了合理解决,从而这些新的技术和代码可以非常顺利地进入对产品质量要求更高的商业用户市场。因为企业版的技术支持是收费的,而且是OSD们主要的收入来源。所以OSD会采用这种比较稳妥的策略来保证企业版的质量,降低风险。由于OSD们直接从baseline获取代码,因此这些OSD被称为是baseline kernel的downstream。同时OSD会根据自己的需要对baseline kernel进行改进,而且他们的修改经常是非常大的。

上一篇:人才―创新的基础 下一篇:发现Linux KVM