linux内核_Linux内核构建

可能我们很少会自己去编辑和定制linux内核,但是了解内核的构建过程是非常有必要的。学习一样东西要知其然还要知其所以然,这样的话我们对于它的了解才会更全面。

开始先说两点注意的地方:

  1. 不要使用root权限构建内核,因为root权限在内核源代码解压的时候会出现很多的文件权限设置问题导致内核构建失败,同时编译内核时会在/dev目录下生成一些垃圾文件(早期问题,现在已经解决)。
  2. 再就是内核源码不能放在/usr/src/linux目录下面,因为这里存放的是构建系统库所需要的内核,而不是你自己定制的新内核。

linux内核的源代码在http://kernel.org网站上

285fe7e655844d380bbb2d6091f894da.png

目前的稳定版是5.5.7,主线的开发版是5.6-rc3,下面是一些长期支持版本。

我们下载5.5.7内核的源代码,大约106MB。

然后我们在自己的主目录下新建一个文件夹来存放下载下来的源码

mkdir  ./linux
mv  linux-5.5.7.tar.xz  ./linux/
tar  -xJvf  linux-5.5.7.tar.xz

解压内核源码后将会出现内核源码的文件夹

或者我们使用git

git clone git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git
cd linux-stable
git tag -l | less
git checkout -b stable  v5.5.7

gihub中kernel的源码项目:https://github.com/torvalds/linux

内核源代码目录结构如下:

470f018a2614eb069aab228c77de2528.png

安装依赖的工具

  • bc是计算器
  • ctags 最先是用来生成C代码的tags文件,后来扩展成可以生成各类语言的tags, 有些语言也有专有的tags生成工具(比如java的jtags, python的 ptags).”它将会递归的遍历当前文件夹,对所有它认识的文件进行索引,并将结果写入到当前目录下的tags文件。一般它很快就执行完了,速度取决于你项目中源文件的多少。
  • ncurses库是一个Linux系统下的图形支持的函数库,字符终端处理库,包括面板和菜单。用于(make menuconfig显示图形界面)
sudo apt-get install libncurses5-dev gcc make git universal-ctags bc libssl-dev  flex  bison

内核的构建系统kbuild基于GUN Make,这是一套非常复杂的系统。对于编译来说,一条make命令即可,但是这不是重点,重点是内核编译前的配置。我们要配置编译系统,指导它编译哪个些内容,开启哪些特性。

我们平时用的linux发行版为了能够在更多的机器上运行,几乎选择开启了全部的配置选项,编译了全部的驱动,这样很容易增加了内核体积,而且还拖慢了内核的运行速度。所以,要想要一个占用空间小,运行更快的内核,就需要我们自己动手配置内核。

但是内核的选项成千上万,往往让人无法下手。万事开头难,只要我们坚持迈出这一步,至少能明白整个事情的大体过程。

内核的构建过程

内核自己的构建系统kbuild是利用make组织的一套复杂的构建系统,下面是理解这套系统的关键。

1、Makefile的文件包含

这是在项目中经常使用的方法,将共同使用的变量或规则定义在一个文件中,在使用者的Makefile中使用关键字"include"来包含这个文件。

linux支持多个平台,所以kbuild必须方便添加新平台,上层的Makefile不能老是做大的改动,最好不要改动。所以,kbuild将与平台无关的变量、规则等放到了linux源码最顶层的Makefile中,不同平台特有的部分定义在了各个平台目录下的顶层Makefile中。然后让源码顶层的Makefile包含各平台目录顶层的Makefile就可以了。

kbuild又将所有参与编译过程相关的公共的规则和变量都提取到了scripts目录下的Makefile.build中

8bd43bc68ee4b13e09e6a36abff1e246.png

kbuild定义了若干变量,如obj-y、obj-m等,用于记录参与编译过程的文件(y是指,这些变量就像是钩子或者回调函数,各个子目录中的Makefile只需为其赋值,设置参与编译的文件即可,其它的事情都由Makefile.build处理。在编译时,Makefile.build会指导make把要编译的子目录下的Makefile文件包含到Makefile.include动态地组成完整的Makefile文件。

变量src始终指向需要构建的目录,obj指向构建的目标存放的目录,并约定在引用源码树中已经存在的对象时使用src,引用编译时动态生成的对象时使用变量obj。kbuild在脚本中小心地维护着这两个变量的值。实际中,因为构建的目标与源文件经常在同一个目录,所以大部分情况下这两个变量均指向同一目录。

正常的make命令

make  -f  script/Makefile.build  obj=子目录  target

通过命令行-f选项,指定Makefile为script目录下的Makefile.build。当make解释执行Makefile.build时,再将子目录中的Makefile包含到Make.include中来,动态地组成子目录的真正的Makefile。

总之,内核源码中每一级目录下都会有Kconfig,其中的menu对应make menucofig中的每一个选项, source表示该目录框架下所包含各个目录或文件的 Kconfig,由此构成了menuconfig。通过手动设置make menuconfig会生成.config文件。

Makefile会读取.config文件,而.config文件内部是由宏定义组成,例如CONFIG_XXX=y, y表示XXX模块编译进内核(配置选项的可选参数有三种,即是否编译进内核”*”,编译成模块”M”不编译”[]”,default y表示默认是编译进内核的)。Makefile里面的条件编译是就是由.config中的宏决定的,而.config中的宏是否被注释又是有make menuconfig中选择决定的,而make menuconfig中的是否存在这个配置选项则是由Kconfig决定的,由此串联起来了四者的关系。工作流程:在子源码的目录里添加Kbuild和makefile文件,Kbuild用于编译写menu项,Kbuild系统会自动找到你的Kbuild,根据你编写的内容生成menuconfig的菜单,最后把你的配置根据用户选择写到.config文件里。Makefile系统会根据.config文件的描述调用你自己源码目录里的makefile生成对应的目标文件。

配置过程

内核提供的配置手段Kconfig

构建内核的第一步始终是Kconfig,有以下几种方式:

config 使用基于行的程序更新当前配置
nconfig 使用ncurses基于菜单的程序更新当前配置
menuconfig 使用基于菜单的程序更新当前配置
xconfig 使用基于Qt的前端更新当前配置
gconfig 使用基于GTK +的前端更新当前配置
oldconfig 使用提供的.config作为基础更新当前配置
localmodconfig 更新当前配置禁用未加载的模块
localyesconfig 更新当前配置,将本地mod转换到内核
defconfig 默认来自Arch提供的defconfig的新配置
savedefconfig 将当前配置保存为./defconfig(最小配置)
allnoconfig 新配置,其中所有选项均以“否”回答
allyesconfig 新配置,其中所有选项都被'yes'接受
allmodconfig 尽可能新配置选择模块
alldefconfig 新配置,所有符号都设置为默认值
randconfig 新配置,随机回答所有选项
listnewconfig 列出新选项
olddefconfig 与oldconfig相同,但在没有提示的情况下将新符号设置为其默认值
kvmconfig 为KVM虚拟机内核支持启用其他选项
xenconfig 为xen dom0和虚拟机内核支持启用其他选项
tinyconfig 配置最小的内核

通常我们一般会先用make allnoconfig生成一个除了必选项其它都不选的.config文件,然后我们再用make menuconfig来打开我们需要的选项。

587936d8f1de728d2f7c051716a15332.png

屏幕顶端显示程序浏览指令和各种字符含义,屏幕的其它部分显示不同的内核配置选项。

内核的配置部分分为几个行,分别代表内核的各个部分,每个部分在它的子菜单里也会包含多个子项。例如,所有的内核驱动程序方面的设置都在主菜单选项Device Drivers中。

我们通过设置对应条目前的符号来告诉编译系统,是把这个条指定的模块构建到内核还是做为模块还是完全不构建这部分。[]里面可以是空或者*,空代表不选,*号代表选构建入内核。<>里面可以是*代表构入内核,M代表构建成模块。留空代表不构建。

使用Y键可以把对应项设置成*,M键设置成M,N键会设置成空。也可以用空格键循环设置。

几个小技巧

1、使用多处理器加速构建

内核构建系统能够很好地把任务划分成小片,并将其分配给不同的处理器,从而加快处理速度,缩减构建时间。

要用多线程方式构建内核,使用make命令的-j选项。应当在-j选项后面附加一个两倍于系统中处理器数量的数字。对于双核计算机(不含超线程)使用 make -j4(如果-j选项后面没加数字,构建系统会为源码树的每一个子目录创建一个新线程,这样大量的线程充斥系统反而使构建速度变慢,所以建议-j后面一定要加数字)

2、部分构建内核

开发内核时,可能你只想构建源码树中特定的子文件夹或单独的文件。要想部分构建,可以在构建内核的命令行中指明所需要构建的文件夹。例如。想要构建drivers/usb/serial文件夹下中的文件,可以使用如下命令:

make drivers/usb/serial

此命令将构建文件夹下的全部所需文件并链接成最终的模块映像。这个映象不会自动更新到内核的成品镜像中,除非你再次在源码树顶层执行make命令,检查和重新构建内核。

如果要构建内核树中的一个特定文件,仅把它传递给make即可,比如:

make drivers/usb/serial/visor.ko

构建系统将会构建所有visor.ko模块所需要的文件,并最终链接成模块visor.ko

最终编译好的内核在arch/<cpu>/boot/目录下,可以使用make install命令安装

如果配置了模块,使用make modules命令编译模块

然后使用make modules_install把散落在各个文件夹下的.o文件安装到系统目录/lib/modules/<内核版本>下

内核升级

通常,我们不想每次内核升级都下载全部的源码,因为内核发布或修复时往往会提供一个补丁,能将老版本的内核升级为新版本。现在我们一般会用git拉取最新版本并自动合并。

首先,我们要保存好我们以前的.config,把它考到新的源码中,然后使用如下命令更新.config中的配置项

make  oldconfig

此命令会显示出所有的配置问题,发果配置文件中已经存在对选项的选择,配置程序将自动执行选择,如果出现新的选项,程序会停下来询问用户怎么设置新选项。用户回答后程序将继续执行直到处理完完整的内核配置。

make slientoldconfig和oldconfig的工作过程完全一样,只是除了询问要新增的配置选项以外,它不在屏幕上打印任何东西。

完成配置后我们重新编译安装内核即可。

内核定制

构建自己的内核最困难的就是确定自己计算机所需要的驱动和配置。

有时我们可以从发行版内核包中的配置开始。由于正确的驱动已经与硬件绑定,在运行中的系统上确定系统所需要的驱动能简单一些。还有就是几乎所有的发行版都在其内核包中提供内核配置文件。我们可以通过各发行版的文档了解如何找到这个配置。它们一般是在/usr/src/linux目录下。如果找不到内核配置文件,请查看当前运行内核是否包含配置信息,大部分发行版的内核构建为包含自身的配置,并将其放置在/proc文件系统下。我们可以使用ls /proc/config.gz命令查看。然后把它考出来解压出config文件。

以上方法往往很难行得通,即便是存在这样的文件,里面的配置也几乎是包含了所有的内核模块和驱动。意义不是很大。

往往没有办法,我们只能从头开始。

一、如果我们现在有一个能正常运行的系统,我们可以从系统中找到设备对应的驱动

系统中的几个位置存放了设备和驱动程序如何对应的信息。其中最重要的是一个叫做sysfs的虚拟文件系统。

举例:确定PCI网卡驱动

首先,逆向的从网络连接名找出控制它的PCI设备

ls  /sys/class/net/

或者使用ip link命令,找到你的网络接口名称,比如说eth0

然后使用如下命令找出eth0的驱动模块

basename  $(readlink  /sys/class/net/eth0/device/driver/module)

我们使用readlink命令找到module真实指向的模块完整路径名,然后用basename命令截取最后的模块名。

现在我们已经得到了模块名称,只要在内核的源码树中找到对应的配置项将它设置成编译进内核或模块就可以了。我们可以手工查找或者使用下面的搜索命令查询:

find  -type  f  -name  Makefile  | xargs  grep  <模块名>

6c205a76395dfd8edf552ff695b6f642.png

输出的内容当中含有CONFIG_的行就是它的配置选项名。比如本例是CONFIG_E1000,则在menuconfig中我们就可以通过搜索E1000这个字符串来找到对应的匹配项。

我们执行make menuconfig打开配置界面,然后按/键启动搜索,并键入E1000。如图所示

850e445cbba2a3b823e88a76b9734a74.png

搜索的结果如下:

23533d621c51b85470e95c16d698a762.png

其中第一个结果就是我们要找的。

总结一下:

  1. 在sysfs的class文件夹中找到设备所对应的文件,文件名就是设备名,网络设备在/sys/class/net,其它设备根据种类不同罗列在/sys/class/下的其它目录中。
  2. 根据设备的名称,/sys/class/类名/设备名/device/driver/module文件便指向的是控制这个设备的驱动模块名。这个文件一般是一个链接文件,我们使用readlink命令找到这个链接指向的真实文件路径。然后使用basename命令处理路径取得最后的文件名。这个文件就是真正的驱动模块名称。
  3. 使用find和grep从内核的Makefile文件中查找出用于构建这个模块的配置项,它是以CONFIG_为前缀。
  4. 在内核配置系统中搜索这个值,找到配置的位置。
    编写工具:

我们可以写个脚本遍历sysfs,查找所有模块的名称。

这里我们要先说一下sysfs系统与linux的设备模型。

linux2.5开始,内核引入了sysfs文件系统,sysfs被看成是与proc、devfs和devpty同类别的文件系统,它可以产生一个包括所有系统硬件的层级视图,与提供进程和状态信息的proc文件系统十分类似。

sysfs把连接在系统上的设备和总线组织成一个分级的文件,它们可以由用户空间存取,向用户空间导出内核数据结构以及它们的属性。sysfs的一个目的就是展示设备驱动模型中各组件的层次关系,其顶级目录包括block,device,bus,drivers,class,power,fimware。

block目录包含所有的块设备,device目录包含系统所有的设备,并根据设备挂接的总线类型组织成层次结构;bus目录包含系统中所有的总线类型;drivers目录包括内核中所有已注册的设备驱动程序;class目录包含系统中的设备类型。

在linux内核中,分别使用bus_type、device_driver和device来描述总线、驱动和设备。设备和驱动都必须依附于一种总线。Linux中设备和驱动是分开注册的,注册一个设备的时候,并不需要驱动已经存在,而一个驱动被注册时也不需要对应的设备已经被注册。设备和驱动各自涌向内核,通过总线bus_type中的match()成员函数来进行配对。

在sysfs中,名为modalias的文件中包含着模块的别名。这个别名是由设备制造商ID、设备ID、类的类型和其它标识组成的唯一标识符。而系统中安装的每个驱动模块也会向内核登记自己支持的设备列表。modprobe这个命令可以利用设备别名的标识符去查所有驱动模块的设备支持列表,看看有没有驱动支持这个设备。我们就利用这个特性把系统中所有的设备与目前系统里现存的模块匹配一下,看看是否能找到对应的模块。脚本内容如下:

#!/bin/bash
for i in `find /sys/ -name modalias -exec cat {} ;`; do
 /sbin/modprobe --config /dev/null --show-depends $i ;
done | rev | cut -f 1 -d '/' | rev | sort -u

此脚本会输出系统中目前已经安装并且在使用的驱动模块名,没有驱动的设备会显示成错误信息。如下图所示:

b8834fbb7f707c9c8d66314d185939c5.png

二、从零开始确定正确的模块

确定一个新设备由哪个驱动控制的最简单的方法,就是把内核源码树中所有的此类驱动都构建成模块,并让udev完成设备和驱动的匹配。也就是通过构建所有的模块到系统里,然后通过上面的方法去确定加载了哪些。然后再回头去内核配置里只打开有用的驱动。

但如果我们不想这么麻烦,或者上面的方法也行不通,那我们就要想另的办法。

为设备匹配驱动的方法取决于你的设备类型,我们接下来以最常见的PCI设备和USB设备为例做说明。

PCI设备

PCI设备依靠供应商ID(vendor ID)和设备ID(device ID)来识别,每个供应商ID和设备ID的组合就可能唯一确定一个设备驱动。

还是以网卡为例,我们先用lspci命令列出所有的pci设备,然后从里面找到网卡的。命令如下:

lspci | grep -i ethernet

结果如下

29965a6108dfaa92a8a93d63910d0dcc.png

lspci显示的首部就是该设备的PCI总线ID,即00:03.0。我们要用这个值在sysfs中查找设备信息。

我们先看一下各种PCI设备在sysfs中的表示:

dea925ea10b2af42e262b0c5011f5b31.png

可以看到内核给PCI设备的编号以0000:开头,它在lspci命令中是不显示的。因此我们要找的就是0000:00:03.0

我进入这个目录,查看供应商ID和设备ID,如下图所示:

0c760b33107374c885d052bc2dc1b8f0.png

其中0x8086是供应商ID,0x100e是设备ID。内核就是使用这些值来正确的匹配驱动和设备。PCI上的每个驱动会把自己支持的所有设备的供应商ID和设备ID告诉内核,所以内核就知道了把哪个驱动绑定到哪个设备上。

上面这些步骤的目的就是找到设备的供应商ID和设备ID。我们接下来就能过这两个值在内核的源码树中查找驱动。

在内核源码中,include/linux/pci_ids.h文件里罗列了所有PCI设备的供应商ID和设备ID。我们在这里搜索。

70bfd9985508779c235b6461f8c64a7f.png

可以看到,我们找到了供应商ID的宏定义PCI_VENDOR_ID_INTEL,虽然没有在这个文件里找到设备ID的宏义,但是不要紧,我们接着去寻找引用这个供应商宏定义的驱动源码文件。

64f730d6e0c7e01e89ee196e99cdb95b.png

我们主要查看drivers/net/ethernet/intel/目录下的.c源文件。我们打开e1000/e1000_main.c

7c6852b094816a1f203b3245ad95b5ae.png

看到这个结构体:pci_device_id。所有的PCI驱动都包含一个他所支持的设备的列表,这个列表就是pci_device_id结构体。我们在这个列表中找到了我们的制造商ID和设备ID。并且文件的开头告诉我们驱动的名称叫e1000。

往往我们不能一下就找到了对的C文件,我们可以使用grep命令查找pci_device_id,然后再去查看有定义这个结构体文件。总归我们的目的是找到驱动的名字。

现在我们有了驱动的名字,就可以按照上节所说的步骤,去找内核源码的Makefile配置项就可以了。

总结一下步骤:

  1. 使用lspci查找设备的PCI总线ID
  2. 进入/sys/bus/pci/devices/0000:bus_id/目录,查看vendor和device文件,找到PCI制造商ID和设备ID。
  3. 到内核源码树include/linux/pci_ids.h文件中通过上步中找到的制造商ID和设备ID查找制造商宏字符串。
  4. 在内核源码中查找哪些文件对这个制造商宏字符串有引用,去这些源码文件中找pci_device_id结构体。从这里面找是否匹配我们的设备ID。如果匹配则看一下对应的驱动模块名称是什么。
  5. 使用find和grep命令在内核的Makefile文件中查找用于构建此模块的以CONFIG_为前缀的字段
   find   -type   f  -name  Makefile  | xargs  grep   DRIVER_NAME

6. 在内核配置系统make menuconfig中搜索这个配置值,进入到对应的配置位置,启用该模块

整个过程:PCI_ID--VendorID和DeviceID--VendorName--驱动源文件---确认各项ID找到驱动名--通过驱动名找到Makefile名---在Makefile文件里找到CONFIG_名--去内核配置文件里打开对应配置

USB设备

USB设备模块的查找和PCI设备的思路是一样的,但是USB设备要简单一些,因为我们直接可以使用lsusb命令显示USB设备的制造商ID和设备ID。我们还是以无线网卡为例:

854e4ec9c692b573f7d2306974f01b72.png

其中的ID 148f:3070就是设备的制造商ID和设备ID。这样我们有这现个ID就可以搜索制造商的宏定义字符串了。

但是,不像PCI设备,USB设备没有一个像pci_ids.h这样的文件来记录所有的USB设置ID。我们只能搜索整个源码中的driver文件夹

b3a20f7f8eeea17776d162dab95cacd1.png

我们知道这是一个无线网卡设备,所以我们只需要查看wireless相关的,我们挨个打开这些文件中查看里面定义的usb_device_id结构体。当我们打开:drivers/net/wireless/ralink/rt2x00/rt2800usb.c文件时看到:

588ac88568d4c7f91763908cf2e2cc02.png

支持我们的设备。并且我们从文件中找到驱动名称为rt2800usb

接下来就是搜索内核Makefile然后去开启配置选项了。

总结一下USB设备驱动模块查找步骤:

  1. 使用lsusb命令找到设备的制造商ID和设备ID
  2. 在内核源码树中搜索drivers目录内有引用制造商ID的文件
  3. 查看文件中的usb_device_id结构体,找到支持我们设备ID的驱动源码文件。得到驱动模块名称
  4. 使用find和grep命令在内核的Makefile文件中查找用于构建此模块的以CONFIG_为前缀的字段
   find   -type   f  -name  Makefile  | xargs  grep   DRIVER_NAME

5. 在内核配置系统make menuconfig中搜索这个配置值,进入到对应的配置位置,启用该模块

也就是制造商ID和设备ID---找到引用文件--确认设备ID---找到驱动名--改配置

和PCI设备不同的是,我们可以直接用命令查看制造商ID和设备ID,同时内核源码中对制造商的引用也没有使用宏定义字符串,直接查找ID就可以。

根文件系统

根文件系统是系统引导的重要组成部分。通常我们把根分区的文件系统支持以及其所在的磁盘控制器支持都构建入内核,而不是构建成模块,当然,使用ramfs的话是构建成模块。但是无论怎么样,我们要找到它的驱动模块。

这个我们只能在已有的系统中查看,比如我的主机,根文件系统是ext4格式,在sda设备上。如下图:

87b56697feac5c4be9b4c6e0962c87d2.png

sda块设备和PCI网络设备一样。在设备目录中有一个叫device的符号链接,它指向控制这个块设备的逻辑设备:

ae534280fcb8f0a7d358687de5ebb960.png

我们需要一步步的沿着这个sysfs中的设备链去找到控制这个设备的驱动:

2b2e1ed34bf5d9815e0bcf792b9a05a4.png

这里面没有driver。我们去sysfs链的上一级目录里找,如果没有再往上一级。

04494c33c83a19192fbe6d23417d88e1.png

这里我们看到driver指向了scsi磁盘控制器。因此我们需要把SCSI磁盘支持加入我们的内核配置中。

我们接着往上一级目录继续查找控制硬件的驱动:

aa37cbc4c4b18ed112f1fbac250b7002.png

就在这里,driver指向的就是我们需要在内核中配置的磁盘控制器。

所以对于根文件系统,我们需要在内核配置中启用ext4、sd和ahci驱动。

最后为了自动化完成上面的工作,我们给出一个脚本:

#!/bin/sh
#
# Find all modules and drivers for a given class device.
#
 
if [ $# != "1" ] ; then
 echo
 echo "Script to display the drivers and modules for a specified sysfs class device"
 echo "usage: $0 <CLASS_NAME>"
 echo
 echo "example usage:"
 echo "      $0 sda"
 echo "Will show all drivers and modules for the sda block device."
 echo
 exit 1
fi
 
DEV=$1
 
if test -e "$1"; then
 DEVPATH=$1
else
 # find sysfs device directory for device
 DEVPATH=$(find /sys/class -name "$1" | head -1)
 test -z "$DEVPATH" && DEVPATH=$(find /sys/block -name "$1" | head -1)
 test -z "$DEVPATH" && DEVPATH=$(find /sys/bus -name "$1" | head -1)
 if ! test -e "$DEVPATH"; then
  echo "no device found"
  exit 1
 fi
fi
 
echo "looking at sysfs device: $DEVPATH"
 
if test -L "$DEVPATH"; then
 # resolve class device link to device directory
 DEVPATH=$(readlink -f $DEVPATH)
 echo "resolve link to: $DEVPATH"
fi
 
if test -d "$DEVPATH"; then
 # resolve old-style "device" link to the parent device
 PARENT="$DEVPATH";
 while test "$PARENT" != "/"; do
  if test -L "$PARENT/device"; then
   DEVPATH=$(readlink -f $PARENT/device)
   echo "follow 'device' link to parent: $DEVPATH"
   break
  fi
  PARENT=$(dirname $PARENT)
 done
fi
 
while test "$DEVPATH" != "/"; do
 DRIVERPATH=
 DRIVER=
 MODULEPATH=
 MODULE=
 if test -e $DEVPATH/driver; then
  DRIVERPATH=$(readlink -f $DEVPATH/driver)
  DRIVER=$(basename $DRIVERPATH)
  echo -n "found driver: $DRIVER"
  if test -e $DRIVERPATH/module; then
   MODULEPATH=$(readlink -f $DRIVERPATH/module)
   MODULE=$(basename $MODULEPATH)
   echo -n " from module: $MODULE"
  fi
  echo
 fi
 
 DEVPATH=$(dirname $DEVPATH)
done

使用举例:

aa.sh sda 获得所有sda块设备的驱动

内核配置项说明

下面列出内核中大部分需要做出更改的部分

以后再写。复工了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值