1、操作系统安装
1.1 下载OpenEular镜像
1.2 安装到VM VirtualBox虚拟机
该步骤遇到很多问题。最开始在OS实验成长故事平台下载了openeuler21.09:
并使用VMware安装虚拟机,但是安装完成进入后无法连接网络,使用ping命令无法ping通,初步分析原因,应该是yum源没有配置好,于是去配置yum源,但在配置好安装源、安装好必要工具、调整虚拟机参数(主要是网络转换方式)安装很多次依然无法连接网络。随后改用官网下载的openeuler20.03,配合VM Virtualbox虚拟机平台,成功完成搭建。因为之前接触linux系统较少、经验不足,整个从安装到能上网前后折腾了一个星期。
最终的虚拟机配置:
内存、处理器大小视情况而定,虚拟硬盘需要设置尽量大(35~45G比较合适),原因是实验一编译内核需要较大的硬盘空间,若过小会导致安装失败,且虚拟硬盘大小调整也比较复杂。
选择安装磁盘(虚拟磁盘)及分区(自动分区),连接网络(NTA模式),添加root用户密码,开始自动安装。
安装完成后只有终端,且字体太小比较难受,故为其安装桌面环境(gnome)以及terminal。
yum update #报错,提示缺少相关文件
首先配置清华源:
vim /etc/yum.repos.d/openEuler_x86_64.repo #打开配置文件
# 添加如下内容
[osrepo]
name=osrepo
baseurl=https://mirrors.tuna.tsinghua.edu.cn/openeuler/openEuler-20.03-LTS/OS/x86_64/
enabled=1
gpgcheck=1
gpgkey=https://mirrors.tuna.tsinghua.edu.cn/openeuler/openEuler-20.03-LTS/OS/x86_64/RPM-GPG-KEY-openEuler
安装gnome:
dnf install gnome-shell gdm gnome-session #安装gnome及相关组件
dnf install gnome-terminal #安装terminal
#设置开机自启动
systemctl enable gdm.service
systemctl set-default graphical.target
#补全丢失文件
cd /tmp
wget https://gitee.com/name1e5s/xsession/raw/master/Xsession
mv Xsession /etc/gdm/
chmod 0777 /etc/gdm/Xsession
gnome桌面安装成功!
【参考教程:在 openEuler 上安装桌面环境 - 知乎 (zhihu.com)】
yum -y install firefox #顺便安装firefox
2. openEuler内核编译安装
【参考:OpenEuler内核编译与替换】
2.1 系统备份
cd ~
dnf install lrzsz # rz和sz可以在终端下很方便的传输文件
tar czvf boot_origin.tgz /boot/
sz boot_origin.tgz # 将备份文件发送到本地
2.2 内核源码下载
在gitee仓库中下载openEuler内核压缩文件并解压:
【内核地址:openeuler_kernel】
移动到内核源码目录:
2.3 清理源代码树
make mrproper
2.4 生成内核配置文件
复制原配置文件:
依赖安装、更改配置:
yum install ncurses-devel
make menuconfig
选择save,生成配置文件.config:
安装编译所需组件:
yum install elfutils-libelf-devel yum install openssl-devel yum install bc
2.5 编译、安装
make -j4 #四核多线程编译,提升编译效率,cpu发热也提升。
两个半小时后,编译完成:
安装模块:
make modules_install
安装内核:
make install
出现报错:(内核上模块构建的返回状态不正确)
查看编译日志:
找到错误:未知的类型名‘time_t'。
先继续查看安装的内核,发现新内核已经存在了:
更新引导:
重启,可以看到编译成功的新内核,选择新内核回车:
成功进入系统后,查看默认启动内核:
查看内核版本:
内核安装成功。
3 内核模块编程
helloworld.c:
#include<linux/module.h>
MODULE_LICENSE("GPL");
int __init hello_init(void)
{
printk("hello init\n"); //printk函数,而不是之前学的printf
printk("hello,world!\n");
return 0;
}
void __exit hello_exit(void)
{
printk("hello exit\n");
}
module_init(hello_init);
module_exit(hello_exit);
Makefile:
ifneq ($(KERNELRELEASE),)
obj-m := helloworld.o
else
KERNELDIR ?=/usr/src/kernels/5.10.0-4.25.0 #此处实验文档所给为树莓派内核目录,在此要修改为x86_64系统下的内核目录,并且在后续的内核编程中所有的Makefile文件都需要更改为该路径。
PWD := $(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif
.PHONY:clean
clean:
-rm *.mod.c *.o *.order *.symvers *.ko
先学习内核模块和Makefile文件的基本结构:
//内核模块.c文件基本框架
#include<linux/module.h> //包含了对模块的结构定义以及模块的版本控制
MODULE_LICENSE("GPL"); //声明GPL版权
static __init module_init(void){ //加载模块
。。。。。。
}
static __exit module_exit(void){ //卸载模块
。。。。。。
}
module_init(module_init);
module_exit(module_exit);
//Makefile文件基本框架
ifneq ($(KERNELRELEASE),)
obj-m :=main.o //指定将要编译的内核模块列表(一些.o文件)
else
KERNELDIR ?=/usr/lib/modules/$(shell uname -r)/build //内核源代码位置
PWD := $(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules //编译连接目标
endif
.PHONY:clean
clean:
-rm *.mod.c *.o *.order *.symvers *.ko
后续Makefile文件只有内核模块列表那一行不一样,其他部分均参考实验一的Makefile文件,故后续实验Makefile文件不再展示。
编译内核:
编译后的文件列表:
模块的加载、查看、卸载:
内核模块的相关操作:
-
加载内核模块:insmod
-
卸载内核模块:rmmod
-
查看内核模块:lsmod
-
查看打印信息:dmesg | tail -n 行数
实验总结
通过本次实验,我完整地进行了从编写内核源码、make编译、加载、卸载、查看内核模块的整个过程,为后续实验的顺利进行奠定了基础。