Linux内核编译及添加系统调用
1.实验要求:
- (1)编译Linux内核;
- (2)添加一个系统调用,实现对指定进程的nice值的修改或读取功能,并返回新的nice值及优先级prio。
2.调用原型:
int mysetnice(pid_t pid, int flag, int nicevalue, void __user* prio, void __user* nice);
参数含义:
- pid:进程ID。
- flag:若值为0,则表示读取nice值;若值为1,则表示修改nice值。
- nicevalue:为指定进程设置新的nice值。
- prio、nice:指向进程当前优先级及nice值。
- 返回值:系统调用成功时返回0;失败时返回错误码EFAULT。
3.实现过程
3.1分配系统调用号,修改系统调用表(arch/x86/entry/syscalls/syscall_64.tbl),如图1所示:
3.2 申明系统调用服务例程原型(include/linux/syscalls.h),如图2所示:
3.3 实现系统调用服务例程(kernel/sys.c)
//kernel/sys.c
SYSCALL_DEFINE5(mysetnice, pid_t, pid, int, flag, int, nicevalue, void __user*, prio, void __user*, nice) {
struct pid* PID;
struct task_struct* pcb;//进程控制块
int cur_prio, cur_nice;
PID = find_get_pid(pid); //根据提供的pid号获取对应的PID
pcb = pid_task(PID, PIDTYPE_PID); //参数type是pid_type型变量,此变量是一个枚举型变量.PIDTYPE_PID是进程的进程号
cur_prio = task_prio(pcb);
cur_nice = task_nice(pcb);
copy_to_user(prio, &cur_prio, sizeof(cur_prio));
copy_to_user(nice, &cur_nice, sizeof(cur_nice));
//将内核空间中的数据拷贝到用户空间
switch(flag) {
case 0:
printk("READ SUCCESSFULLY!\n");
return 0;
case 1:
if( nicevalue >= -20 && nicevalue <= 19) {
set_user_nice(pcb, nicevalue);
cur_prio = task_prio(pcb);
cur_nice = task_nice(pcb);
copy_to_user(prio, &cur_prio, sizeof(cur_prio));
copy_to_user(nice, &cur_nice, sizeof(cur_nice));
printk("SET SUCCESSFULLY!\n");
return 0;
} else {
printk("SET FAILED.INVALID NICEVALUE!\n");
return EFAULT;
}
default:
printk("SET FAILED.INVALID FLAG!\n");
return EFAULT;
}
}
3.4 重新编译内核
在linux-4.16.3目录下执行以下命令:
//清除残留的.config和.o文件
make mrproper
//配置内核
make menuconfig
其中,在make menuconfig时选择General Setup进入,更改版本发行名,如图3所示:
然后继续在当前目录下执行以下命令:
//编译内核
make -j8
//编译模块
make modules
//安装模块
make modules_install
//安装内核
make install
//配置grub引导程序
update-grub2
//重启系统
reboot
系统重启后在Ubuntu高级选项中选择如图4所示内核版本:
重启后在命令行执行以下使用uname -a命令查看当前内核版本,结果如图5所示:
4.验证
4.1 编写测试代码
#include <unistd.h>
#include <sys/syscall.h>
#include <stdio.h>
#define _SYSCALL_MYSETNICE_ 333
#define EFALUT 14
int main()
{
int pid, flag, nicevalue;
int cur_prio, cur_nice;
printf("Please input variable(pid, flag, nicevalue): ");
scanf("%d%d%d", &pid, &flag, &nicevalue);
syscall(_SYSCALL_MYSETNICE_, pid, flag, nicevalue, &cur_prio, &cur_nice);
printf("Priority value is : [%d], nice value is [%d]\n", cur_prio, cur_nice);
return 0;
}
4.2 使用gcc编译后运行,运行结果如图6所示:
接下来使用当前测试程序对编写的内核调用进行测试。当输入的pid错误时,程序的输出如图7所示:
当输入的pid正确时,可以正确查看进程nice值和prio值。先在命令行使用top命令实时查看进程pid,nice值和优先级,如图8所示:
我们选择pid为1861的进程进行nice值和优先级的查看,如图9所示:
在命令行执行dmesg命令查看输出,结果如图10所示:
由图10可知,系统调用返回了指定进程的nice值和优先级,且结果与top中的结果相同。接下来我们进行pid正常,flag值有误时的测试,输入如图11所示:
接下来执行dmesg命令,结果如图12所示:
由图11和图12可知,当输入的pid正确,flag值有误时,系统调用将正确返回指定进程的nice值和优先级,但不会对进程的nice值进行修改,同时也将返回flag有误的错误信息。接下来测试pid正常,flag值为1,nice value有效的情况,如图13所示:
执行dmesg命令,输出如图14所示:
由图13和图14可知,当输入的pid,flag和nice值均正确时,系统调用将正确修改进程的nice值,并将修改后的结果返回,输出修改成功的信息。接下来进行pid正常,flag值为1,nice value有误时的测试。测试输入如图15所示:
执行dmesg命令,输出如图16所示:
由图15和图16可知,当输入的pid,flag正确,但nice值有误时,系统调用将不会对进程的nice值进行修改,但会返回进程原先的nice值,并输出修改失败,nice值有误的错误提示信息。