杭电实验一:用云主机加速内核编译
本次实验我使用极客云,价格良心,使用的是容器。本博客只打算粗略地写一写,写起来很头大。
(ps:如果你觉得本文不错,可以去传送门的博客园链接中赞助我-。-
欢迎杭电学弟学妹加我好友,联系方式也在博客园链接中。)
下载内核
进入主机后输入,下载linux内核,下载4.18会比较好一些
wget https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.18.19.tar.xz
使用命令解压
xz -d linux-4.19.tar.xz
tar -xvf linux-4.19.tar
安装make
我们需要安装make
apt-get install make
我们第一次编译先调用
make mrproper
期间报错需要安装:
sudo apt-get install libncurses5-dev
然后调用:
make menuconfig
点save=>exit
我们可以先make一下,大概不超过15分钟,-j表示使用最大核心数。
make -j
期间根据提示需要安装:
sudo apt-get install bison
sudo apt-get install flex
sudo apt-get install libelf-dev
sudo apt-get install libssl-dev
sudo apt-get install bc
apt-get install python-software-properties
apt-get install software-properties-common
-
make mrproper命令会删除所有的编译生成文件、内核配置文件(.config文件)和各种备份文件,所以几乎只在第一次执行内核编译前才用这条命令。
make clean命令则是用于删除大多数的编译生成文件,但是会保留内核的配置文件.config,还有足够的编译支持来建立扩展模块。所以你若只想删除前一次编译过程的残留数据,只需执行make clean命令。
总而言之,make mrproper删除的范围比make clean大,实际上,make mrproper在具体执行时第一步就是调用make clean
更改gcc
由于云主机内gcc版本过低,可能在make时会报错,我们需要下载gcc-8
sudo add-apt-repository ppa:ubuntu-toolchain-r/test
sudo apt-get update
sudo apt-get install gcc-8 g+±8
gcc-8 --version
由于默认用gcc编译内核,我们只好把gcc改为gcc-8
cd /user/bin
创建软连接ln -s gcc-8 gcc
添加系统调用
vim linux-4.19/arch/x86/entry/syscalls/syscall_64.tbl
336 64 mychangenicesyscall __x64_sys_change_nice_syscall
注意不要放在最后,放在合适的位置。
vim linux-4.19/include/linux/syscalls.h
int change_nice_syscall(pid_t pid, int flag, int nicevalue,void_user*prio,void_user*nice)
sys.c
摘自https://blog.csdn.net/babybabyup/article/details/79839734#comments,里面有些错误,已改正。
SYSCALL_DEFINE5,2是两个变量,5是五个变量,0是没有变量。
SYSCALL_DEFINE5(mysetnice,pid_t,pid,int,flag,int,nicevalue,void __user*,prio,void __user*,nice){
struct pid * kpid;/*进程描述符指针,指向一个枚举类型*/
struct task_struct * task;/*任务描述符信息*/
kpid = find_get_pid(pid);/* 根据进程号返回kpid,并增加引用次数count+1*/
/*当引用计数为0时,当前进程就可以被抢占.*/
task = pid_task(kpid, PIDTYPE_PID);/* 返回task_struct */
int n;
n = task_nice(task);/* 返回进程当前nice值 */
int p; /*这里linux内核编译时建议,先声明后赋值,不建议一起做*/
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;
}
- 关于用户空间和系统空间
copy_to_user
来自:~/arch/i386/lib/usercopy.c
相关函数:copy_from_user
- 实时进程(rt_priority)和非实时进程
所谓实时,就是要你即刻处理,换言之,在容忍的范围内得马上处理,优先级要高(之后会讲),并且能够抢占其他进程。
实时进程之间的调度除了看优先级外,还需要看调度策略。
- find_get_pid
来自:/linux/kernel/pid.c
enum pid_type
{
PIDTYPE_PID, //进程的进程号
PIDTYPE_PGID, //进程组领头进程的进程号
PIDTYPE_SID, //会话领头进程的进程号
PIDTYPE_MAX
};
- struct task_struct
task_struct 中的成员变量 prio越小,进程的优先级越高。prio 值的取值范围为0…139。
来自:linux-2.6.30/include/linux/sched.h
#define MAX_USER_RT_PRIO 100
#define MAX_RT_PRIO MAX_USER_RT_PRIO
#define MAX_PRIO (MAX_RT_PRIO + 40)
#define DEFAULT_PRIO (MAX_RT_PRIO + 20)
其中,实时进程(sched policy)的优先级取值范围是0…99,非实时进程的取值范围为100…139。
优先级转nice值时,通过当前task的静态优先级的值减去DEFAULT_PRIO。DEFAULT_PRIO的值是120。
- task_prio
int task_prio(const struct task_struct *p)
{
return p->prio - MAX_RT_PRIO;//ps 命令得到的priority 值也是 p->prio - 100 得到的!!!
}
- set_user_nice源码
void set_user_nice(struct task_struct *p, long nice)
{
bool queued, running;
int old_prio, delta;
struct rq_flags rf;
struct rq *rq;
//如果当前task的nice值已经等于要设置的nice值,就直接退出
//从这里可以看出nice值的范围在-20~19 之间
if (task_nice(p) == nice || nice < MIN_NICE || nice > MAX_NICE)
return;
rq = task_rq_lock(p, &rf);//锁住进程队列
update_rq_clock(rq);
//如果当前是实时进程,这里也可以看出实时进程的的调度策略可以分为deadline/fifo/rr.
//针对实时进程设置nice值其实是没有作用的,但是这里还是将nice值转成优先级后设置到p->static_prio
if (task_has_dl_policy(p) || task_has_rt_policy(p)) {
p->static_prio = NICE_TO_PRIO(nice);
goto out_unlock;
}
queued = task_on_rq_queued(p);
running = task_current(rq, p);
if (queued)
dequeue_task(rq, p, DEQUEUE_SAVE | DEQUEUE_NOCLOCK);
if (running)
put_prev_task(rq, p);
//将nice值转成优先级设置到static_prio 中,#define NICE_TO_PRIO(nice) ((nice) + DEFAULT_PRIO)
//这里的DEFAULT_PRIO 值经过计算是120.
//从这里也可以看出优先级转nice值应该是减去DEFAULT_PRIO #define PRIO_TO_NICE(prio) ((prio) - DEFAULT_PRIO)
p->static_prio = NICE_TO_PRIO(nice);
set_load_weight(p);
old_prio = p->prio;
p->prio = effective_prio(p);
delta = p->prio - old_prio;
//如果要设置的nice的task在queue中
if (queued) {
enqueue_task(rq, p, ENQUEUE_RESTORE | ENQUEUE_NOCLOCK);
//如果增大优先级且task正在运行或者减小优先级的话,则重新调度rq。
if (delta < 0 || (delta > 0 && task_running(rq, p)))
resched_curr(rq);
}
//如果要设置nice值的task正在运行,由于我们这里改变了p的优先级,则重新指定task的rq.
if (running)
set_curr_task(rq, p);
out_unlock:
task_rq_unlock(rq, p, &rf);
}
- rq
进程就绪队列
- 其他函数获取nice值
getpriority函数可以像nice函数获取进程nice值,而且他还可以获取一组相关进程的nice值。
make一下
make modules
make modules_install
make install
云主机无法执行update-grub2:服务器采用的是容器,貌似搞不了。
所以我们只能看看编译产生了什么文件,复制到虚拟机上:
/lib/modules 中有个4.19.0(内核库文件)
/boot全部文件(启动的核心档案和内核映像)
常见问题
arch/x86/entry/syscall_64.o:(.rodata+0x1120):对‘sys_zwhsyscall’未定义的引用
这是由于系统调用按其他调用的格式来写,加个__x64_
虚拟机运行
我们在复制好了文件后执行:
sudo update-grub2
我们编写测试样例:
#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 p = 0;
int n = 0;
int *prio;
int *nice;
prio = &p;
nice = &n;
printf("请输入pid:\n");
scanf("%d",&pid);
printf("请输入nice:\n");
scanf("%d",&nicevalue);
printf("请输入flag:\n");
scanf("%d",&flag);
syscall(336,pid,flag,nicevalue,prio,nice);
printf("现在的nice为%d\n,prio为%d\n",n,p);
return 0;
}
测试
我先打开火狐浏览器,用命令查看所有进程:
ps -el
或者正则匹配一下和火狐有关的进程ps -el | grep fire
- F 代表这个程序的旗标 (flag), 4 代表使用者为 superuser;
- S 代表这个程序的状态 (STAT);
- UID 代表执行者身份
- PID 进程的ID号!
- PPID 父进程的ID;
- C CPU使用的资源百分比
- PRI指进程的执行优先权(Priority的简写),其值越小越早被执行;
- NI 这个进程的nice值,其表示进程可被执行的优先级的修正数值。
- ADDR 这个是内核函数,指出该程序在内存的那个部分。如果是个执行 的程序,一般就是『 - 』
- SZ 使用掉的内存大小;
- WCHAN 目前这个程序是否正在运作当中,若为 - 表示正在运作;
- TTY 登入者的终端机位置;
- TIME 使用掉的 CPU 时间。
- CMD 所下达的指令名称
找到火狐的pid,图中为XXX,用以下命令可以查到进程详细信息,如nice值。
top -p pid
我们编写测试用例:
#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 p = 0;
int n = 0;
int *prio;
int *nice;
prio = &p;
nice = &n;
printf("请输入pid:\n");
scanf("%d",&pid);
printf("请输入nice:\n");
scanf("%d",&nicevalue);
printf("请输入flag:\n");
scanf("%d",&flag);
syscall(334,pid,flag,nicevalue,prio,nice);
printf("现在的nice为%d\n,prio为%d\n",n,p);
return 0;
}
- nice值的范围?
PRI指进程的执行优先权(Priority的简写),其值越小越早被执行
进程的nice值表示进程可被执行的优先级的修正数值。
如果nice过大或过小,都会被系统降到最大合法值或提高到最小合法值。
nice范围从-20~19,其中-20最高,19最低,只有系统管理者可以设置负数的等级。
当我们更改nice值时,系统不会对错误的nice值做响应。