Signal9Signal9 — home
online
all lessons

LPIC-2 Linux Kernel: Components, Compiling, Modules, and sysctl Tuning

Advanced35 min7 steps
What you'll learn
  • Build and install a kernel in the correct order, and explain why modules_install can't be skipped
  • Choose between modprobe and insmod, and inspect modules with modinfo and lsmod
  • Set module options and blacklist a driver in /etc/modprobe.d/
  • Tune kernel parameters at runtime and persist them so they survive a reboot
  1. What the kernel is, and why you'd ever rebuild it

    Frame the kernel as the layer everything else trusts, and name the two ways you change it.

    The kernel is the program that owns the hardware. Every other process talks to disks, network cards, and memory by asking the kernel to do it on their behalf. Think of the kernel as the building's electrical and plumbing system: applications are tenants who flip switches and turn taps, but the wiring and pipes behind the walls are shared infrastructure that one team controls.

    You change kernel behaviour two ways, and LPIC-2 tests both:

    • At build time — compile a new kernel (or new drivers) from source. Heavy, occasional, done when you need hardware support or a feature the distro kernel lacks.
    • At run time — load modules (drivers you snap in and out of a running kernel) and set sysctl parameters (tunable knobs like network and memory behaviour) without rebooting.

    This lesson walks the build flow first, then the everyday module and tuning workflow.

  2. The build flow, in order

    Run the four build steps in sequence and understand what each one produces.

    Compiling a kernel is four commands in a fixed order. Each feeds the next, like a production line: configure what to build, build it, install the parts (modules), then install the kernel itself so the bootloader can find it.

    Example — not runnable on your lab box

    make oldconfig
    scripts/kconfig/conf  --oldconfig Kconfig
    #
    # configuration written to .config
    #
    oldconfig
    Start from the existing .config and only ask about options that are new in this source tree.
    This produces the `.config` file — the answer sheet of which features and drivers to compile. oldconfig reuses your current kernel's choices and prompts only for genuinely new options, so you're not re-answering thousands of questions. Nothing is compiled yet; you've only decided what to compile.

    Example — not runnable on your lab box

    make -j$(nproc)
      ...
      LD      vmlinux
      SORTTAB vmlinux
      OBJCOPY arch/x86/boot/bzImage
    Kernel: arch/x86/boot/bzImage is ready
    -j N
    Run N compile jobs in parallel. More jobs = faster build on multi-core machines.
    $(nproc)
    Substitutes the CPU core count, so -j matches the cores you actually have.
    This is the long step: it compiles the kernel image (bzImage) and every driver you marked as a module (.ko files). -j$(nproc) uses all your cores — on an 8-core box that's roughly 8x faster than a single-threaded build. When it finishes, the compiled artifacts exist in the source tree but aren't installed anywhere the system looks.

    Example — not runnable on your lab box

    sudo make modules_install
      INSTALL /lib/modules/6.8.0-custom/kernel/fs/xfs/xfs.ko
      INSTALL /lib/modules/6.8.0-custom/kernel/net/...
      DEPMOD  6.8.0-custom
    modules_install
    Copy every compiled .ko module into /lib/modules/<version>/ and run depmod.
    This copies the compiled modules into `/lib/modules/<kernel-version>/` — the only place a running kernel looks for its drivers — and runs depmod to build the dependency map. Notice the `DEPMOD` line at the end: that's the system recording which module needs which, the data modprobe will rely on later.

    Example — not runnable on your lab box

    sudo make install
      INSTALL /boot/vmlinuz-6.8.0-custom
      INSTALL /boot/initramfs-6.8.0-custom.img
    Generating grub configuration file ...
    done
    install
    Copy the kernel image to /boot, build the initramfs, and update the bootloader.
    The final step copies the kernel image into `/boot`, generates an initramfs, and updates the bootloader (GRUB) so the new kernel appears in the boot menu. Now the system can actually boot the kernel you built.

    Check yourself

    You compiled and installed a new kernel but forgot make modules_install. What goes wrong at the next boot?

  3. Loading modules: modprobe vs insmod

    Pick the right loader by understanding which one resolves dependencies.

    A module is a driver you can plug into the running kernel — no reboot, no recompile. But modules often depend on other modules (a filesystem driver may need a compression helper). Two tools load them, and the difference is entirely about dependencies.

    Think of it like installing software: modprobe is your package manager — ask for one thing and it pulls in everything that thing needs. insmod is copying a single file into place — it installs exactly what you named and nothing else.

    Example — not runnable on your lab box

    sudo modprobe xfs
    modprobe
    Load a module by name, resolving and loading its dependencies first.
    -r
    Remove (unload) a module and any now-unused dependencies it pulled in.
    Silent success — no output means it worked. modprobe looked up xfs in `modules.dep` (the map built by depmod), loaded any modules xfs depends on first, then loaded xfs. You name one module by its name (no path, no .ko) and it handles the chain.

    Example — not runnable on your lab box

    sudo insmod /lib/modules/$(uname -r)/kernel/fs/xfs/xfs.ko
    insmod: ERROR: could not insert module .../xfs.ko: Unknown symbol in module
    insmod
    Insert exactly one .ko file by full path — no dependency resolution at all.
    insmod takes a full path to a single `.ko` file and loads only that file. Because it does no dependency resolution, if xfs needs another module that isn't already loaded, you get an Unknown symbol error — the missing symbol lives in the dependency insmod didn't load. modprobe would have loaded that dependency first and this would have just worked.

    Example — not runnable on your lab box

    sudo modprobe -r xfs
    Unloading mirrors loading. modprobe -r removes xfs and any dependencies it pulled in that nothing else is still using. The lower-level equivalent is rmmod xfs, which removes a single module (and complains if something still depends on it).

    Check yourself

    Which command loads a module AND automatically pulls in the other modules it depends on?

  4. Inspecting modules: modinfo and lsmod

    Find out what a module does and whether it's currently loaded — before you touch it.

    Before loading or blacklisting a driver, you want two facts: what is this module and what options does it accept (modinfo), and is it loaded right now and what's using it (lsmod). Look before you leap.

    Example — not runnable on your lab box

    modinfo xfs
    filename:       /lib/modules/6.8.0/kernel/fs/xfs/xfs.ko
    license:        GPL
    description:    SGI XFS with ACLs, security attributes...
    depends:        libcrc32c
    parm:           bug_on_assert:Bug on assert (bool)
    modinfo
    Print a module's metadata: file path, license, description, dependencies, and parameters.
    modinfo reads the module's built-in metadata without loading it. The fields that matter: `depends:` lists modules this one needs (here libcrc32c) — exactly what modprobe resolves and insmod ignores. `parm:` lists the options you can pass the module — the names you'd set in /etc/modprobe.d/ in the next step.
    lsmod | head
    Module                  Size  Used by
    xfs                  2179072  1
    libcrc32c              16384  1 xfs
    nf_conntrack          172032  2 nf_nat,nf_tables
    lsmod
    List modules currently loaded into the running kernel (a formatted view of /proc/modules).
    Three columns. Module is the name, Size is its memory footprint, and `Used by` is the count and names of things depending on it. Here libcrc32c shows 1 xfs — one user, namely xfs. That Used by count is why rmmod xfs must come before you can remove libcrc32c: you can't pull out a module something still depends on.

    Check yourself

    Which command shows you a module's accepted parameters and dependencies WITHOUT loading it?

  5. Module options and blacklists in /etc/modprobe.d/

    Pass options to a module and stop an unwanted driver from loading — persistently.

    Loading a module by hand is temporary. To make a module always load with certain options, or to stop a driver from ever loading, you write config files in `/etc/modprobe.d/` — any *.conf file there is read by modprobe. Two directives do most of the work:

    • options <module> <key>=<value> — set parameters whenever the module loads.
    • blacklist <module> — prevent the module from being loaded automatically.

    Example — not runnable on your lab box

    cat /etc/modprobe.d/blacklist-custom.conf
    # Stop the legacy framebuffer driver from grabbing the GPU
    blacklist nouveau
    
    # Always disable IPv6 in the module
    options ipv6 disable=1
    Two directives in one file. `blacklist nouveau` tells modprobe not to auto-load the nouveau driver — the usual move before installing a proprietary GPU driver that wants the card to itself. `options ipv6 disable=1` sets the disable parameter (one of the parm: lines modinfo would show) every time ipv6 loads. The filename is arbitrary as long as it ends in .conf.

    Check yourself

    Which directive in an /etc/modprobe.d/*.conf file passes parameters to a module each time it loads?

  6. sysctl: runtime tuning vs persistent tuning

    Set a kernel parameter now, then make it survive a reboot the right way.

    sysctl reads and writes kernel tunables — knobs for networking, memory, and more, exposed under /proc/sys/. The single most-tested fact here is the split between runtime and persistent changes.

    Think of sysctl -w as writing on a whiteboard: instant, but wiped clean at the next reboot. Persisting a value is writing it into the building's manual so it's applied every time the building opens.

    sudo sysctl -w net.ipv4.ip_forward=1
    net.ipv4.ip_forward = 1
    -w
    Write (set) a kernel parameter immediately, in the running kernel only.
    This turns on IP forwarding (routing packets between interfaces) right now. It's identical to running echo 1 > /proc/sys/net/ipv4/ip_forward — the key net.ipv4.ip_forward maps directly to the file path /proc/sys/net/ipv4/ip_forward (dots become slashes). But this change lives only in memory.

    Example — not runnable on your lab box

    cat /etc/sysctl.d/90-router.conf
    # Persist IP forwarding across reboots
    net.ipv4.ip_forward = 1
    To make the change permanent, write the key = value line into a *.conf file under `/etc/sysctl.d/`. This file is read at every boot, so the tunable is reapplied automatically. The filename's number prefix (90-) just controls ordering when multiple files set the same key — higher numbers win.
    sudo sysctl --system
    * Applying /etc/sysctl.d/90-router.conf ...
    net.ipv4.ip_forward = 1
    * Applying /etc/sysctl.conf ...
    --system
    Reload and apply all sysctl config files from the standard directories, in order.
    After editing a config file, sysctl --system applies all the files immediately so you don't have to reboot to test them. It walks /etc/sysctl.d/, /run/sysctl.d/, /usr/lib/sysctl.d/, and /etc/sysctl.conf, echoing each key it sets — confirming your file was picked up. Now the value is both live and persistent.
    sysctl net.ipv4.ip_forward
    net.ipv4.ip_forward = 1
    Read a single tunable back by naming it with no -w. Reading is harmless and needs no root. This is how you verify a value actually took — the same as cat /proc/sys/net/ipv4/ip_forward.

    Check yourself

    You set a tunable with sysctl -w and it works, but it's gone after a reboot. What did you skip?

  7. Try it in your live terminal

    Do it, don't just read it — these run read-only on the real sandbox to your right.

    You have a real root shell. Run these in order. They are all read-only — they inspect the running kernel without changing it, so they're safe to run anywhere. The point is to make the inspect-before-you-touch moves reflex.

    lsmod | head
    See which modules are loaded right now, their size, and what depends on them (the Used by column).
    modinfo xfs 2>/dev/null || modinfo ext4
    Inspect a filesystem module's metadata — note its depends: and parm: lines. Falls back to ext4 if xfs isn't present.
    sysctl net.ipv4.ip_forward
    Read a single tunable. Reading needs no root and changes nothing — this is how you verify a value took.
    ls /etc/sysctl.d/
    List the persistent tunable files. Anything here is reapplied at every boot — the durable half of the runtime-vs-persistent split.
Recap
  • Build order is fixed: make oldconfig -> make -j$(nproc) -> make modules_install -> make install. Skip modules_install and the new kernel boots without its drivers.
  • modprobe resolves dependencies via depmod/modules.dep; insmod loads one .ko blindly (an Unknown symbol error means a missing dependency modprobe would have loaded).
  • Inspect with modinfo (metadata, params, deps — without loading) and lsmod (what's loaded and used by what). Persist module options and blacklists in /etc/modprobe.d/*.conf.
  • sysctl -w (and writing /proc/sys/...) is runtime-only. Persist tunables in /etc/sysctl.d/*.conf and apply with sysctl --system.
Command -> what it does
CommandEffectPersists across reboot?
make modules_installCopies .ko modules to /lib/modules/<ver>/, runs depmodYes (part of install)
modprobe <name>Loads a module + its dependenciesNo (runtime)
insmod <path.ko>Loads one .ko file, no dependency resolutionNo (runtime)
modinfo <name>Shows a module's metadata without loading itRead-only
lsmodLists currently loaded modules and their usersRead-only
/etc/modprobe.d/*.confoptions/blacklist directives for modulesYes
sysctl -w key=valSets a tunable now, in memory onlyNo
/etc/sysctl.d/*.conf + sysctl --systemSets a tunable and reapplies at bootYes