杭电OS实验二——添加系统调用之永久修改主机名
CSDN上找了很久也没找到这类资料,故把自己实现的放上来给大家作为参考,此实验实现了永久修改主机名(重启后依然生效),欢迎提问和评论。
实验环境:
· 本机实验环境为 Ubuntu20.04 + linux5.10.37内核
· Ubuntu虚拟机root用户名为su
1. 安装linux内核
虚拟机打开firefox浏览器,打开下面网址
https://mirrors.edge.kernel.org/pub/linux/kernel/v5.x/
选择安装linux-5.10-37.tar.xz
如果虚拟机很慢的话也可以本地下载后,打开虚拟机文件夹直接拉进去
解压内核源码:
xz -d linux-5.10.37.tar.xz (执行1分钟,中间无信息)
tar -xvf linux-5.10.37.tar
2. 添加系统调用
注意要进入解压后的linux-5.10.37文件中:
打开终端,输入"sudo su" 以root身份运行,进入内核文件夹下:
cd linux-5.10.37
2.1 分配系统调用号,修改系统调用表
运行命令:
vim ./arch/x86/entry/syscalls/syscall_64.tbl
如果报错无vim 执行:
sudo apt-get install vim
运行命令:
vim ./arch/x86/entry/syscalls/syscall_64.tbl
修改系统调用表:
441是系统调用号,hlsetname是要定义的系统调用函数名,可根据自己实际情况修改
建议加在图示位置(common下面x32上面),此外实验书上说的要在sys_前加__64,经实验认为可不加。
2.2 申明系统调用服务例程原型
运行命令:
vim ./include/linux/syscalls.h
在文件最下面添加即可:
asmlinkage long sys_hlsetname(char __user *name,int len);
> sys_hlsetname是刚刚在系统调用表里定义的函数
> (char __user *name,int len)设置函数所需参数
> 这里注意内核函数只能传char *类型的,不能直接传char类型,否则用户态调用时会报错
2.3 实现系统调用服务例程
执行命令:
vim ./kernel/sys.c
增加代码:
// “DEFINE2”里的2代表2个参数 (这里是name和len),可根据自身定义参数个数修改
SYSCALL_DEFINE2(hlsetname, const char __user *, name, int, len) {
struct file *filp;
char *tmp;
int ret = 0;
//判断设置名字长度是否合法
if (len < 0 || len > PATH_MAX)
return -EINVAL;
//设置一个缓冲区
tmp = kmalloc(len, GFP_KERNEL);
if (!tmp)
return -ENOMEM;
//从用户态复制主机名到缓冲区
//注意要使用copy_from_user函数,否则用户编译程序时会报错“已杀死”
if (copy_from_user(tmp, name, len)) {
ret = -EFAULT;
goto out;
}
//打开/etc/hostname文件,只有修改此文件才能永久修改主机名
filp = filp_open("/etc/hostname", O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (IS_ERR(filp)) {
ret = PTR_ERR(filp);
goto out;
}
//写入要修改名字
ret = kernel_write(filp, tmp, len, &filp->f_pos);
if (ret < 0)
ret = -EIO;
else
ret = 0;
//关闭文件
filp_close(filp, NULL);
out:
kfree(tmp);
return ret;
}
3. 重新编译内核
3.1 预先安装工具包
若报错无法修正错误则执行 sudo apt-get update
apt-get install libncurses5-dev
apt-get install libssl-dev
apt-get install bison
apt-get install flex
apt-get install make
apt-get install pkg-config
3.2 清除残留的.confiapt-get install makeg 和.o 文件
在开始完全重新编译之前,需要清除残留的.config 和.o 文件
执行命令: 要 cd 进入 linux-5.10.37 子目录
make mrproper
3.3 配置内核
执行命令:
make menuconfig
执行过程出现下面界面,选 64bit kernel + +回车:
点击一次exit后回到原来页面是正常现象,只需按左键再点一次exit退出
3.4 make编译内核,生成启动映像文件 (大概一个多小时)
执行命令:
make -j4 //-j4是使用4核来加快速度,可根据自己虚拟机的配置更改为j8或j16
个人make阶段经历的所报错误如下:
报错1:
warning: Cannot use CONFIG_STACK_VALIDATION=y, please install libelf-dev, libelf-devel or elfutils-libelf-devel error: Cannot resolve BTF IDs for CONFIG_DEBUG_INFO_BTF, please install libelf-dev, libelf-devel or elfutils-libelf-devel入代码片
安装libelf-dev
apt-get install libelf-dev
报错2:
./include/linux/syscalls.h:311: error: unterminated #ifndef 311 | #ifndef CONFIG_ARCH_HAS_SYSCALL_WRAPPER | In file included from init/main.c:21: ./include/linux/syscalls.h:9: error: unterminated #ifndef make[1]: [scripts/Makefile.build:279:init/main.o] 错误 1 make: [Makefile:1821:init] 错误 2
解决:
执行下面命令:
gedit ./kernel/sys.c
这个错找了很久,最后经过与bootlin网站上面的源码比对发现是因为sys.c最后一行(2735行)漏了一个endif,加上下面内容即可:
#endif /* CONFIG_COMPAT */
报错3:
make[1]: *** 没有规则可制作目标“debian/canonical-certs.pem”,由“certs/x509_certificate_list” 需求。 停止。
解决:
参考博客 https://blog.csdn.net/m0_51203305/article/details/120805372
执行下面步骤:
gedit .config
将10372行的 CONFIG_SYSTEM_TRUSTED_KEYS=“debian/canonical-certs.pem” 改为 CONFIG_SYSTEM_TRUSTED_KEYS=“”
注意:当你完全重新编译使用命令#make mrproper 后.config文件就会被清理,再次make会报这个错误
报错4:
BTF: .tmp_vmlinux.btf: pahole (pahole) is not available
Failed to generate BTF for vmlinux
Try to disable CONFIG_DEBUG_INFO_BTF
解决:
sudo apt-get install dwarves
再次编译前要清除残留错误重新配置
make mrproper
make menuconfig
3.5 编译模块 (很短)
执行指令:
make modules -j4
执行成功结果:
3.6 安装内核 (半小时左右)
执行指令:
安装模块:make modules_install
安装内核:make install
make modules_install成功界面:
make install结果:
个人在 make install 经历如下报错:
解决:安装zstd库然后重来
sudo apt-get install zstd
3.7 配置 grub 引导程序
执行指令:
update-grub2
3.8 重启系统
执行指令:
reboot
然后进入终端查看内核版本:
uname -a
个人在这里报错reboot无法切换内核,版本还是原内核:
查看服务器启动内核的顺序
执行以下命令:
grep menuentry /boot/grub/grub.cfg
可发现 Ubuntu, with Linux 5.10.37 (recovery mode)在第五个 [注意顺序是从0开始算的],记住这个顺序数
在终端运行命令 :
sudo gedit /etc/default/grub
将打开文件的红框内 GRUB_DEFAULT=0 修改为 GRUB_DEFAULT=“1> 5”
更新grub:
sudo update-grub
重启服务器:
sudo shutdown -r now
4. 编写用户态程序测试程序
我的调用函数的hlsetname系统调用号是441
我把测试程序放在/ostest文件夹下
进入对应文件夹编写测试程序
执行以下命令:
cd ostest
gedit test.c
编写程序如下:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <string.h>
#include <errno.h>
#define __NR_mysyscall 441
int main(void) {
char new_hostname[] = "banana"; //banana是我想把主机名修改成的字符串
char current_hostname[256]; //存储当前主机名
//调用系统调用函数
long ret = syscall(441, new_hostname, strlen(new_hostname));
//判断是否调用成功
if (ret < 0) {
fprintf(stderr, "Failed to set hostname: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
//拿取当前主机名
if (gethostname(current_hostname, sizeof(current_hostname)) == -1) {
perror("Failed to get current hostname");
exit(EXIT_FAILURE);
}
//打印输出
printf("New hostname: %s\n", new_hostname);
printf("Current hostname: %s\n", current_hostname);
return 0;
}
编译程序
gcc -o test2 test2.c
执行程序
./test2
执行完成后,有两种方法查看是否修改成功
一是执行以下指令
gedit /etc/hostname
查看里面的主机名是否已经更改为你在用户态测试程序里所设定的字符串,若二者一致,恭喜你,修改成功~
二是直接reboot重启系统,打开终端看@后面的名字是否已经更改为你在用户态测试程序里所设定的字符串,若二者一致,也表示修改成功~
彩蛋:
再给大家提供几个系统调用流程写的比较详细的博客网址:
https://www.qyyshop.com/info/939378.html //这个图文齐全,点很细
https://blog.csdn.net/qq_43561345/article/details/109952820
//这个也是详细的系统调用修改主机名,但是临时性修改,重启后名字不会改变