Linux学习54 Linux内核功能及模块应用

一、回顾

  1、CentOS启动流程

    POST --> Bootloader(BIOS,MBR) --> Kernel(initrd) --> rootfs(readonly)--> switch_root --> /sbin/init

      root (hd0,0)

      kernel 

      initrd

  2、ldd命令:打印一个二进制应用所依赖的库

    ldd [OPTION]... FILE...

    a、我们查看我们ls依赖哪些库文件

[root@node1 ~]# ldd /bin/ls
    linux-vdso.so.1 =>  (0x00007ffc857d4000)
    libselinux.so.1 => /lib64/libselinux.so.1 (0x00007fbd9c63d000)
    libcap.so.2 => /lib64/libcap.so.2 (0x00007fbd9c438000)
    libacl.so.1 => /lib64/libacl.so.1 (0x00007fbd9c22e000)
    libc.so.6 => /lib64/libc.so.6 (0x00007fbd9be6b000)
    libpcre.so.1 => /lib64/libpcre.so.1 (0x00007fbd9bc09000)
    libdl.so.2 => /lib64/libdl.so.2 (0x00007fbd9ba04000)
    /lib64/ld-linux-x86-64.so.2 (0x000055758c083000)
    libattr.so.1 => /lib64/libattr.so.1 (0x00007fbd9b7ff000)
    libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fbd9b5e3000)

      左侧是库文件名称,右侧是库文件路径,而对于vdso这个库文件和ld-linux-x86-64这两个库文件而言你可以认为这是整个系统调用库的入口,尤其是ld-linux-x86-64对于64位系统来讲这是各应用程序调用其他库文件的入口。

    b、我们来取出这个应用程序依赖的各库文件的文件路径

      

 二、Linux内核

  1、内核设计体系:单内核,微内核

  2、Linux:单内核设计,但充分借鉴了微内核体系的设计的优点;为内核引入了模块化机制;

  3、内核的组成部分

    a、kernel:内核核心,一般为bzimage,通常位于/boot目录下,但是你在/boot目录下看到这个文件时他已经没用了,因为在系统启动时他已经被加载过了。当系统启动起来的时候他不会再去读取这个内核文件。之所以我们要把他放在/boot目录下是为了便于后期管理。其名称为vmlinuz-VERSION-release;

    b、kernel object:内核对象,即内核模块,一般放置于/lib/modules/,无论你是32位系统还是64位系统他们都放在lib目录下而不是lib64,在/lib/modules目录下有一个以VERSION-release(内核 版本号和release号)共同命名的目录,他一定是和内核版本号完全匹配,即内核模块和内核核心版本必须严格匹配。我们内核编译时相应的参数项如下:

      (1)、[ ]:N

      (2)、[M]:Module,把它编译成模块,用到时我们就可以临时装载,不用就可以不装载,最多占用磁盘空间而不占用内核内存空间。

      (3)、[*]:Y,编译进内核核心,也就是说只要内核在,这个功能就在,但是我们一般必须是最核心的必须所有人都用到的功能才这样编译进系统内核

      但是编译成模块有一个缺陷,因为他支持动态装载和卸载,因此我们一般只能在用到此模块时还要临时把他装载进来才能用,这中间就有一个时间差,如果你能在内核直接提供,上来就可以直接用而不用装载,但是这个时间差几乎可以忽略不计。

      内核模块支持动态装载和卸载

    c、ramdisk:辅助性文件,并非是必须的,这取决于内核是否能直接驱动rootfs(根文件系统)所在的设备,如果可以驱动就不需要,如果不能驱动我们就需要借助于他。

      (1)、目标设备驱动,比如SCSI设备的驱动;

      (2)、逻辑设备驱动,例如LVM设备的驱动

      (3)、文件系统,例如xfs文件系统

      (4)、ramdisk是一个简装版的根文件系统;

  4、内核信息的获取

    a、uname命令

      uname - print system information

    b、相应选项

      (1)、-r:显示内核的release号

      (2)、-n:显示当前主机名

[root@node1 ~]# uname -n
node1

  5、模块信息获取

    a、lsmod命令,显示当前内核加载的各模块的模块名,模块大小以及被引用的次数和被某个模块所引用

[root@node1 ~]# lsmod 
Module                  Size  Used by
ipt_MASQUERADE         12678  2 
nf_nat_masquerade_ipv4    13412  1 ipt_MASQUERADE
nf_conntrack_netlink    40449  0 
nfnetlink              14696  2 nf_conntrack_netlink
...

      其实其显示的相应的信息都是抽取的/proc/modules这个文件中的信息

    b、modinfo,用于显示一个指定的linux模块的详细信息

      Show information about a Linux Kernel module

      modinfo  [-F field] [-k kernel] [modulename|filename...]

      (1)、比如我们要查看ext4这个模块的详细信息

[root@node1 ~]# modinfo ext4
filename:       /lib/modules/3.10.0-693.el7.x86_64/kernel/fs/ext4/ext4.ko.xz #模块文件路径
license:        GPL #许可证
description:    Fourth Extended Filesystem #描述
author:         Remy Card, Stephen Tweedie, Andrew Morton, Andreas Dilger, Theodore Ts'o and others#作者
alias:          fs-ext4 #别名,下面的也是
alias:          ext3
alias:          fs-ext3
alias:          ext2
alias:          fs-ext2
rhelversion:    7.4 #适用于哪一个版本
srcversion:     ACCC278D4090A389397D579
depends:        mbcache,jbd2 #依赖的模块
intree:         Y
vermagic:       3.10.0-693.el7.x86_64 SMP mod_unload modversions 
signer:         CentOS Linux kernel signing key #谁做了签名的模块
sig_key:        DA:18:7D:CA:7D:BE:53:AB:05:BD:13:BD:0C:4E:21:F4:22:B6:A4:9C #用的什么签名
sig_hashalgo:   sha256 #签名算法

      (2)、modinfo获取相应的信息其实是通过/lib/modules/3.10.0-693.el7.x86_64/这个目录中相应的元数据文件来获取的,因此就算此时这个模块没有被装载至内核也可以通过该命令来获取信息

      (3)、-k:modinfo只是展示的是当前版本的内核中对应模块的信息,如果想要查看另一个版本的内核中对应的模块信息应该怎么办呢?我们可以使用-k命令

      (4)、-F 我们可以指定只显示指定字段信息

[root@node1 ~]# modinfo -F filename btrfs
/lib/modules/3.10.0-693.el7.x86_64/kernel/fs/btrfs/btrfs.ko.xz

  6、模块的装载和卸载

    a、modprobe命令,用于模块的动态装载和卸载

      Add and remove modules from the Linux Kernel

      (1)可以通过-C config-file指明相应的配置文件进行装载,如果不指明则默认是相应的/etc/modprobe.d目录下所有以点conf结尾的文件和/etc/modprobe.conf文件

      (2)、modprobe [-r] module_name ,不带-r是装载模块,带-r是卸载模块

      (3)、现在我们来手动装载和卸载btrfs模块

[root@node1 /]# lsmod |grep btrfs
[root@node1 /]# modprobe btrfs
[root@node1 /]# lsmod |grep btrfs
btrfs                1073861  0 
raid6_pq              102527  1 btrfs
xor                    21411  1 btrfs
[root@node1 /]# modprobe -r btrfs
[root@node1 /]# lsmod |grep btrfs

      (4)、对正在使用中的内核模块千万不要随意去卸载他,比如你远程在一个服务器上你干掉你的网卡驱动,此时你的网络功能就没了

    b、depmod:内核模块依赖关系文件及系统信息映射文件的生成工具

      Generate modules.dep and map files.为模块生成依赖关系

    c、模块的装载和卸载的另一组命令

      (1)、insmod命令,安装模块

        insmod [filename] [module options...]

        filename:模块文件的文件路径

        1)、现在我们来通过此命令安装我们的btrfs模块,首先我们需要先按照相应的依赖模块然后再安装我们的btrfs模块

[root@node1 /]# modinfo btrfs
filename:       /lib/modules/3.10.0-693.el7.x86_64/kernel/fs/btrfs/btrfs.ko.xz
license:        GPL
alias:          devname:btrfs-control
alias:          char-major-10-234
alias:          fs-btrfs
rhelversion:    7.4
srcversion:     A6F6827ED983ACC62A4A97E
depends:        raid6_pq,xor
intree:         Y
vermagic:       3.10.0-693.el7.x86_64 SMP mod_unload modversions 
signer:         CentOS Linux kernel signing key
sig_key:        DA:18:7D:CA:7D:BE:53:AB:05:BD:13:BD:0C:4E:21:F4:22:B6:A4:9C
sig_hashalgo:   sha256
[root@node1 /]# insmod `modinfo -n raid6_pq`
[root@node1 /]# insmod `modinfo -n xor`
[root@node1 /]# insmod `modinfo -n btrfs`

      (2)、rmmod命令,移除模块

[root@node1 /]# lsmod |grep btrfs
btrfs                1073861  0 
xor                    21411  1 btrfs
raid6_pq              102527  1 btrfs
[root@node1 /]# rmmod btrfs
[root@node1 /]# lsmod |grep btrfs

  7、ramdisk文件的管理

    a、mkinitrd命令(CentOS 5用,不过CentOS6和7也有相应的链接)

      为当前正在使用中的内核重新制作ramdisk文件

      mkinitrd - is a compat wrapper, which calls dracut to generate an initramfs

      mkinitrd [OPTION...] [<initrd-image>] <kernel-version>

      (1)、现在我们将我们/boot目录下与当前内核版本相匹配的ramdisk文件initramfs-3.10.0-229.el7.x86_64.img这个文件给删了,现在我们来进行创建

[root@node1 /]# mkinitrd /boot/initramfs-$(uname -r).img $(uname -r)

      注意,这个创建过程需要去搜寻当前这个根文件系统所在的设备以及这个设备所在的驱动模块和这个设备上面的文件系统的驱动模块等给你加载到之后才会完完整整的给你创建出这个文件来。这个格式对567都是适用的

      (2)、--with=<module>,创建时除了他默认装载的模块之外如果我们还想手动给其提供一些模块的话我们就需要使用此选项 

      (3)、--preload=<module>:initramfs所提供的模块需要预先装载的模块

    b、dracut命令(CentOS6和7用)

      dracut - low-level tool for generating an initramfs image,较为底层的用来生成initramfs文件的命令

      dracut [OPTION...] [<image> [<kernel version>]]

      

 三、我们操作系统上的两个用于内核信息输出的伪文件系统

  1、/proc(proc伪文件系统):内核状态及统计信息的输出接口;同时,其还提供了一个配置接口,/proc/sys

    a、相应参数

      (1)、只读:只用于实现信息输出;例如/proc/#

      (2)、可写:可接受用户指定一个"新值"来实现对内核某功能或特性的配置:/proc/sys/,这里的写权限仅有管理员才能拥有

    b、相应的内核参数值修改方式

      (1)、sysctl命令

        sysctl [options] [variable[=value]] [...]

        sysctl -p [file or regexp] [...]

        专用于查看或设定/proc/sys目录下的参数的值(后面的值都是以/proc/sys为相对路径)

        1)、查看值

          #sysctl -a

          #sysctl variable

          #cat /proc/sys/PATH/TO/SOME_KERNEL_FILE

          显示当前所有的可配置的内核参数

[root@node1 ipv4]# sysctl -a

          我们看某一个参数值

[root@node1 ipv4]# sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1

        2)、修改值

          #sysctl -w variable=value(=两边不能有空格)

          我们现在修改其值为0

[root@node1 ipv4]# sysctl -w net.ipv4.ip_forward=0
net.ipv4.ip_forward = 0
[root@node1 ipv4]# sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 0

        3)、我们现在通过sysctl来设置我们的hostname并查看

[root@node1 ipv4]# ls /proc/sys
abi  crypto  debug  dev  fs  kernel  net  user  vm
[root@node1 ipv4]# sysctl -w kernel.hostname=www.wohaoshuai.com
kernel.hostname = www.wohaoshuai.com
[root@node1 ipv4]# uname -n
www.wohaoshuai.com
[root@node1 ipv4]# cat /proc/sys/kernel/hostname 
www.wohaoshuai.com

          我们可以看到,我们相应的hostname值就是文件统/proc/sys/kernel/hostname中的值,我们也可以通过cat命令查看,所以我们称之为伪文件系统,对于伪文件系统来说我们是有映射关系的,参数和文件的映射关系都相对于/proc/sys目录而言,每一个参数对应的我们所看到的文件系统应该是我们某个目录或某个子系统下的某一个参数文件,但是我们却可以使用点来隔开。比如我们的kernel.hostname参数就相当于我们的/proc/sys/kernel/hostname,我们也可以使用cat查看

      (2)、文件系统命令(cat,echo)

        1)、查看

          #cat /proc/sys/PATH/TO/SOME_KERNEL_FILE

        2)、覆盖

[root@www ~]# uname -n
www.wohaoshuai.com
[root@www ~]# echo "node1" > /proc/sys/kernel/hostname 
[root@www ~]# uname -n
node1

      (3)、sysctl -p

        上述两种方式的设定仅当前运行的内核有效,一旦操作系统重启了当前所有参数都会丢失的,我们想永久有效的话我们需要修改配置文件/etc/sysctl.conf或/etc/sysctl.d/所有以conf结尾的文件(CentOS 7才多了/etc/sysctl.d/这个路径的方式),此配置文件修改后不会立即生效,而是下次重启系统以后才会有效。我们想立即有效的话可以重读此文件立即生效,通过sysctl -p命令即可

[root@www /]# sysctl kernel.hostname
kernel.hostname = node1
[root@www /]# echo "kernel.hostname = www.wohaoshuai.com" >> /etc/sysctl.conf 
[root@www /]# cat /etc/sysctl.conf |grep -Ev "^#"
kernel.hostname = www.wohaoshuai.com
[root@www /]# sysctl kernel.hostname
kernel.hostname = node1
[root@www /]# sysctl -p 
kernel.hostname = www.wohaoshuai.com
[root@www /]# sysctl kernel.hostname
kernel.hostname = www.wohaoshuai.com

      (4)、内核参数net.ipv4.ip_forward:参数的意思是打开或关闭linux的核心转发功能,他的作用在于打开服务器的核心转发功能。什么意思呢?假设我们现在有一台linux主机,他有两块网卡分别连到两个不同的网络中去的,假如A网中有一主机,B网中有一主机,当我们主机A去ping主机B的时候能否ping通呢?首先第一点我们要打开我们内核的核心转发,如果不打开的话我们不允许从一块网卡收到的报文给其转发至另一块网卡所在的网络中去的。因此我们核心转发的意思是他能把来自于一个网络中的报文从一个网络中收到的报文转发至另一个网络中去,另一个网络仍然是本机可到达的报文。如果我们不打开核心转发,我们A网中的主机就没法达到B网中的主机

        

       (5)、相应的内核参数

        1)、net.ipv4.ip_forward:核心转发

        2)、vm.drop_caches:释放缓存

          0

          1

          2

        3)、kernel.hostname:主机名

        4)、net.ipv4.icmp_echo_ignore_all:忽略所有ping操作

  2、/sys目录(sys伪文件系统)(kernel2.6引用的)

     a、sysfs:输出内核识别出的各硬件设备的相关属性信息,也有内核对硬件特性的可设定参数;对此些参数的修改,即可定制硬件设备工作特性。

[root@www /]# ll /sys
total 0
drwxr-xr-x   2 root root 0 Mar 20 18:29 block #块设备
drwxr-xr-x  28 root root 0 Mar 20 18:29 bus #总线
drwxr-xr-x  55 root root 0 Mar 20 18:29 class#类别
drwxr-xr-x   4 root root 0 Mar 20 18:29 dev
drwxr-xr-x  13 root root 0 Mar 20 18:29 devices
drwxr-xr-x   5 root root 0 Mar 20 18:29 firmware
drwxr-xr-x   5 root root 0 Mar 20 18:29 fs
drwxr-xr-x   2 root root 0 Mar 20 18:29 hypervisor
drwxr-xr-x   9 root root 0 Mar 20 18:29 kernel
drwxr-xr-x 166 root root 0 Mar 20 18:29 module
drwxr-xr-x   2 root root 0 Mar 23 15:13 power

    b、我们装完系统以后我们的设备都在dev目录下,那么我们linux内核怎么知道你的主机对于用户来讲装完操作系统以后会有哪些硬件设备呢?我们应该给他准备什么设备文件让其能够访问呢?比如我怎么事先知道这个主机是几块硬盘呢?如果不知道的话只能在/dev下sda,sdb....都给其加进来,linux内核在2.4以后在/dev目录下有两万多个文件,无论你用或者不用他都静静的躺在那里,因为我们事先没法预测用户会用到什么设备,所以他会把能用到的全创建出来,所以这样就会有两个问题,第一个就是我们用到的可能不到一百个,剩下的几千上万个全都是浪费的,他可能不占用空间但是他至少是一个文件数量,其次我们没办法根据文件判定哪些设备是有的哪些设备是没有的,所以很不好用。因此到了kernel2.6以后,正是/sys这个目录的方式的出现使得我们能够按需创建设备文件,意思是说每一次当内核系统完成自身初始化的时候他会探测识别出每个设备的信息然后他会把这些设备信息在系统启动完成以后在内核初始化完成以后,在根文件系统挂载以后再重新探测输出一遍输出到/sys目录下,所以当前主机上有多少个硬件都得在/sys目录下显示出来,但是有些设备可能没有设备文件怎么办呢?临时读取/sys目录下的设备信息然后按需创建设备文件就行了。而谁来负责这个事情呢?就是udev

    c、udev:通过读取/sys目录下的硬件设备信息按需为各硬件设备创建设备文件。我们内核的初始化是在什么时候完成的呢?POST完以后Bootloader,Bootloader后就是内核初始化,初始化完成以后加载根文件系统所在的设备的驱动而后去装载根文件系统并且切换到init,所以当你的根文件系统挂载完以后内核初始化就已经结束了,这些信息就不会再输出了,因此/sys的存在会强制内核再重新输出一遍内核信息,输出内核读取到的各硬件设备信息到/sys目录下,而这个时候udev就可以根据/sys目录下所读取到的各信息为各设备创建所需要的设备文件了,你没有设备信息怎么创建设备文件?而内核在启动完以后他已经输出过了,如果我们没有其他渠道来获取这些硬件信息你就没办法知道有哪些硬件设备,也就不可能按需创建设备文件了,所以/sys目录的存在恰好解决了这样的问题。我们的udev是用户空间程序,所以他不能直接和硬件打交道,正是/sys存在才能使得它能够读取设备信息。

      (1)、我们还有专门创建设备的工具,比如devadmin,hotplug等等

      (2)、我们在dev目录下的很多文件都是udev创建的,但有些是内核自己创建的。比如说内核自己要首先识别出sda硬盘,假如你的操作系统在sda上,因此他不得不能自动识别sda硬盘并且把sda标记为一个设备的话他怎么能挂载这个设备呢?但好在这个设备文件为了以后能够访问正常内核就能够自动通过内置的devtmpfs(设备临时文件系统),为内核每一个必须用到的设备创建一个设备文件,而且这个设备文件能够在根文件系统挂载以后从内核直接移到/dev目录下,但这里面补全,剩下的就需要靠udev了。尤其是后来你插了一个u盘,相当于是新添加的,这种也要靠udev,但是udev怎么创建设备文件呢?udev为设备创建设备文件时,会读取其事先定义好的规则文件,一般在/etc/udev/rules.d/目录下,以及/usr/lib/udev/rules.d/目录下,因此可以通过修改这两个目录下的文件来改变他所识别到的硬件设备。

原文地址:https://www.cnblogs.com/Presley-lpc/p/12550317.html