Linux内核开发之编译和运行

在本篇文章中,读者可以跟着我们的一步一步的教程最终学会如何搭建Linux内核开发环境,并最终能成功运行自己编译的内核,本文主要是针对arm架构来编译Linux内核,因此读者不仅可以学习到如果编译内核,还将学会如何使用QEMU搭建arm仿真环境。 本文将介绍两种Linux内核编译方法

  1. 第一种为不借助任何编译系统的原始编译方法,相对较为复杂
  2. 第二种则借助BuildRoot编译系统实现了一键编译,相对比较简单

编译Linux内核需要在Linux系统中运行,推荐使用Ubuntu,并安装 build-essentials 包,本

1. 原始编译

如果不使用编译系统进行一键式Linux编译的话,我们需要先获取交叉编译器,有几种不同的方式:

  1. 直接在系统中安装
  2. 使用crosstool-ng
  3. 使用第二章中使用 buildroot 编译出来的host工具链

本文第二章将介绍buildroot的使用方法,因此我们的环境中已经有buildroot帮我们生成的交叉编译器,因此我们会使用第三种方式,将buildroot编译出来的工具添加到PATH里边

1
2
export PATH=/home/exu/WorkArea/linux-study/buildroot-2022.02.8/output/host/bin:$PATH
export CROSS_COMPILE=arm-linux-

1.1 U-Boot配置和编译

下载U-Boot源码https://github.com/u-boot/u-boot 以后,我们需要拷贝默认配置

1
cp configs/vexpress_ca9x4_defconfig .config

如果我们需要修改默认配置可以运行

1
make menuconfig

保存修改以后的配置,然后运行make -j32启动编译,编译的结果会放置在u-boot文件夹的根目录,我们使用QEMU测试下刚刚编译好的U-Boot,运行

1
qemu-system-arm -M vexpress-a9 -m 128M -nographic -kernel u-boot

以后将成功启动u-boot,在QEMU可以按 ctrl-a, x退出

1.1.1 向SD卡中安装U-Boot

  • 创建空的SD卡文件dd if=/dev/zero of=sd.img bs=1M count=1024
  • 使用cfdisk在SD卡中创建分区 cfdisk sd.img, 并格式化
1
2
3
sudo losetup -f --show --partscan sd.img
sudo mkfs.vfat -F 16 -n boot /dev/loop<x>p1
sudo losetup -d /dev/loop<x>
  • 带着SD卡启动QEMU
1
2
3
qemu-system-arm -M vexpress-a9 -m 128M -nographic \
-kernel u-boot-2020.04/u-boot \
-sd sd.img

1.1.2 配置QEMU网络接口,并启动U-Boot

  • 创建qemu-myifup文件

    1
    2
    3
    
    #!/bin/sh
    /sbin/ip a add 192.168.0.1/24 dev $1
    /sbin/ip link set $1 up
    
  • 更改文件模式 chmod +x qemu-myifup

  • 启动QEMU

    1
    2
    3
    4
    
    sudo qemu-system-arm -M vexpress-a9 -m 128M -nographic \
    -kernel u-boot-2020.04/u-boot \
    -sd sd.img \
    -net tap,script=./qemu-myifup -net nic
    
  • 在U-Boot中配置IP

    1
    2
    3
    
    setenv ipaddr 192.168.0.100
    setenv serverip 192.168.0.1
    saveenv
    
  • 在U-Boot中ping主机

    1
    
    ping 192.168.0.1
    
  • 在主机中安装tftp服务器(/srv/tftp),则可以实现,在U-Boot中使用tftp从主机下载文件到QEMU

1.2 Linux编译

  • 设置交叉编译器 export CROSS_COMPILE=arm-linux-
  • 下载linux源码,并使用默认配置 make ARCH=arm vexpress_defconfig (可以运行make ARCH=arm help | grep defconfig 来搜索配置)
1
2
3
make menuconfig
# disable CONFIG_GCC_PLUGINS
# add CONFIG_DEVTMPFS_MOUNT
  • 运行 make -j32

编译结束以后,文件的位置:

  • 镜像:arch/arm/boot/zImage
  • Device Tree: arch/arm/boot/dts/vexpress-v2p-ca9.dtb

1.3 U-Boot加载Linux

  • 在U-Boot中设置Linux启动参数
1
2
setenv bootargs console=ttyAMA0
saveenv
  • 使用tftp加载经常和DTB文件
1
2
tftp 0x61000000 zImage
tftp 0x62000000 vexpress-v2p-ca9.dtb
  • 启动Kernel
1
bootz 0x61000000 - 0x62000000

在这里启动肯定会抛出panic,因为我们没有提供root filesystem

也可以通过设置bootcmd来自动化前边的操作

1
2
setenv bootcmd 'tftp 0x61000000 zImage; tftp 0x62000000 vexpress-v2p-ca9.dtb; bootz 0x61000000 - 0x62000000'
saveenv

2. 使用编译系统 Buildroot

2.1 使用Buildroot来编译Linux

Buildroot is a tool that simplifies and automates the process of building a complete Linux system for an embedded system, using cross-compilation. In order to achieve this, Buildroot is able to generate a cross-compilation toolchain, a root filesystem, a Linux kernel image and a bootloader for your target. Buildroot can be used for any combination of these options, independently (you can for example use an existing cross-compilation toolchain, and build only your root filesystem with Buildroot).

使用BuildRoot可以大大简化Linux内核的编译过程,实现一键编译,在开始编译以前,我们需要到 BuildRoot官方网页 下载BuildRoot编译系统,下载完毕后,我们将压缩包解压以后就可以开始配置我们要编译的Linux内核了

  • 运行 make menuconfig 可以从头开始配置Linux
  • 也可以使用默认的一些配置,通过运行 make list-defconfigs 来列出所有支持的默认配置,然后通过 make <defconfig-name> 来使用某个默认配置(在这里我们选择arm)配置会生成到 .config 文件夹中

配置完成以后通过运行make命令来启动编译

  • 运行 make -j32 来启动多线程编译

当成功编译以后,编译结果将被存放在 output 目录中其中

host 目录包含在 host 机器(也就是我们用户编译Linux内核的机器)上用到的工具,例如

  • 交叉编译器名称,例如:arm-buildroot-linux-uclibcgnueabi
  • 交叉编译器可执行文件,在目录 output/host/bin
  • Sysroot:output/host/arm-buildroot-linux-uclibcgnueabi/sysroot

images目录包含目标板上用到的文件,例如

  • Root file system:rootfs.ext2
  • Device tree: versatile-pb.dtb
  • Kernel: zImage

2.2 在QEMU上运行Linux

在这里我们使用 Direct Linux Boot 的方法在QEMU中启动Linux内核,大家可以在QEMU官网介绍 中找到Direct Linux Boot的相关介绍,简单来说就是QEMU支持的不使用启动镜像的方式来加载内核镜像。

因为我们在编译内核前选择了arm架构,因此我们编译出来的Linux内核需要在arm环境中才能运行,QEMU提供了针对arm的仿真,我们在安装好QEMU的系统中使用qemu-system-arm命令来仿真arm架构

通过运行下述命令来启动我们刚刚编译好的内核

1
2
3
4
5
6
7
qemu-system-arm -M versatilepb -m 256 \   # versatilepb board 256MB memory
-kernel output/images/zImage          \   # kernel linux image
-dtb output/images/versatile-pb.dtb   \   # device tree
-drive file=output/images/rootfs.ext2,if=scsi,format=raw \  #rootfs SCSI disk
-append "root=/dev/sda console=ttyAMA0,115200" \ # kernel command line argument
-serial stdio \ # redirect virtual serial port to stdio
-net nic,model=rtl8139 -net user   # create virtual network and user mode network stack

2.3 为我们自己的Linux写一个hello world

代码和编译

在host机器上创建一个hello.c文件

1
2
3
4
5
6
#include <stdio.h>

void main(void)
{
    printf("hello world in QEMU Linux!\n");
}

使用 output/host/bin目录中的交叉编译编译hello.c

1
.output/host/bin/arm-buildroot-linux-uclibcgnueabi-gcc hello.c -o hello

使用file命令可以检查文件格式,确认文件为arm架构的ELF格式

1
2
file hello
hello: ELF 32-bit LSB pie executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, not stripped

可执行文件导入到QEMU系统

将 images 里的rootfs 挂在的host机器上

1
sudo mount -t ext2 -o rw,loop output/images/rootfs.ext2 /mnt/try

将编译出来的hello可执行文件拷贝到rootfs

1
sudo cp hello /mnt/try/root

取消挂载

1
sudo umount /mnt/try

启动QEMU并运行hello文件

通过Direct Linux Boot启动QEMU,在系统启动后,可以在/root目录下找到hello可执行文件,运行后将输出我们打印的字符串

1
2
3
4
5
6
Welcome to Buildroot
buildroot login: root
# ls
hello
# ./hello
hello world in QEMU Linux!

类似文章

智能家居好伙伴 树莓派,MQTT和Python – 下篇

智能家居好伙伴 树莓派,MQTT和Python – 下篇

  • xhyl
  • October 17, 2020

在本篇文章中,我们主要结合代码来讲解,如何在树莓派上使用Python开发基于MQTT的通讯机制, 项目的整体设计请阅读智能家居好伙伴 树莓派,MQTT和Python – 上篇 本文中提到的代码均在GitHub上共享,大家可以在文章结尾找到GitHub仓库地址。

阅读更多
智能家居好伙伴 树莓派,MQTT和Python – 上篇

智能家居好伙伴 树莓派,MQTT和Python – 上篇

  • xhyl
  • October 10, 2020

作为一个技术宅,不在业余时间折腾折腾,总觉得浑身不得劲,能够利用自己的知识,使自己的生活更加舒适便捷,这也会带来极大的满足感,智能家居项目就是这样一种项目,通过自己的奇思妙想,搭建符合自己和家人生活习惯的贴近生活的小设备是非常有意思的一件事情。 而在搭建这样系统的过程中,我们也可以从中学到更多的技术知识,增加自己的技术储备。在这篇文章中,我们将以树莓派为载体,使用Python搭建一个最基础的智能家居设备(温湿度传感器 + 电脑远程唤醒),目的是通过手机APP能够观察到室内温度湿度,并且可以通过APP远程唤醒自己的NAS服务器。

阅读更多
一文掌握Pandas数据结构

一文掌握Pandas数据结构

  • xhyl
  • June 20, 2020

在网络上的Pandas教程中,很多都提到了如何使用Pandas将已有的数据(如csv,如hdfs等)直接加载成Pandas数据对象,然后在其基础上进行数据分析操作,但是,很多时候,我们需要自己创建Pandas数据对象,并填入一些数据,常见的应用场景如:我们想要将现有的数据进行处理,并生成一个新的Pandas数据对象,还有,我们想利用Pandas的数据保存功能(比如to_csv, to_json, to_hdf等等)把我们采集到的数据写入到IO里边,因此掌握Pandas对象的特性,以及如何创建也是很重要的。因此在本篇文章中,我们主要侧重于介绍Pandas数据结构本身的特性,以及如何创建一个Series或者DataFrame数据对象,并填入一些数据。

阅读更多