Linux内核初探
内核的组成部分
-
kernel:内核核心文件,一般为bzp_w_picpath,经过压缩处理的镜像文件;通常内核核心文件保存在/boot/目录下,名称为vmlinuz-version-release
-
kernel object(ko):内核对象,内核额外功能模块,一般该类文件放置于/lib/modules/[version-release]
内核管理的相关命令
uname
-
功能:打印当前系统相关信息(内核版本号、硬件架构、主机名称和操作系统类型等)。
-
常用选项
-n
:显示节点名称-r
:显示内核版本号,包括version和release-a
:显示所有信息
lsmod
-
功能:列出内核已载入模块的状态
-
用法:lsmod
-
描述:lsmod 以美观的方式列出/proc/modules的内容,显示当前加载的所有模块。
输出为:
Module(模块名) Size(模块大小) Used by(被…使用)
eg. ne2k_pci 8928 0
8390 9472 1 ne2k_pci在/proc/modules中相应的是:
(模块名,模块大小,被…使用,模块地址)
ne2k_pci 8928 0 – Live 0×3086400
8390 9472 1 ne2k_pci , Live 0xe086000
注意:在ubuntu12 之前,还可以通过 modprobe -l 来查看已经加载的模块列表,但现在已经没有这个选项。
假设你没有设定开机加载某个模块,比如ntfs,那么开机后执行lsmod,列表里不会有ntfs这个模块的,这时你再执行 mount -t ntfs xxx后,执行lsmod后列表里就会有ntfs这个模块了。
还要注意的是lsmod显示的是模块名,而不是别名(alias)。
depmod
-
功能:分析可加载模块的依赖性,生成modules.dep文件和映射文件,,在构建嵌入式系统时,需要由这个命令来生成相应的文件,由modprobe使用。
-
用法:
depmod [-b basedir] [-e] [-F System.map] [-n] [-v] [version] [-A]
depmod [-e] [-F System.map] [-n] [-v] [version] [filename…]
-
描述:
Linux内核模块可以为其它模块提供提供服务(在代码中使用EXPORT_SYMBOL),这种服务被称作”symbols”。若第二个模块使用了这个symbol,则该模块很明显依赖于第一个模块。这些依赖关系是非常繁杂的。
depmod读取在/lib/modules/version 目录下的所有模块,并检查每个模块导出的symbol和需要的symbol,然后创建一个依赖关系列表。默认地,该列表写入到/lib/moudules /version目录下的modules.dep文件中。若命令中的filename有指定的话,则仅检查这些指定的模块(不是很有用)。
若命令中提供了version参数,则会使用version所指定的目录生成依赖,而不是当前内核的版本(uname -r 返回的)。
-
选项:
-b
basedir –basedir basedir 若你的模块并没有正确的在/lib/mdules/version下,可以指定目录生成依赖。-e
–errsyms 和-F选项一起使用,当一个模块需要的symbol在其它模块里面没有提供时,做出报告。正常情况下,模块没有提供的symbol都在内核中有提供。-F
–filesyms System.map 提供一个System.map文件(在内核编译时生成的)许-e选项报告出unresolved symbol。-n
–dry_run 将结果modules.dep和各种映射文件输出到标准输出(stdout),而不是写到模块目录下。-A
–quick 检查是否有模块比modues.dep中的模块新,若没有,则退出不重新生成文件。
modules.dep与depmod
modprobe加载某个模块是根据/lib/modules/`uname -r`目录下的modules.dep文件中的模块列表,这个文件中有的模块modprobe会正确加载,否则就会出错。
我们拿ntfs这个模块来举例:
vi /lib/modules/`uname -r`/modules.dep
注释掉 /lib/modules/2.6.18-4-k7/kernel/fs/ntfs/ntfs.ko
这一行,就是加个#号,这个修改是即时生效的。
modinfo ntfs
modinfo: could not find module ntfs
modprobe ntfs
FATAL: Module ntfs not found.
重启机器,执行同样的命令会得到同样的结果,说明开机不会自动执行 depmod 的,而
locate ntfs.ko
# 输出:
# /lib/modules/2.6.18-4-k7/kernel/fs/ntfs/ntfs.ko
证明我们并没有删除ntfs模块。
注意如果重启机器之前进行mount还是可以的,重启之后就会报错了,而上边的都是即时生效的。还有如果modules.dep里注释掉了ntfs,那么在/etc/modules里写上也是不起作用的,说明这个和mount一样都是依赖 modprobe来完成加载模块命令的。而insmod是可以的,因为insmod后面跟的是绝对路径,它和modules.dep没什么关系。 insmod比较重要的用途是用来测试模块的正确性,加载一般都是依靠modprobe。(这个可能也不起作用了,都用modprobe吧)
这一切只是因为我们注释掉了modules.dep中关于ntfs.ko的那一行,而模块并没有删除或转移。
modprobe
-
功能:Linux内核添加删除模块
-
用法:
modprobe [ -v ] [ -V ] [-C config-file] [ -n ] [ -i ] [ -q ] [ -o modulename] [ modulename ] [ module parameters … ]
modprobe [ -r ] [ -v ] [ -n ] [ -i ] [ modulename … ]
modprobe [ -l ] [ -t dirname ] [ wildcard ]
modprobe [ -c ]
-
描述:
modprobe可智能地添加和删除Linux内核模块(为简便起见,模块名中’_'和’-'是一样的)。modprobe会查看模块 目录/lib/modules/’uname -r’里面的所有模块和文件,除了可选的/etc/modprobe.conf配置文件和/etc/modprobe.d目录外。
modprobe需要一个最新的modules.dep文件,可以用depmod来生成。该文件列出了每一个模块需要的其他模块,modprobe使用这个去自动添加或删除模块的依赖。
-
选项:
-v
–verbose 显示程序在干什么,通常在出问题的情况下,modprobe才显示信息。-C
–config 重载(意思取自C++的重载)默认配置文件(/etc/modprobe.conf或/etc/modprobe.d)。-c
–showconfig 输出配置文件并退出-n
–dry-run 只打印,不做实际动作,可以和-v选项一起使用,调试非常有用-i
–ignore-install –ignore-remove 该选项会使得modprobe忽略配置文件中的,在命令行上输入的install和remove命令。-q
–quiet 一般modprobe删除或插入一个模块时,若没有找到会提示错误。使用该选项,会忽略指定的模块,并不提示任何错误信息。-r
–remove 该选项会导致modprobe去删除,而不是插入一个模块。通常没有没有理由去删除内核模块,除非是一些有bug的模块。你的内核也不一定支持模块的卸载。-V
–verssion 版本信息-f
–force 和同时使用–force-vermagic ,–force-modversion一样。使用该选项是比较危险的。-l
–list 列出所有模块-a
–all 插入所有命令行中的模块-t
–type 强制 -l 显示dirname中的模块-s
–syslog 错误信息写入syslog
modinfo
-
功能:显示内核模块的信息
-
用法:
modinfo [ -0 ] [ -F field] [modulename | filename … ]
modinfo -V
modinfo -h
-
描述:
modinfo列出Linux内核中命令行指定的模块的信息。若模块名不是一个文件名,则会在/lib/modules/version 目录中搜索,就像modprobe一样。
modinfo默认情况下,为了便于阅读,以下面的格式列出模块的每个属性:fieldname : value。
-
选项:
-V
–version 版本-F
–field 仅在一行上显示field值,这对于脚本较为有用。常用的field有:author, description, licence, param, depends, alias, filename。-0
–NULL 使用’/0′字符分隔field值,而不是一个新行。对脚本比较有用。-a -d -l -p -n
这些分别是author, description, license, param ,filename的简短形式。
insmod 和 rmmode
insmod
-
功能:向Linux内核中插入一个模块
-
用法:insmod [filename] [modue options …]
-
描述:
insmod是一个向内核插入模块的小程序:若文件名是一个连字符’-’,模块从标准输入输入。大多数用户使用modprobe,因为它比较智能化。
rmmod
-
功能:删除内核中的一模块
-
用法:rmmod [ -f ] [ -w ] [ -s ] [ -v ] [ modulename ]
-
描述:rmmod是一个可以从内核中删除模块的小程序,大多数用户使用modprobe -r去删除模块。
-
选项:
-v
–verbose 显示程序正在做些什么,一般只显示执行时的错误信息。-f
–force 该选项是非常危险:除非编译内核时,CONFIG_MODULE_FORCE_UNLOAD被设置该命令才有效果,否则没效果。用该选项可以删除正在被使用的模块,设计为不能删除的模块,或者标记为unsafe的模块。-w
–wait 通常,rmmod拒绝删除正在被使用的模块。使用该选项后,指定的模块会被孤立起来,直到不被使用。-s
–syslog 将错误信息写入syslog,而不是标准错误(stderr)。-V
–version 版本信息
modprobe与insmod、rmmode
-
加载某个模块
modprobe xxx.ko
insmod xxx.ko
-
卸载某个模块
modprobe -r xxx.ko
rmmod xxx.ko
insmod 与 modprobe 都是载入 kernel module,不过一般差别于 modprobe 能够处理 module 载入的相依问题。
比方你要载入 a module,但是 a module 要求系统先载入 b module 时,直接用 insmod 挂入通常都会出现错误讯息,不过 modprobe 倒是能够知道先载入 b module 后才载入 a module,如此相依性就会满足。
不过 modprobe 并不是大神,不会厉害到知道 module 之间的相依性为何,该程式是读取 /lib/modules/`uname -r`/modules.dep 档案得知相依性的。而该档案是透过 depmod 程式所建立。
ramdisk管理
ramdisk文件是在操作系统安装完成之后,由特定的应用程序根据当前硬件设备信息,文件系统信息等量身定制而成;
ramdisk文件的制作工具
-
centos 5:
mkinitrd:建立要载入ramdisk的映像文件,以供Linux开机时载入ramdisk。
-
选项:
-f
:若指定的映像问家名称与现有文件重复,则覆盖现有的文件;-v
:执行时显示详细的信息;--omit-scsi-modules
:不要载入SCSI模块;--preload=<模块名称>
:指定要载入的模块;--with=<模块名称>
:指定要载入的模块;--version
:显示版本信息。 -
例子:
mkinitrd -v -f myinitrd.img $(uname -r)
-
-
centos 6/7
dracut:建立要载入ramdisk的映像文件,以供Linux开机时载入ramdisk。
例子:
dracut /boot/initramfs-$(uname -r).img $(uname -r)
Linux中重要的伪文件系统
/proc:procfs
Linux系统上的/proc
目录是一种文件系统,即proc文件系统。与其它常见的文件系统不同的是,/proc
是一种伪文件系统(也即虚拟文件系统),存储的是当前内核运行状态的一系列特殊文件,用户可以通过这些文件查看有关系统硬件及当前正在运行进程的信息,甚至可以通过更改其中某些文件来改变内核的运行状态。
另外,/proc
是存储在内存(RAM)中,而非硬盘中的,不占用外部存储空间。
基于/proc
文件系统如上所述的特殊性,其内的文件也常被称作虚拟文件,并具有一些独特的特点。例如,其中有些文件虽然使用查看命令查看时会返回大量信息,但文件本身的大小却会显示为0字节。此外,这些特殊文件中大多数文件的时间及日期属性通常为当前系统时间和日期,这跟它们随时会被刷新(存储于RAM中)有关。
为了查看及使用上的方便,这些文件通常会按照相关性进行分类存储于不同的目录甚至子目录中,如/proc/scsi
目录中存储的就是当前系统上所有SCSI设备的相关信息,/proc/N
中存储的则是系统当前正在运行的进程的相关信息,其中N
为正在运行的进程(可以想象得到,在某进程结束后其相关目录则会消失)。
大多数虚拟文件可以使用文件查看命令如cat
、more
或者less
进行查看,注意\proc
中文件通常比较长,直接使用cat
命令来查看全部信息可读性不佳。可以用more
和cat
命令,关于Linux中查看文件内容的命令,可参考博客:Linux查看文件内容命令:cat, tail, head, more, less。
\proc
中有些文件信息表述的内容可以一目了然,但大部分文件的信息却不怎么具有可读性。不过,这些可读性较差的文件在使用一些命令如apm
、free
、lspci
或top
查看时却可以有着不错的表现。
可详见博客:linux /proc 详解。
内核状态及统计信息的主要的输出接口;同时还提供了一个能够输入配置信息,完成内核参数实时配置的接口——/proc/sys;
/proc/*
(除了sys目录):信息输出,只读;/proc/sys
可读写,可以接受用户指定的“新值”,来实现对内核相应功能或特性的实时配置;
procfs的查看和修改:
-
查看内核输出的状态信息或统计信息,直接使用cat命令即可;
-
修改或设置内核功能或特性,使用echo命令,借助于覆盖输出重定向进行修改或设置即可;
echo "value" > /proc/sys/path/to/parameter
sysctl
查看内核参数以及配置/proc/sys中的诸多功能,还可以使用sysctl命令。
用于在内核运行时动态地修改内核的运行参数,可用的内核参数在目录/proc/sys中。
-
查看内核参数
sysctl -a
:查看所有可以被修改的内核参数;sysctl variable
:查看指定内核参数的设定值; -
配置某个内核参数(功能或特性)的值
sysctl -w variable=value
,注意:此方法中,"="两端不能写空格字符; -
根据配置文件设置内存参数;重新读取并加载配置文件中所有的设置参数并且使其生效;
sysctl -p
常用的几个内核参数
-
net.ipv4.ip_forward
:Linux的核心转发功能,路由功能;取值0,1 -
net.ipv4.icmp_echo_ignore_all
:忽略所有来源于外部主机的ping操作请求,取值0,1 -
vm.drcp_caches
:清理buffer和cache,释放物理内存,取值:0,1,2 -
kernel.hostname
:当前生效的主机名 -
DDOS:dynamic deny of service,动态拒绝服务攻击
-
DCHP:IP地址耗尽策略,发送大量随机Mac地址DHCP discover消息
安全相关:DNS:ARP攻击,ARP欺骗,网关欺骗,源IP地址欺骗,dead ping
/sys:sysfs
专门为用户提供使用的伪文件系统,输出内核识别出来的各硬件设备的相关属性信息,也包括内核对硬件特性的可设定的信息;对于某些参数进行特定格式的修改,以调整或设定硬件的工作特性;
比如:
echo '- - -' > /sys/class/scsi_host/host2/scan
/etc/udev
udev是运行在用户空间的进程;通过读取/sys目录下的硬件设备的信息,按需为各硬件设备创建设备文件; 专用工具:udevadmin,hotplug…
当内核已经被加载至内存中,假如操作系统被安装到sda磁盘上,则内核需先标识出sda磁盘并而将其标记为设备(创建设备文件),而后才能挂载此设备;
为了能够让这样的设备以后也能正常使用,内核通过内置的devtmpfs为每个内核所要使用的设备创建设备文件;而这样的文件可以被当作文件系统挂载之后,从内核直接转移到真正的rootfs中的dev目录内的;对于操作系统启动之后被新插入的设备,就必须要依靠udev来识别并创建设备文件了;
udev之所以能够为设备创建设备文件,主要依赖于其事先定义好的规则;而这样的规则一般保存在udev的规则配置文件中:
/etc/udev/runles.d
/usr/lib/udev/rules.d
Linux kernel 内核组成及编译方式
内核设计流派
单内核设计,但是充分借鉴了微内核体系设计的优点,为内核引入了模块化机制,内核高度模块化;
内核被模块化之后,一些最为基本最为重要的内容,被编译到内核核心;而其他更多的功能则以模块的方式来提供;而且支持动态装载和卸载各内核模块;
内核的组成部分
-
kernel:内核核心文件,一般为bzp_w_picpath,经过压缩处理的镜像文件;通常内核核心文件保存在/boot/目录下,名称为vmlinuz-version-release
-
kernel object(ko):内核对象,内核额外功能模块,一般该类文件放置于/lib/modules/[version-release]
注意:内核模块与内核核心,版本号必须严格匹配;
内核模块其实就是内核源代码的一部分,只是在编译内核的过程中,由于其功能可能并非内核核心所必须,所以以模块的方式被编译;
在编译内核时,内核的功能通常有如下几种选择方式:
-
[ ] kernel function:no,不选择编译此功能;
-
[M] kernel function:modules,将此功能编译为内核模块使用;此功能不占据内和空间,只占用磁盘空间;
-
[*] kernel function:yes,将此功能直接编译进内核核心;
ramdisk:内核补充文件,辅助文件,对于内核核心来说,此文件非必须,是否使用此文件取决于内核能否直接驱动rootfs所在的存储设备;
-
设备的驱动程序,SCSI设备的驱动
-
逻辑设备驱程序,lvm的驱动程序,软raid驱动程序等;
文件系统;
cpio -i -F initramfs-2.6.32-573.el6.x86_64.img
简化的rootfs
注意:一般来讲,kernel核心文件和ramdisk文件必须具有完全相同的版本号;
编译源代码的前提条件
开发环境
-
开发工具:gcc,make,automake,qt,GTK,ncurese
-
程序包组:
“Development Tool”,“Server Platform Development”
“开发工具”,“服务器平台开发”,ncurses-devel库
-
头文件:/usr/include/*.h
获取目标主机上各硬件设备的相关信息;
- CPU
cat /proc/cpuinfo
lscpu
x86info -a
-
PCI设备
lspci[-v|-vv]
-
USB设备
lsubs[-v|-vv]
-
块设备
lsblk
-
了解更多的硬件设备信息
hal-device
(centos6可用包名:hal-0.5.14-14.e16.x86_64)
获取目标主机系统功能的相关选项
比如:目标主机需要使用哪种文件系统;
目标主机是否需要启动安全防护机制;
…
编译安装应用程序的一般步骤
./.configure arg1 [arg2...]
make
make install
加载内核驱动的通常流程
- 先将.ko文件拷贝到/lib/module/`uname -r`(内核版本号)/kernel/driver/…目录下,根据具体用途的区别分为net、ide、scsi、usb、video、parport、md、block、ata等等。
- 运行depmod -a,更新模块依赖项,主要是更新modules.dep文件
- 运行modprobe加载内核模块
编译安装内核的一般步骤
步骤
-
需要准备或生成一个.config的文件,该文件记录了内核的编译细节:
-
哪些功能直接编译进内核;
-
哪些功能编译成内核模块;
-
哪些功能在此次编译中不启用;
make menuconfig | xconfig | config
-
-
开始编译内核
make module-install
多线程编译,可以将编译进程在 nnn 个CPU核心上并行进行;
-
安装模块文件
make module-install
-
安装内核核心文件,并生成grub的启动菜单
make install
安装的是bziage文件,安装到 /boot/vmlinuz-version-release
生成与内核版本完全匹配的initramfs文件
编辑grub的配置文件,生成启动菜单项;
Screen:一款由GNU计划开发的用于命令行终端切换的自由软件
简单用法:
-
screen(开启screen)
-
Ctrl +a;d(拆除screen)
-
screen -ls(列表显示screen)
-
screen -r screen_ID(恢复连接至指定的screen)
-
exit(关闭screen)
内核的配置选项
-
64-bit kernel: 是否支持64位内核
-
general setup —>通用配置项:
() local version - append to kernel release:自定义本地版本号,附加到内核版本号后面的信息,由编译者定义;
((none)) default hostname:定义当没有设置主机名时的默认主机名;
-
enable loadable module support: 是否支持内核模块的动态装卸载;
-
enable the block layer:是否支持启用块层,通常是选择支持;
-
processor type and features:处理器类型和特性
-
processor family (generic-x86-64) --> (core 2/newer Xeon):选择处理器类型
-
power management and acpi options:电源管理及高级电源管理接口选项
-
executable file formats / emulations:指定可执行文件的格式,默认为ELF,以#!开头的文件也具备可执行特性;
-
networking support:内核中的网络协议栈
-
networking options:[ ] ipv6 support
-
device drivers:设备驱动程序
-
file system:dos/fat/nt file systems,[M] NTFS support
-
kernel hacking:内核调试的相关内容
-
security options,NAS selinux support:安全选项
-
cryptographic API:加密解密的应用程序变口
-
virtualization:虚拟化相关
配置内核的方式
-
make config:基于单行命令以遍历内核所有功能的方式进行内核配置,因此每个内核选项的配置都是交互式的;
-
make menuconfig:基于curses的文本模式的配置窗口;
-
make gconfig:基于gtk开发环境的窗口配置界面;一般情况下,只要安装了“桌面平台开发”程序包组就可以了;
-
make xconfig:基于QT开发环境的窗口配置界面;一般情况下,只要安装了“桌面平台开发”程序包组就可以了;
-
make defconfig:基于内核为目标平台提供默认配置模板进行配置;
-
make allnoconfig:所有的功能全部不编译(全部选no)的配置方式;
-
make allyesconfig:所有的功能全部编译进核心(全部选yes)的配置方式;
内核的编译方式
-
全编译:
make [-jn]
-
部分编译:
a.只编译某个子目录中的相关源代码;
cd /user/src/linux make [-jn] dir_name/
b.只编译特定的模块
cd /usr/src/linux make [dir/]file.ko
示例:
cd /usr/src/linux make drivers/net/ethernet/intel/e1000/e1000.ko
-
交叉编译:编译的目标平台与当前编译的平台不相同;
make arch=arch_name
示例:
make arch=arm
内核重新编译
-
将所有 /usr/src/inux-version 目录的内容直接删除,重新从源代码包释放;随后可以重复之前的步骤重新编译即可;
-
先清理之前的编译接口
a. 清理大多数的编译生成的文件,但是会保留.config文件
make clean
b. 清理所有编译生成的文件,包括.config以及其他的备份文件
make mrproper
c. 相当于make mrproper,但是还会额外清理各种patches以及编译器自身的备份文件;
make distclean
编译安装内核实例
~]# tar xf linux-3.10.99.tar.xz -C /usr/src~]# cd /usr/srcsrc]# ln -sv linux-3.10.99 linuxsrc]# cd /usr/src/linuxlinux]# cp /boot/config-$(uname -r) ./.configlinux]# make menuconfiglinux]# make -j 4linux]# make modules_installlinux]# make installlinux]# reboot
重启之后在grub菜单中选择新编译的内核来启动,如果可以看到登录提示符,则说明内核编译升级成功!
然后可以尝试编译其他与当前操作系统版本不同的其他更高级版本的内核,多试几个版本,了解一下各个不同版本的内核的特性;
Ref
https://blog.51cto.com/u_12486569/1952181
https://www.cnblogs.com/jacklikedogs/p/4659249.html
https://forum.ubuntu.org.cn/viewtopic.php?t=469088