国产Linux操作系统-openEuler
一、VMware安装openEuler
1. 镜像下载
2. 虚拟机创建
-
因为没有vmware没有openEuler选项,所以选择其它Linux。实验要求的镜像版本是20.03,则选择Linux 4.x。
-
性能分配
-
网络以及其他配置
-
选择映像文件
3. 启动系统
-
选择第一个选项install
-
图形化界面显示
-
设置root密码以及创建用户
-
成功进入系统
-
执行
uname -a
指令 -
执行
getconf PAGESIZE
指令
二、安装图形化界面
1. 换源
sudo vi /etc/yum.repos.d/openEuler_x86_64.repo
vi相比vim而言比较原始,但是更常见,在openEuler系统中没有内置vim,所以这里使用vi。
编写清华源:
2. 安装 GNOME
设置gdm开机自启:
sudo systemctl enable gdm.service
sudo systemctl set-default graphical.target
-
出现的问题:安装完后重启,会遇到 gdm 无法登陆的问题。
问题解决:这个问题是由于 openEuler 的
gdm
的配置文件不全导致的。具体来说,是/etc/gdm/Xsession
指向的/etc/X11/Xsession
不存在。我们首先切换到
root
用户,然后从网络上下载并替换该文件即可:cd /tmp wget https://gitee.com/name1e5s/xsession/raw/master/Xsession mv Xsession /etc/gdm/ chmod 0777 /etc/gdm/Xsession
覆盖文件后,我们输入
reboot
重启,可以看到图形化界面展示如下: -
出现的问题:图形化界面中文乱码。
问题解决:
ctrl
+alt
+F3
切回终端,输入sudo yum -y groupinstall fonts
等待下载完成。
最后使用dnf install gnome-terminal
以及yum -y install firefox
给当前图形化界面安装终端跟火狐浏览器,就完成了。
三、内核的编译安装
1. 系统备份
cd ~
dnf install lrzsz # rz和sz可以在终端下很方便的传输文件
tar czvf boot_origin.tgz /boot/
sz boot_origin.tgz # 将备份文件发送到本地
2. 内核源码下载
-
下载gitee仓库中的内核压缩文件:
-
自动解压后,移动到内核源码目录:
-
清理源代码树:
-
备份文件
-
依赖安装、更改配置:
yum install ncurses-devel
出现的问题:在执行
make menuconfig
命令时缺少了bison
工具。bison
是一个语法分析器生成器,用于生成解析器代码。make menuconfig
依赖于bison
来生成配置菜单的相关文件。问题解决:
yum install bison flex
安装bison
。安装编译所需组件:
yum install elfutils-libelf-devel yum install openssl-devel yum install bc
-
编译安装:
输入
make -j4
四核多线程编译:大约半小时后,显示编译完成。
-
使用
make modules_install
安装模块 -
使用
make install
安装内核 -
cd /boot/
查看安装的内核,发现新内核已经存在了 -
更新引导
grub2-mkconfig -o /boot/grub2/grub.cfg
-
重启之后,再次使用
uname -a
可以看到编译成功的新内核如下:下面是旧内核:
-
四、内核模块编程
1. 程序编写
-
使用C语言编写一个helloworld文件(内核模块)
内核模块.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文件
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
2. 编译内核
-
输入
make
指令进行编译产生的问题:
问题解决:通常是由于Makefile中使用了空格而不是制表符(TAB)作为命令的开头分隔符。在Makefile中,命令行必须以TAB字符开头,而不是空格。
修改以后成功编译,结果如下:
编译后的文件列表如下:
-
内核模块的相关操作:
-
加载内核模块:
insmod
-
卸载内核模块:
rmmod
-
查看内核模块:
lsmod
-
查看打印信息:
dmesg | tail -n 行数
-
五、内存管理
1. kmalloc
使用kmalloc
分配1KB,8KB的内存,并打印指针地址
-
创建
kmalloc.c
文件源代码如下:
#include <linux/module.h> #include <linux/slab.h> MODULE_LICENSE("GPL"); unsigned char *kmallocmem1; unsigned char *kmallocmem2; static int __init mem_module_init(void) { printk("Start kmalloc!\n"); kmallocmem1 = (unsigned char*)kmalloc(1024, GFP_KERNEL); if (kmallocmem1 != NULL){ printk(KERN_ALERT "kmallocmem1 addr = %lx\n", (unsigned long)kmallocmem1); }else{ printk("Failed to allocate kmallocmem1!\n"); } kmallocmem2 = (unsigned char *)kmalloc(8192, GFP_KERNEL); if (kmallocmem2 != NULL){ printk(KERN_ALERT "kmallocmem2 addr = %lx\n", (unsigned long)kmallocmem2); }else{ printk("Failed to allocate kmallocmem2!\n"); } return 0; } static void __exit mem_module_exit(void) { kfree(kmallocmem1); kfree(kmallocmem2); printk("Exit kmalloc!\n"); } module_init(mem_module_init); module_exit(mem_module_exit);
-
创建
Makefile
文件 -
编译加载模块
-
查看内存布局
-
得出结论
在x86_64架构下,虚拟地址空间分为用户空间和内核空间:
- 用户空间:通常是从
0x0000000000000000
到0x00007fffffffffff
。 - 内核空间:通常是从
0xffff800000000000
到0xffffffffffffffff
。
4级页表内存:ffff888000000000 - ffffc87fffffffff (43 bits)
5级页表内存布局:ffff888000000000 - ffffc87fffffffff (52 bits)
在这两种布局中,
kmalloc
分配的内存地址都位于ffff888000000000
到ffffc87fffffffff
这个范围内。这个范围明显高于0xffff800000000000
,属于内核空间。因此得出,kmalloc 分配的内存地址位于内核空间。
- 用户空间:通常是从
2. vmalloc
使用vmalloc
分别分配8KB、1MB、64MB的内存,打印指针地址
-
创建
vmalloc.c
文件源代码如下:
#include <linux/module.h> #include <linux/vmalloc.h> MODULE_LICENSE("GPL"); unsigned char *vmallocmem1; unsigned char *vmallocmem2; unsigned char *vmallocmem3; static int __init mem_module_init(void) { printk("Start vmalloc!\n"); vmallocmem1 = (unsigned char*)vmalloc(8192); if (vmallocmem1 != NULL){ printk("vmallocmem1 addr = %lx\n", (unsigned long)vmallocmem1); }else{ printk("Failed to allocate vmallocmem1!\n"); } vmallocmem2 = (unsigned char*)vmalloc(1048576); if (vmallocmem2 != NULL){ printk("vmallocmem2 addr = %lx\n", (unsigned long)vmallocmem2); }else{ printk("Failed to allocate vmallocmem2!\n"); } vmallocmem3 = (unsigned char*)vmalloc(67108864); if (vmallocmem3 != NULL){ printk("vmallocmem3 addr = %lx\n", (unsigned long)vmallocmem3); }else{ printk("Failed to allocate vmallocmem3!\n"); } return 0; } static void __exit mem_module_exit(void) { vfree(vmallocmem1); vfree(vmallocmem2); vfree(vmallocmem3); printk("Exit vmalloc!\n"); } module_init(mem_module_init); module_exit(mem_module_exit);
-
创建
Makefile
文件 -
编译加载模块
-
查看系统页表大小
结论:vmalloc 分配的内存地址位于内核空间。
六、收获总结
在这次实验中,我通过实际操作openEuler系统,深入体验了内核的编译安装、内核模块的编写以及内存管理的关键技术。这一过程不仅加深了我对操作系统内核工作原理的理解,而且锻炼了我的实践技能和问题解决能力。
内核编译的经历让我对Linux内核的构建过程有了更加直观的认识。从下载源码到配置、编译,再到解决依赖问题和最终的安装,每一步都是对细节的考验,也是对我耐心和解决问题能力的锻炼。特别是在遇到编译错误时,我学会了如何利用系统资源和网络信息来定位并解决问题。在内核模块编程部分,我学习了如何从零开始编写一个简单的内核模块,并通过Makefile进行编译。这个过程让我理解了内核模块与内核之间的交互方式,以及如何在模块中使用printk函数进行调试输出。同时,我也掌握了如何加载和卸载模块,以及如何通过dmesg查看模块的打印信息。内存管理的实验让我对kmalloc和vmalloc这两种内存分配函数有了实际的应用经验。我学习了如何在内核中分配和释放内存,并观察了不同大小内存块的分配情况。这些实验帮助我认识到了内存管理在系统性能和稳定性中的重要性,以及如何避免常见的内存分配错误。
总的来说,这次实验不仅让我获得了宝贵的实践经验,也让我对操作系统的内核有了更深层次的认识。通过亲手操作和解决实际问题,我增强了对复杂技术的理解能力,为将来进一步深入学习操作系统和进行系统级开发打下了坚实的基础。
- 感谢你赐予我前进的力量