另外,里面已经提供了一个简单的 Makefile 例子,可以直接通过 make 生成 hello.i, hello.s 和 hello.o。
ubuntu@linux-lab:/labs/linux-lab/examples/c/hello$ make hello.i
ubuntu@linux-lab:/labs/linux-lab/examples/c/hello$ make hello.s
ubuntu@linux-lab:/labs/linux-lab/examples/c/hello$ make hello.o
在这个基础上,结合任何一本经典的 C 语言书籍,就可以学习基本的语法、算法和 Posix API 用法了。
学习汇编语言和处理器指令集
Linux Lab 不只准备了 x86 的 gcc 编译器,而且准备了其他几大主流处理器架构的编译器,学习各种架构编译器不在话下,而且可以立即使用 qemu 来执行。
X86 汇编
先来看一段 x86 的汇编:
$ tools/docker/bash linux-lab
ubuntu@linux-lab:/labs/linux-lab$ cd examples/assembly/
ubuntu@linux-lab:/labs/linux-lab/examples/assembly$ ls
aarch64 arm mips64el mipsel powerpc powerpc64 README.md riscv32 riscv64 x86 x86_64
ubuntu@linux-lab:/labs/linux-lab/examples/assembly/x86$ make
as --32 -o x86-hello.o x86-hello.s
ld -m elf_i386 -o x86-hello x86-hello.o
/labs/linux-lab/examples/assembly/x86/x86-hello
Hello, world!
ubuntu@linux-lab:/labs/linux-lab/examples/assembly/x86$ cat x86-hello.s
.data # section declaration
msg:
.string "Hello, world!\n"
len = . - msg # length of our dear string
.text # section declaration
# we must export the entry point to the ELF linker or
.global _start # loader. They conventionally recognize _start as their
# entry point. Use ld -e foo to override the default.
_start:
# write our string to stdout
movl $len,%edx # third argument: message length
movl $msg,%ecx # second argument: pointer to message to write
movl $1,%ebx # first argument: file handle (stdout)
movl $4,%eax # system call number (sys_write)
int $0x80 # call kernel
# and exit
movl $0,%ebx # first argument: exit code
movl $1,%eax # system call number (sys_exit)
int $0x80 # call kernel
$ make kernel-run init/main.o
$ ls output/x86_64/linux-v5.6-pc/init/main.o
Linux 内核模块开发
要学习和开发一个 Linux 内核模块是如此的简单。
编译一个准备好的 hello 模块
在 examples/hello 下面已经准备了一个极其简单的内核模块,直接编译:
$ make module module=hello
Building module: hello ...
LOG: m=hello ; M=/labs/linux-lab/modules/hello
Current using module is /labs/linux-lab/modules/hello.
to compile modules under linux-stable, use 'make kernel-modules'.
make O=/labs/linux-lab/output/x86_64/linux-v5.6-pc -C linux-stable ARCH=x86 LOADADDR= CROSS_COMPILE= V= CONFIG_INITRAMFS_SOURCE= -j4 modules_prepare
GEN Makefile
DESCEND objtool
CALL /labs/linux-lab/linux-stable/scripts/atomic/check-atomics.sh
CALL /labs/linux-lab/linux-stable/scripts/checksyscalls.sh
CC [M] /labs/linux-lab/modules/hello/hello.o
MODPOST 1 modules
CC [M] /labs/linux-lab/modules/hello/hello.mod.o
LD [M] /labs/linux-lab/modules/hello/hello.ko
ubuntu@linux-lab:/labs/linux-lab$ make BOARD=x86_64/pc
ubuntu@linux-lab:/labs/linux-lab$ make local-config LINUX=v5.0.21
ubuntu@linux-lab:/labs/linux-lab$ make kernel-cleanup
ubuntu@linux-lab:/labs/linux-lab$ make kernel-defconfig
ubuntu@linux-lab:/labs/linux-lab$ make feature feature=rt
ubuntu@linux-lab:/labs/linux-lab$ make kernel-olddefconfig
ubuntu@linux-lab:/labs/linux-lab$ make kernel
ubuntu@linux-lab:/labs/linux-lab$ make boot
[ 26.744772] 001: Run /init as init process
Starting syslogd: OK
Starting klogd: OK
Initializing random number generator... [ 31.520793] 001: random: dd: uninitialized urandom read (512 bytes read)
done.
Starting network: [ 36.352869] 002: ifconfig (1146) used greatest stack depth: 13904 bytes left
eth: eth0, ip: 172.17.0.61, gw: 172.17.0.3
[ 37.091929] 001: ip (1150) used greatest stack depth: 13600 bytes left
OK
Welcome to Linux Lab
linux-lab login: root
#
# dmesg | grep -i Preempt
[ 0.000000] 000: Linux version 5.0.21-rt16+ (ubuntu@linux-lab) (gcc version 8.3.0 (Ubuntu 8.3.0-16ubuntu3~14.04.2)) #2 SMP PREEMPT RT Fri May 15 04:38:39 CST 2020
[ 3.539891] 000: rcu: Preemptible hierarchical RCU implementation.
U-Boot 2020.04-dirty (May 06 2020 - 16:19:09 +0800)
DRAM: 1 GiB
WARNING: Caches not enabled
Flash: 128 MiB
MMC: MMC: 0
*** Warning - bad CRC, using default environment
In: serial
Out: serial
Err: serial
Net: smc911x-0
Hit any key to stop autoboot: 0
## Warning: defaulting to text format
4471936 bytes read in 1360 ms (3.1 MiB/s)
1430238 bytes read in 442 ms (3.1 MiB/s)
14087 bytes read in 22 ms (625 KiB/s)
## Booting kernel from Legacy Image at 60003000 ...
Image Name: Linux-5.1.0
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 4471872 Bytes = 4.3 MiB
Load Address: 60003000
Entry Point: 60003000
Verifying Checksum ... OK
## Loading init Ramdisk from Legacy Image at 60900000 ...
Image Name:
Image Type: ARM Linux RAMDisk Image (uncompressed)
Data Size: 1430174 Bytes = 1.4 MiB
Load Address: 00000000
Entry Point: 00000000
Verifying Checksum ... OK
## Flattened Device Tree blob at 60500000
Booting using the fdt blob at 0x60500000
Loading Kernel Image
Loading Ramdisk to 7fd18000, end 7fe7529e ... OK
Loading Device Tree to 7fd11000, end 7fd17706 ... OK
Starting kernel ...
Booting Linux on physical CPU 0x0
Linux version 5.1.0 (ubuntu@524b6f3a0481) (gcc version 4.7.3 (Ubuntu/Linaro 4.7.3-12ubuntu1)) #3 SMP Thu May 30 08:44:37 UTC 2019
CPU: ARMv7 Processor [410fc090] revision 0 (ARMv7), cr=10c5387d
CPU: PIPT / VIPT nonaliasing data cache, VIPT nonaliasing instruction cache
...
ubuntu@linux-lab:/labs/linux-lab$ make BOARD=aarch64/virt
ubuntu@linux-lab:/labs/linux-lab$ make list root
[2016.05] 2019.11.1
ubuntu@linux-lab:/labs/linux-lab$ make local-config BUILDROOT=2019.11.1
tools/board/config.sh BUILDROOT=2019.11.1 /labs/linux-lab/boards/arm/vexpress-a9/.labconfig v5.1;
BUILDROOT := 2019.11.1 /labs/linux-lab/boards/arm/vexpress-a9/.labconfig
ubuntu@linux-lab:/labs/linux-lab$ make list root
2016.05 [2019.11.1]
ubuntu@linux-lab:/labs/linux-lab$ make root
调整配置:
ubuntu@linux-lab:/labs/linux-lab$ make root-menuconfig
调整 Busybox 配置:
ubuntu@linux-lab:/labs/linux-lab$ make root-run busybox-menuconfig
通过 Linux Lab 运行 mini Ubuntu
同样以 aarch64/virt 为例:
ubuntu@linux-lab:/labs/linux-lab$ make BOARD=aarch64/virt
下载一份 mini 版本的 Ubuntu:
$ mkdir tmp && cd tmp
$ wget -c https://rcn-ee.com/rootfs/eewiki/minfs/ubuntu-20.04-minimal-armhf-2020-05-10.tar.xz
$ tar Jxf ubuntu-20.04-minimal-armhf-2020-05-10.tar.xz
$ cd ubuntu-20.04-minimal-armhf-2020-05-10
$ mkdir rootfs
$ tar xf armhf-rootfs-ubuntu-focal.tar -C rootfs/
$ make boot ROOTDIR=$PWD/tmp/ubuntu-20.04-minimal-armhf-2020-05-10/rootfs/ ROOTDEV=/dev/nfs U=0
...
Ubuntu 20.04 LTS arm ttyAMA0
default username:password is [ubuntu:temppwd]
arm login: root
Last login: Thu Jan 1 00:01:44 UTC 1970 on ttyAMA0
root@arm:~#
root@arm:~#
Uboot 和 Linux 调试
Uboot 调试
已经提前配置好了 gdb 调试脚本, 可以直接调试 Uboot。
开一个终端:
$ tools/docker/bash linux-lab
ubuntu@linux-lab:/labs/linux-lab$ make debug uboot
根据上一个提示,再开一个:
$ tools/docker/bash linux-lab
ubuntu@linux-lab:/labs/linux-lab$ env PATH=/labs/linux-lab/prebuilt/toolchains/aarch64/gcc-linaro-7.4.1-2019.02-x86_64_aarch64-linux-gnu/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin aarch64-linux-gnu-gdb /labs/linux-lab/output/aarch64/uboot-v2019.10-virt/u-boot
(gdb) bt
GNU gdb (Linaro_GDB-2019.02) 8.2.1.20190122-git
Reading symbols from /labs/linux-lab/output/aarch64/uboot-v2019.10-virt/u-boot...done.
Waiting for 1 secs...
Executing gdb commands in local .gdbinit ... my own script
(gdb) target remote :1234
0xffffff80100876dc in ?? ()
(gdb) break _start
Breakpoint 1 at 0x0: file /labs/linux-lab/u-boot/arch/arm/cpu/armv8/start.S, line 31.
gdb bt
#0 0xffffff80100876dc in ?? ()
#1 0x0000000000000070 in save_boot_params_ret () at /labs/linux-lab/u-boot/arch/arm/cpu/armv8/start.S:117
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
Linux 调试
也能类似直接调试内核:
ubuntu@linux-lab:/labs/linux-lab$ make debug kernel
如果没有打开调试选项:
ubuntu@linux-lab:/labs/linux-lab$ make feature feture=debug
ubuntu@linux-lab:/labs/linux-lab$ make kernel-olddefconfig
ubuntu@linux-lab:/labs/linux-lab$ make kernel
ubuntu@linux-lab:/labs/linux-lab$ make debug kernel
如果想在服务器上运行 Linux Lab,那么启动完,需要获取到服务器的外网 IP 地址,配置到 .host_name 即可。
webssh 登陆方式:
$ echo x.y.z.i > .host_name
$ tools/docker/webssh linux-lab
LOG: Please login via ssh client with:
SSH_IP: 172.17.0.3
SSH_PORT: 22
User: ubuntu
Password: ******
Workdir: //labs/linux-lab
LOG: Or access via web browser:
http://x.y.z.i:4433?ssh=ssh://ubuntu:******@172.17.0.3:22
webvnc 登陆方式:
$ tools/docker/webvnc linux-lab
Please login via VNC Client with:
IP: 172.17.0.3
User: 7827c9 (Only for noVNC)
Password: ****** (Normal)
Password: ****** (View)
Login for noVNC web client:
* Normal: http://x.y.z.i:6080/?u=******&p=******
* View: http://x.y.z.i:6080/?r=************
Login for local VNC clients:
* IP: 172.17.0.3
* Normal: ******
* View: ******
The Other Login methods:
* Bash: tools/docker/bash linux-lab
* SSH: tools/docker/ssh linux-lab
* WebSSH: tools/docker/webssh linux-lab
Note: Please make sure network available outside and then replace 'localhost' with the external ip or domain name.
Then 'echo $ip > .host_name' or 'echo $domain_name > .host_name'
Note: firefox, safari and edge work, but google chrome web browser is preferable.
Linux Lab 推出十大精彩使用案例
Linux Lab 是一套用于 Linux 内核学习、开发和测试的即时实验室,可以极速搭建和使用,功能强大,用法简单!
Linux Lab Boot example
经过 3 年多的开发与迭代,Linux Lab 已经发布了 v0.4-rc3 版,其易用性和功能逐渐强大,非常推荐各位 Linux 内核和嵌入式 Linux 开发者尝试。
下面列举了十大使用 Linux Lab 的案例。
学习 C 语言
安装完以后,如果选择 bash 登陆方式,会立即进入到 Linux 命令行。
C 语言开发
里头已经安装好了必备的编辑器 vim 和编译器 gcc,可以直接上手 C 语言开发。
简单 C 语言工程管理 Makefile
另外,里面已经提供了一个简单的 Makefile 例子,可以直接通过 make 生成 hello.i, hello.s 和 hello.o。
在这个基础上,结合任何一本经典的 C 语言书籍,就可以学习基本的语法、算法和 Posix API 用法了。
学习汇编语言和处理器指令集
Linux Lab 不只准备了 x86 的 gcc 编译器,而且准备了其他几大主流处理器架构的编译器,学习各种架构编译器不在话下,而且可以立即使用 qemu 来执行。
X86 汇编
先来看一段 x86 的汇编:
ARM 汇编
arm 的也不在话下:
RISC-V 汇编
其他架构也可以类似使用,有个别架构,比如 RISC-V,需要先切到 Linux Lab 根目录下载工具链:
之后同样可以使用:
免费使用某块虚拟开发板
7 大架构 + 16 块板子
Linux Lab 已经集成了 7 大架构的 16 块开发板,平均每个架构支持两款以上。常用的开发板基本都有了,包括 32 位和 64 位架构基本可以任选。
所有的板子都有:
编译好的 Qemu 的模拟器
编译好的 文件系统以及配置文件
编译好的 内核镜像、dtb 以及配置文件
可直接启动到串口的 Qemu 启动脚本
所有仓库都可以单独下载,可以在 Linux Lab 内使用,也可以单独使用。这里是已经集成到主线的部分:
任选一块板子立即启动
如果要使用模块板子,可以直接:
所有默认准备好的文件系统帐号是 root,密码为空,直接敲击回车即可进去。大部分板子都可以用
reboot -f
退出,个别板子可能会卡死,可以用 “CTRL+a x” 退出 Qemu。传统的 Qemu 启动脚本:boot.sh
当然,也可以直接用最简单易懂的 boot.sh 脚本,这个脚本甚至可以在主机下直接使用:
不过,boot.sh 可没 “make boot” 灵活方便。比如说,想通过 tftp 加载镜像,并通过 nfsboot 加载文件系统,要用 boot.sh 得手动改一堆不是那么容易理解和上手的参数,特别地,要配置网络并没有那么简单。
无比优雅的 “make boot”
用 Linux Lab 的 “make boot” 是这么的优雅:
Linux 内核开发
直接使用编译好的当然不够酷,得能自己修改内核并编译内核才行。
编译 Linux v5.6
加入我们想学习 x86_64 架构上的 Linux v5.6 内核,可以切到
x86_64/pc
这块板子,然后升级到内核 v5.6:编译其他版本 Linux
如果想用默认的版本,则不需要使用
kernel-clone
,查看已经验证过的内核版本。需要注意的是,kernel-clone 会使用当前板子默认配置的内核配置文件,如果内核版本差异太大,现有配置文件可能不会工作,建议直接从
linux-stable
目录下复制一份现有配置到boards/<BOARD>/bsp/configs
目录下。之后再配置和编译。如果是更早的 Linux 版本,可能会涉及到编译器不兼容,需要修改部分代码或者调整编译器版本。
更换编译器版本
首先我们查看哪些版本可以用,包括内置的版本和外置的版本,内置的版本用
CCORI=internal
指定后,配置GCC
即可,外部的直接指定CCORI
进行切换。查看当前架构支持的编译器,可以看到暂时没有配置外部的版本,但是内部的有三个:
直接切换:
或者更持久的配置:
如果要为某个版本配特定 GCC:
查看记录:
如果发现某个已经配置好的内核必须用特定 GCC 才能编译,请往 Linux Lab 发送 PR。
修改代码后编译启动
接着,随便改一点你想修改的代码,然后就可以重新编译并启动了:
堪称完美,我们改动的地方生效了,日志被打印了出来。
单独编译某个文件并查看效果
比如说刚刚修改了 init/main.c,希望立即查看预处理的结果、汇编以及二进制文件。
预处理的结果:
汇编:
目标文件:
Linux 内核模块开发
要学习和开发一个 Linux 内核模块是如此的简单。
编译一个准备好的 hello 模块
在
examples/hello
下面已经准备了一个极其简单的内核模块,直接编译:修改、编译并简单测试该模块
修改:
编译:
启动后简单验证:
自动验证(无需手动安装,直接取代上述所有命令):
如果有些动作做过了,想直接测试,更简单:
Linux 内核特性开发
先列出当前已经验证过的 feature:
接着来跑一下 rt preempt 特性:
Uboot 开发
支持 Uboot 的板子
当前已经验证可以在 arm32 和 arm64 位的板子上进行 Uboot 开发。已经验证过的板子有:
arm/versatilepb
arm/vexpress-a9
aarh64/virt
启动时禁用 Uboot
默认情况下,会直接启动 Uboot,然后加载内核再启动。
如果想直接启动内核:
启动时切换不同的设备加载内核等镜像
ubuntu@linux-lab:/labs/linux-lab make boot BOOTDEV=mmc mkfs.fat 3.0.26 (2014-03-07) sudo env PATH=/labs/linux-lab/boards/arm/vexpress-a9/bsp/qemu/v4.1.1/bin/:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin qemu-system-arm -M vexpress-a9 -m 1024M -net nic,model=lan9118 -net tap -smp 1 -kernel /labs/linux-lab/output/arm/uboot-v2020.04-vexpress-a9/u-boot -no-reboot -drive if=sd,file=tftpboot/sd.img,format=raw,id=sd0 -drive if=pflash,file=tftpboot/pflash.img,format=raw -nographic
修改、编译并启动 Uboot
默认先编译一遍,会自动下载、配置和编译:
修改代码:
编译并启动,启动过程中立即按下任意键进入 Uboot 命令行:
玩转文件系统
通过 Buildroot 构建文件系统
目前已经内置支持通过 Buildroot 编译文件系统,当然,也可以用 Busybox 来自己制作。
比如说
aarch64/virt
,已经支持到 2019.11.1,可以直接切过去编译:调整配置:
调整 Busybox 配置:
通过 Linux Lab 运行 mini Ubuntu
同样以
aarch64/virt
为例:下载一份 mini 版本的 Ubuntu:
然后以 nfsboot 的方式加载,这里先来删除掉 root 的密码(当前 ubuntu 帐号密码无法登陆):
接着启动并用 root 登陆:
Uboot 和 Linux 调试
Uboot 调试
已经提前配置好了 gdb 调试脚本, 可以直接调试 Uboot。
开一个终端:
根据上一个提示,再开一个:
Linux 调试
也能类似直接调试内核:
如果没有打开调试选项:
灵活多样的登陆方式
本地登陆
以上都是用
tools/docker/bash
在本地登陆,大部分实验都能开展,但是跟图形相关的,比如 LCD 显示这些,就需要 vnc 登陆。之后,vinagre 启动并弹出密码输入框,直接粘贴即可。
远程登陆
如果想在服务器上运行 Linux Lab,那么启动完,需要获取到服务器的外网 IP 地址,配置到
.host_name
即可。webssh 登陆方式:
webvnc 登陆方式: