1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > 杭电操作系统实验一 --- Linux内核编译及添加系统调用(arm架构华为云)

杭电操作系统实验一 --- Linux内核编译及添加系统调用(arm架构华为云)

时间:2020-08-14 16:00:35

相关推荐

杭电操作系统实验一 --- Linux内核编译及添加系统调用(arm架构华为云)

实验要求

掌握Linux 内核的编译与安装掌握Linux 系统调用基本概念设计和添加linux系统调用

(1)修改或返回指定进程的优先级(nice值和prio值)(详见教材P328)提示:可能参考的内核函数:set_user_nice().

(2)改变主机名称为自定义字符串(自选题目)

1、Linux 内核的编译与安装 (使用华为云,完成openEuler内核的编译与安装)

(1)登录系统并查看当前内核版本

[root@openEuler ~]# uname -r

(2)安装工具,构建开发环境

[root@openEuler ~]# yum group install -y "Development Tools"

[root@openEuler ~]# yum install -y bc

[root@openEuler ~]# yum install -y openssl-devel

(3)备份boot目录以防后续步骤更新内核失败

[root@openEuler ~]# tar czvf boot.origin.tgz /boot/

保存当前内核版本信息

[root@openEuler ~]# uname –r > uname_r.log

(4)获取内核源代码并解压

[root@openEuler ~]# wget /openeuler/kernel/repository/archive/kernel-4.19.zip

[root@openEuler ~]# unzip kernel-4.19.zip

(5)编译内核

[root@openEuler ~]# cd kernel-kernel-4.19

[root@openEuler kernel]# make openeuler_defconfig

[root@openEuler kernel]# make -j4 Image modules dtbs

这一步是编译内核的Image、modules和dtbs,make -j 4表示4个线程编译(可以根据CPU核数调整)

(6)安装内核

[root@openEuler kernel]# make modules_install

[root@openEuler kernel]# make install

注意:在最后一步“make install”时出现的错误在这里可以忽略。

(7)以VNC登录ECS

(8)重启系统

[root@openEuler kernel]# reboot

(9)登录并验证

在VNC窗口中选择以新编译出来的内核启动系统

这里编译完以后已经有了4.19.208版本的新内核,选择该内核登录

2、掌握Linux 系统调用基本概念

Linux系统处理系统调用的流程以及增加系统调用的方法。Linux系统提供了多达几百种的系统调用,为了唯一地标识每一个系统调用,Linux为每个系统调用都设置了一个唯一的编号,称为系统调用号,同时每个系统调用需要一个服务例程完成其具体功能。

这里不做过多描述。

(重点是怎么添加系统调用!!!)

阅读教材7.2.3节Linux 添加系统调用的步骤,添加一个新的系统调用,通过内核打印调试语句printk打印自己的学号。

cd kernel-kernel-4.19 进入文件夹viminclude/uapi/asm-generic/unistd.h 修改及添加系统调用号(注意系统调用号不能随意加,只能一次加1)

#define __NR_hello_euler 294__SYSCALL(__NR_hello_euler, sys_hello_euler)#undef __NR_syscalls#define __NR_syscalls 295

vim include/linux/syscalls.h 添加代码申明系统调用服务例程

asmlinkage long sys_hello_euler(void);

vimkernel/sys.c实现系统调用服务例程

SYSCALL_DEFINE0(hello_euler){printk(KERN_INFO "xuehao:20273108");return 0;}

重新编译,安装内核配置内核:make openeuler_defconfig

编译内核:make -j4 Image modules dtbs

安装模块:make modules_install

安装内核:make install

立即重启:reboot (重启后进入新内核)

重启后

cd kernel-kernel-4.19 进入文件夹

vim testhello.c编写测试代码testhello.c

#include <stdio.h>#include <unistd.h>#include <sys/syscall.h>int main(){ret = syscall(294); return 0;}

编译 gcc testhello.c输入 ls ,会发现出现了a.out 文件(在使用gcc编程时,没有指定输入可执行文件名,默认生成可执行文件a.out文件。) 执行./a.out输入 dmesg 查看结果(成功输出学号~)

3、设计和添加linux系统调用

(1)修改或返回指定进程的优先级(nice值和prio值)(详见教材P328)提示:可能参考的内核函数:set_user_nice().

viminclude/uapi/asm-generic/unistd.h 修改及添加系统调用号

#define __NR_mysetnice 295__SYSCALL(__NR_mysetnice,sys_mysetnice)

注意下面的 #define __NR_syscalls 295 要变成 #define __NR_syscalls 296

vim include/linux/syscalls.h 添加代码申明系统调用服务例程

asmlinkage long sys_mysetnice(pid_t pid,int flag,int nicevalue,void __user*prio,void __user*nice);

vimkernel/sys.c实现系统调用服务例程

SYSCALL_DEFINE5(mysetnice,pid_t,pid,int,flag,int,nicevalue,void __user*,prio,void __user*,nice){int n;int p;struct pid * kpid;struct task_struct * task;kpid = find_get_pid(pid);/*得到pid */task = pid_task(kpid, PIDTYPE_PID);/* 返回task_struct */n = task_nice(task);/* 返回进程当前nice值 */p = task_prio(task);/*返回进程当前prio值*/if(flag == 1){set_user_nice(task, nicevalue);/* 修改进程nice值 */n = task_nice(task);/*重新取得进程nice值*/p = task_prio(task);/*重新取得进程prio值*/copy_to_user(nice,&n,sizeof(n));/*将nice值拷贝到用户空间*/copy_to_user(prio,&p,sizeof(p));/*将prio值拷贝到用户空间*/return 0; }else if(flag == 0){copy_to_user(nice,&n,sizeof(n));/*将nice值拷贝到用户空间*/copy_to_user(prio,&p,sizeof(p));/*将prio值拷贝到用户空间*/return 0;}return EFAULT;}

同上,重新编译安装内核配置内核:make openeuler_defconfig

编译内核:make -j4 Image modules dtbs

安装模块:make modules_install

安装内核:make install

立即重启:reboot (重启后进入新内核)

cd kernel-kernel-4.19 进入文件夹

vim my_setnice.c编写测试代码my_setnice.c

#define _GNU_SOURCE#include<unistd.h>#include<sys/syscall.h>#include<stdio.h>#include<stdlib.h>int main(){pid_t pid;int nicevalue;int flag;int n=0;int p=0;int *prio;int *nice;prio = &p;nice = &n;printf("请输入pid: \n");scanf("%d",&pid);printf("pid输入成功\n请输入nice值:\n");scanf("%d",&nicevalue);printf("nice输入成功\n请输入flag(flag为1时修改,为0时查看):\n");scanf("%d",&flag);syscall(295,pid,flag,nicevalue,prio,nice);printf("现在的nice为%d,prio为%d\n",n,p);return 0;}

gcc my_setnice.c./a.out

(2)改变主机名称为自定义字符串(自选题目)

viminclude/uapi/asm-generic/unistd.h 修改及添加系统调用号

#define __NR_mysethostname 296__SYSCALL(__NR_mysethostname,sys_mysethostname)

同理,下面的 #define __NR_syscalls 296 要变成 #define __NR_syscalls 297

vim include/linux/syscalls.h 添加代码申明系统调用服务例程

asmlinkage long sys_mysethostname(char __user *name, int len);

vimkernel/sys.c实现系统调用服务例程

SYSCALL_DEFINE2(mysethostname, char __user *, name, int, len){int errno;char tmp[__NEW_UTS_LEN]; /*__NEW_UTS_LEN=64*/if(len<0 || len>__NEW_UTS_LEN)return -EINVAL;errno = -EFAULT;if(!copy_from_user(tmp, name, len)) /*copy_from_user将name指向的字符串从用户空间拷贝到内核空间,失败返回没有被拷贝的字节数,成功返回0*/{struct new_utsname *u;down_write(&uts_sem); /*写者使用该函数来得到读写信号量sem,它会导致调用者睡眠,只能在进程上下文使用,用于获取Linux内核读写信号量中的写锁*/u = utsname(); /*获取当前内核名称和其它信息,成功执行时,返回0。失败返回-1,errno被设为EFAULT,表示buf无效*/memcpy(u->nodename, tmp, len); /*从源内存地址的起始位置开始拷贝若干个字节到目标内存地址中*/memset(u->nodename + len, 0, sizeof(u->nodename)- len);errno = 0;uts_proc_notify(UTS_PROC_HOSTNAME); /*使用UTS namespace隔离hostname*/up_write(&uts_sem); /*释放写锁*/}return errno;}

同上重新编译内核
cd kernel-kernel-4.19 进入文件夹

vim sethostname.c编写测试代码sethostname.c

#define _GUN_SOURCE#include<unistd.h>#include<sys/syscall.h>#include<stdio.h>int main(){syscall(296,"xjyxjy",6);return 0;}

gcc sethostname.c./sethostname输入命令hostname或者重新在终端上连接ssh,即可看到主机名已经成功更换

4、实验总结

(1)大家做实验之前看教程一定要搞清楚是x86还是arm架构的呀!!!我就是在这上面栽跟头了重建了三四次华为云

(2)如果用VNC登陆时出现以下报错,那么恭喜你,多半是你的内核崩了~我的老师说,只要你手速够快,在重启虚拟机的时候立马用VNC重新登陆是可以进去的,但是我没成功过,只能重建了n次云主机。

(3)虽然老师给的实验报告里要求VNC登录,但我个人建议使用cloudshell来执行命令。

(4)在验收的时候,被老师冷不丁问了系统调用里的函数的意义和用法,直接栽跟头了。我会在下面po出一部分内部函数的意义及用法。

1.find_get_pid(pid)

find_get_pid 在内核中有不同的命名空间,在各自的命名空间同一个进程pid值可能是不一样的,find_get_pid为了找到在内核态中的我们寻找进程真正的pid

2.set_user_nice(task, nicevalue)

用于设置进程的nice值

3.copy_to_user()

完成内核空间到用户空间的复制,To 目标地址,这个地址是用户空间的地址;From 源地址,这个地址是内核空间的地址; N 将要拷贝的数据的字节数。

如果数据拷贝成功,则返回零;否则,返回没有拷贝成功的数据字节数。

4.copy_from_user()

copy_from_user将name指向的字符串从用户空间拷贝到内核空间,失败返回没有被拷贝的字节数,成功返回0。

5.down_write()

函数down_write()是写者用来得到读写信号量sem时调用的,如果该信号量被读者或写者所持有,则对该函数的调用会导致调用者的睡眠,只能在进程上下文使用,用于获取Linux内核读写信号量中的写锁。

6.memcpy(str1, str2, n)

从存储区str2复制n个字节到存储区str1。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。