杭电网安操作系统实验一:Linux 内核编译及添加系统调用
文章较长直接点击以下目录索引即可
一、实验介绍:
本实验通过修改Linux内核源码,添加新的Linux系统调用,替换编译后内核,并测试结果,了解Linux内核源码的编译方法和内核的安装方法,系统调用的概念、编写步骤和调用方法。
二、任务描述:
①掌握Linux 系统调用基本概念
②Linux内核源码的编译和安装
③添加Linux的系统调用
④Linux的系统调用的测试方法
三、具体实验步骤:
(一)实验思路:
(二)遇到问题及解决办法:
1.在kernel的include\linux路径下的syscalls.h文件中添加代码后,在编译内核中遇到了如下图中的问题
在通过网上学习知道了,这是由于函数无返回值而产生的报错,将原来代码中的asmlinkage long sys_zlk();改为无返回值函数asmlinkage long sys_zlk(void);便可解决该报错。
2.在添加完各部分的函数之后开始编译内核时,我还遇到了如下图的问题
这个问题看似报错和上面的很像,但是我怎么也找不到问题所在,最后在询问之下我才知道,完成的同学都是把新加的函数放到了文档末尾,而我则是放在文档开头,抱着试一试的心态,我调整了函数在文档中的位置,果然这一次内核编译成功了。
3.在使用本地虚拟机编译内核的时候,一个典型的问题就是缺各种依赖包,只需下载安装一下各类依赖就能解决了。
(三)核心代码与结果展示:
<1>掌握Linux 内核的编译与安装(任务0)
1.登录系统并查看当前内核版本
输入uname -r
2. 安装工具,构建开发环境(按序输入如下命令):
yum group install -y "Development Tools"
yum install -y bc
yum install -y openssl-devel
3.备份boot目录以防后续步骤更新内核失败
tar czvf boot.origin.tgz /boot/
uname –r > uname_r.log
完成以上操作后,ls
查看当前目录下的文件会看到两个新增的文件
3. 获取内核源代码并解压
wget https://gitee.com/openeuler/kernel/repository/archive/kernel-4.19.zip
unzip kernel-4.19.zip
完成以上操作后, ls
查看当前目录下的文件会看到两个新增的文件
5.编译内核
cd kernel
make openeuler_defconfig
看一下编译内核有哪些可配置项,这一步查看了可编译的Image
make help | grep Image
这一步是编译内核的Image、modules和dtbs,make -j 4表示4个线程编译(可以根据CPU核数调整)
make -j4 Image modules dtbs
6.安装内核
make modules_install
make install
7.安装好内核之后重启虚拟机便可选择使用新内核了<第一行为新内核,选择进入>
reboot
4. 登录系统并查看版本uname -r
<2> 掌握Linux 系统调用基本概念
- 把系统调用函数加入到syscalls数组中(操作目录:include/uapi/asm-generic/unistd.h)
/* added by zlk */
#define __NR_zlk 294
__SYSCALL(__NR_zlk, sys_zlk)
2. 在头文件(include/linux/syscalls.h)中声明系统调用函数
/* added by zlk */
asmlinkage long sys_zlk(void);
3.在kernel/sys.c中添加函数体
/* added by zlk */
SYSCALL_DEFINE0(zlk)
{
printk(KERN_INFO "姓名:XXX,学号:XXXXXX");
return 0;
}
4.编译、安装内核
配置内核:make menuconfig
编译内核:make -j64
编译模块:make modules
安装模块:make modules_install
安装内核:make install
立即重启:reboot
5.编写测试代码
#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
int main()
{
printf("Test sys_zlk:");
if (syscall(294) == 0)
printf("success!\n");
else
printf("fail!\n");
return 0;
}
6.编译生成syscall_zlk文件,先用dmesg -c
命令清空原有日志后用dmesg
查看运行结果
<3>设计和添加系统调用
任务一:修改或返回指定进程的优先级(nice值和prio值)
1.把系统调用函数加入到syscalls数组中(操作目录:include/uapi/asm-generic/unistd.h)
/* added by zlk */
#define __NR_mysetnice 295
__SYSCALL(__NR_mysetnice,sys_mysetnice)
2.在头文件(include/linux/syscalls.h)中声明系统调用函数
/* added by zlk */
asmlinkage long sys_mysetnice(pid_t pid,int flag,int nicevalue,void __user*prio,void __user*nice);
3.在kernel/sys.c中添加函数体
/* added by zlk */
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;
}
4.编译、安装内核
配置内核:make menuconfig
编译内核:make -j64
编译模块:make modules
安装模块:make modules_install
安装内核:make install
立即重启:reboot
5.编写测试代码
#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;
}
6.编译生成mysetnice.c文件,通过gcc mysetnice.c -o mysetnice
生成mysetnice文件后运行./mysetnice
任务二:添加一个新的系统调用,改变主机名称为自定义字符串
1.把系统调用函数加入到syscalls数组中(操作目录:include/uapi/asm-generic/unistd.h)
/* added by zlk */
#define __NR_mysethostname 296
__SYSCALL(__NR_mysethostname,sys_mysethostname)
2.在头文件(include/linux/syscalls.h)中声明系统调用函数
/* added by zlk */
asmlinkage long sys_mysethostname(char __user *name, int len);
3.在kernel/sys.c中添加函数体
/* added by zlk */
SYSCALL_DEFINE2(mysethostname, char __user *, name, int, len)
{
int errno;
char tmp[__NEW_UTS_LEN];
if(len<0 || len>__NEW_UTS_LEN)
return -EINVAL;
errno = -EFAULT;
if(!copy_from_user(tmp, name, len))
{
struct new_utsname *u;
down_write(&uts_sem);
u = utsname();
mcpy(u->nodename, tmp, len);
memset(u->nodename + len, 0, sizeof(u->nodename)- len);
errno = 0;
uts_proc_notify(UTS_PROC_HOSTNAME);
up_write(&uts_sem);
}
return errno;
}
return -EFAULT; //errno:14 地址错
return -EAGAIN; //errno:11 资源暂时不可用
return -EINTR; //errno:4 中断的函数调用
return -ESPIPE //errno:29 无效的文件指针重定位
return -ENOTTY;//errno:25 不适当的IO控制操作
return -EINVAL;//errno:22 无效的参数
4.编译、安装内核
配置内核:make menuconfig
编译内核:make -j64
编译模块:make modules
安装模块:make modules_install
安装内核:make install
立即重启:reboot
5.编写测试代码
#define _GUN_SOURCE
#include<unistd.h>
#include<sys/syscall.h>
#include<stdio.h>
int main()
{
syscall(296,"zlkzlk",6);
return 0;
}
6.编译生成mysethostname.c文件,通过gcc mysethostname.c -o mysethostname
生成mysethostname文件后运行./mysethostname