Linux内核工程导论——用户空间设备管理

用户空间设备管理

用户空间所能见到的所有设备都放在/dev目录下(当然,只是一个目录,是可以变化的),文件系统所在的分区被当成一个单独的设备也放在该目录下。以前的2.4版本的曾经出现过devfs,这个思路非常好,在内核态实现对磁盘设备的动态管理。可以做到当用户访问一个设备的设备的时候,devfs驱动才会去加载该设备的驱动。甚至每个节点的设备号都是动态获得的。但是该机制的作者不再维护他的代码,linux成员经过讨论,使用用户态的udev代替内核态的devfs,所以现在的devfs已经废弃了。用户态的udev在设备发现的时候加载设备驱动,动态的在/dev目录下创建节点。/dev只是一个目录,而不是挂载devfs文件系统了。

当然,这个udev只是个应用的程序,还可以用别的程序替代,例如busybox就实现了mdev完成同样的工作。

设备变化通知用户端
udevd

系统在启动时会对设备做检测,系统启动后设备的变化也应该能够识别。本质上,启动时发现的硬件也是一种设备的变化。这种设备的变化仅仅是内核知道是没有意义的,因为使用设备的用户是用户空间的程序,内核只是管理者,单管理却不能使用,资源就是无意义的存在。那么内核如何将设备的变动信息通知到用户程序?这个机制叫做uevent。

内核通过向用户空间发送uevent事件来通知用户空间程序设备资源的变化,事件所传递的变化的具体内容是通过uevent事件所附带的参数缓存来实现的。而用户空间对该事件进行响应的程序就叫做udevd(或者叫其他的名字),但是这种内核通知,用户响应的机制就叫做udev。

但是这只是目前的机制。linux是个不断演化的系统,之前为了完成相同的功能还使用过hotplug程序(有设备变动就执行一遍该程序,可重入的多次执行),还有devfs(提前在/dev目录创建了一堆节点文件,无动态性),而udevd是一个后台服务程序,不是像hotplug一样来一个信息执行一个程序副本。udevd这种处理消息的能力免除了可重入问题,加快了用户端响应内核设备变动的效率。

虽然说了udevd是用户端检测内核设备变动。那么其检测来干什么呢?这就是linux的机制所决定的。linux用户端要使用每个设备都要在/dev/目录中引用,除非更上层的封装(如mount),然而这也离不开/dev文件系统中对设备的引用。所以,对用户端应用来说,/dev/目录时他们与内核设备直接打交道的唯一途径。例如drm设备可以直接访问显卡,tty设备可以直接访问串口,sr0可以直接访问cdrom,sda可以直接访问磁盘。内核确认规定了如何使用各个设备,但是使用何种设备需要用户来指定(例如你修改一个文件整个操作都是在内核中驱动的,但是首先你得先把文件所在分区设别mount到你的文件系统中)。

所以,udevd最重要的功能就是创建dev下的设备节点。

但是并不是所有做这个事情的应用都用的udevd,这只是udev协议的一种广泛使用的实现软件,还有一个是busybox使用的mdev,也可以完成相同的功能。

另外,近代的操作系统倾向于把所有的服务程序纳入统一的管理,有的是管理用来在需要的使用启动该程序实体,例如inetd,有的则直接是整合了程序本身进管理程序,例如目前被开始广泛使用的systemd程序。如果你打开你的进程,你会发现在后台运行的可能不是udevd程序了,变成了/lib/systemd/systemd-udevd –daemon 服务。这就是被systemd统一管辖的结果。甚至在initrd中也是直接使用了这种服务,大有一统江湖的趋势。

那么,linux是用何种通信手段与用户端的服务程序通信的呢?答案是netlink。

其他应用程序对热插播事件的捕捉

是不是只有udevd通过监听netlink事件才能得到内核事件的变化?肯定不是。内核在实现的过程中考虑了各种情况,你甚至还可以像以前一样指定hotplug程序。但是内核在实现KObject机制的同时也顺便实现了这种功能,叫做uevent_helper。在用户空间是/sys/kernel/uevent_helper。通过向这个文件写入一个程序路径,linux的uevent就会顺便通知这个程序。

这个目录的存在是需要内核支持的,内核配置中的CONFIG_UEVENT_HELPER=y

CONFIG_UEVENT_HELPER_PATH="" 可以控制该机制。

设备类型

内核中定义的设备类型共有2种:字符类和块类。这些/dev目录下的设备并不一定都对应着具体的硬件(如zero、tty),有的一个硬件可能对应着多个节点(如sda、sda1)。大部分发挥特殊功能的设备都是字符设备,正是由于设备是可以虚拟的,所以诞生了框架设备这一种新的设备子类型。

input设备是一种字符设备,很多输入相关的设备都是使用这个input设备进行管理的。也就是说input虚拟设备是为其他输入设备服务的。

磁盘相关一般是sda、sdb等,这里的s代表是scsi设备。以前还经常出现hd、fd等,fd表示软盘,hd表示IDE硬盘。由于sata和scsi已经很大程度合并,软件上已经可以处理相同的命令,所以对于只关心软件的linux来说sata设备也是sd设备。sr是cdrom,一般也有一个cdrom节点文件。

tty是串口,一般会模拟很多出来,通过ctrl+alt+F1...F7分别调用。但还有一种模拟串口的方式是图形界面的pty。在ubuntu的程序打开一个terminal就是一个pty,p是伪装的意思。

Loop是回环设备,是块设备。其本身不是设备,将一个文件挂载到一个目录,这个文件就被认为是一个虚拟的磁盘,里面是有分区结构的。在设备中就是一个回环设备。

内核数据结构的面向用户组织KObject

linux内核用的一种组织数据的方式是实现一个结构体(或一种数据组织方式),为这个组织方式的每个元素定义了结构体。任何其他的部分想要使用这个数据组织方式只需要包含对应的结构体,就可以把自己安放在数据结构的特定位置。例如list数据结构的实现,还有一个很重要的就是KObject。

KObject也是内核预先设计好的数据结构组织方式,是一个树形的结构的实例。每一个KObject都是这棵树的一个节点,每一个KSet都是这棵树的一颗非叶子节点,里面包含了KSet或者一些KObject。按照定义,同样的,一个KSet也是一个KObject。包含KSet的可以是KSet,还可以是该组织方式定义的最高层的分层数据类型KSubsystem。从树的角度看,KSubsystem与KSet没有区别,但是这个数据组织方式是在树的基础上定义了,对最上层的树做了额外的区别于KSet的定义,就命名为KSubsystem。

操作用户空间设备节点的命令和工具

mknod

磁盘管理
MBR与GPT

见bootloader

LVM

概览

Linux和Windows都经常面对一个问题就是磁盘空间的划分不能有效的适用长时间的使用。动态的调整大小在Windows下有优秀的工具,但是非常费时,并且需要关机重启。这对个人用户来说不算什么,所以可以适用家用。但linux不只是家用,企业通常在有分区扩张需求的时候又不希望重启电脑,并且要较快的完成操作。这时LVM就诞生了。现在已经在发展LVM2。

LVM的主要思想是不以硬件的sda1、sdb2等物理分区为划分分区的手段,而是允许组织多个物理磁盘到一个分区。将很多磁盘组成卷组(volumegroup),然后在卷组上随意划分逻辑卷组(logicalvolumes)。

一个卷组叫做VG(Volume Group),物理磁盘叫做PV(PhysicalVolume),划分的逻辑卷组叫做LV(Logical Volume)。在一个PV上不像是以前划分为文件系统,而是划分为相同大小的存储单元叫PE(Physical Extents),默认一个PE的大小是4MB。PE是LVM可以寻址的最小单位。逻辑卷LV也被划分为可被寻址的基本单位,称为LE。在同一个卷组中,LE的大小和PE是相同的,并且一一对应。

和非LVM系统将包含分区信息的元数据保存在位于分区的起始位置的分区表中一样,逻辑卷以及卷组相关的元数据也是保存在位于物理卷起始处的VGDA(卷组描述符区域)中。VGDA包括以下内容:PV描述符、VG描述符、LV描述符、和一些PE描述符。

系统启动LVM时激活VG,并将VGDA加载至内存,来识别LV的实际物理存储位置。当系统进行I/O操作时,就会根据VGDA建立的映射机制来访问实际的物理位置。

LVM并不是取代文件系统,而是文件系统以下的分区手段。在创建完LVM之后,在LV上还要像传统分区一样进行文件系统格式化才能被使用。

优点与缺点

LVM的最大优势是可以快照,这在传统磁盘是不可想象的,只在vmware这种虚拟化机制中容易实现。快照也是同样的采用写时拷贝。通过一个写时拷贝表记录新写入和修改的PE,而在修改时并不修改原有的PE,而是使用新的,如此就可以回溯到快照了。

另一个优势是可伸缩性。可以无需停机就调整分区的大小。这对于RAID系统来说由其有用。

也正是LVM的设计原因导致了其缺点也很明显。就是当一个分区的物理PE不连续时,就会造成极大的性能损耗。

除了LVM之外,还有其他类似的机制,如EVMS、dmraid。但是LVM被广泛采用。

工作方式

这个机制不可能只在用户空间完成,需要内核空间的代码协助,这部分代码是device-mapper(dm_mod模块)。dm_mod模块完成IO请求的转换工作,与内存管理一样本质上是映射。代码位于driver/md。这部分代码是策略与机制分离的生动体现,策略由用户端指定,机制由内核提供。

这个模块建模了3个实体:mappeddevice、映射表和target device。映射表用来记录两个设备的映射。一个是mapped device,是内核向外提供的设备,是逻辑存在的。一个是target device,所有对逻辑设备的操作最后都会转变为对target device的操作,是物理存在的。

我们知道通用块层的核心数据请求是BIO,而BIO不能跨越多个物理设备。因此在映射的情况下,一个BIO会被克隆分割为多个送到各个target device。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-01 20:45:13

Linux内核工程导论——用户空间设备管理的相关文章

Linux内核工程导论——用户空间进程使用内核资源

本文大部分转载和组装,只是觉得这些知识应该放到一起比较好. 进程系统资源的使用原理 大部分进程通过glibc申请使用内存,但是glibc也是一个应用程序库,它最终也是要调用操作系统的内存管理接口来使用内存.大部分情况下,glibc对用户和操作系统是透明的,所以直接观察操作系统记录的进程对内存的使用情况有很大的帮助.但是glibc自己的实现也是有问题的,所以太特殊情况下追究进程的内存使用也要考虑glibc的因素.其他操作系统资源使用情况则可以直接通过proc文件系统查看. 进程所需要的系统资源种类

Linux内核工程导论——基础架构

基础功能元素 workqueue linux下的工作队列时一种将工作推后执行的方式,其可以被睡眠.调度,与内核线程表现基本一致,但又比内核线程使用简单,一般用来处理任务内容比较动态的任务链.workqueue有个特点是自动的根据CPU不同生成不同数目的队列.每个workqueue都可以添加多个work(使用queue_work函数). 模块支持 模块概述 可访问地址空间,可使用资源, 模块参数 用户空间通过"echo-n ${value} > /sys/module/${modulenam

Linux内核工程导论——进程

进程 进程调度 概要 linux是个多进程的环境,不但用户空间可以有多个进程,而且内核内部也可以有内核进程.linux内核中线程与进程没有区别,因此叫线程和进程都是一样的.调度器调度的是CPU资源,按照特定的规则分配给特定的进程.然后占有CPU资源的资源去申请或使用硬件或资源.因此这里面涉及到的几个问题: 对于调度器来说: l  调度程序在运行时,如何确定哪一个程序将被调度来使用CPU资源? n  如何不让任何一个进程饥饿? n  如何更快的定位和响应交互式进程? l  单个CPU只有一个流水线

Linux内核中断引入用户空间(异步通知机制)【转】

转自:http://blog.csdn.net/kingdragonfly120/article/details/10858647 版权声明:本文为博主原创文章,未经博主允许不得转载. 当Linux内核空间发生中断后怎么使用户空间的应用程序运行相应的函数呢,当芯片有数据到来时内核会产生一个中断,但是怎样通知应用程序来取数据,以前这个问题一直困扰我很长时间,后来发现linux中有异步通知机制,在用户程序中用signal注册一个响应SIGIO信号的回调函数,然后在驱动程序中向该进程发出SIGIO信号

Linux内核工程导论——网络:Netfilter概览

简介 最早的内核包过滤机制是ipfwadm,后来是ipchains,再后来就是iptables/netfilter了.再往后,也就是现在是nftables.不过nftables与iptables还处于争雄阶段,谁能胜出目前还没有定论.但是他们都属于netfilter项目的子成员. 钩子 netfilter基于钩子,在内核网络协议栈的几个固定的位置由netfilter的钩子.我们知道数据包有两种流向,一种是给本机的:驱动接收-->路由表-->本机协议栈-->驱动发送.一种是要转发给别人的:

Linux内核工程导论——网络:Filter(LSF、BPF)

数据包过滤 LSF(Linux socket filter)起源于BPF(Berkeley Packet Filter),基础从架构一致,但使用更简单.其核心原理是对用户提供了一种SOCKET选项:SO_ATTACH_FILTER.允许用户在某个sokcet上添加一个自定义的filter,只有满足该filter指定条件的数据包才会上发到用户空间.因为sokket有很多种,你可以在各个维度的socket添加这种filter,如果添加在raw socket,就可以实现基于全部IP数据包的过滤(tcp

Linux内核工程导论——内存管理(一)

Linux内存管理 概要 物理地址管理 很多小型操作系统,例如eCos,vxworks等嵌入式系统,程序中所采用的地址就是实际的物理地址.这里所说的物理地址是CPU所能见到的地址,至于这个地址如何映射到CPU的物理空间的,映射到哪里的,这取决于CPU的种类(例如mips或arm),一般是由硬件完成的.对于软件来说,启动时CPU就能看到一片物理地址.但是一般比嵌入式大一点的系统,刚启动时看到的已经映射到CPU空间的地址并不是全部的可用地址,需要用软件去想办法映射可用的物理存储资源到CPU地址空间.

Linux内核工程导论——UIO

要开启hugepages文件系统,这个文件系统要使用mmap来映射页,可以显著的减少缺页中断. UIO介绍 UIO是一个在用户端实现内核驱动的机制.其在内核中有一个模块支持uio模块.现在这个模块只支持字符设备.用户可以添加多个uio设备(用户端的设备驱动),每个设备在/dev/uioX,X为数字,第一个为0,依次类推.我们知道设备都是靠中断来响应的,响应uio设备中断的方法是读取/dev/uioX文件,没有中断的时候读取会阻塞,来中断的时候会读取到整数值,代表已经发生的中断的次数. 但是这只是

Linux内核工程导论——进程:ELF文件执行原理(2)

ELF 强符号与弱符号(本小节是转别人的) 我们经常在编程中碰到一种情况叫符号重复定义.多个目标文件中含有相同名字全局符号的定义,那么这些目标文件链接的时候将会出现符号重复定义的错误.比如我们在目标文件A和目标文件B都定义了一个全局整形变量global,并将它们都初始化,那么链接器将A和B进行链接时会报错: 1 b.o:(.data+0x0): multiple definition of `global'2 a.o:(.data+0x0): first defined here 这种符号的定义