编写Linux内核程序查看优先级对程序运行的影响

实验描述

Linux的程序运行优先级nice分为了-20~19共40个等级,其使用了完全公平调度算法(CFS)来进行调度,但公平并不意味着每个等级所分到的CPU时间一致,由此来探究nice和CPU运行时间的关系。本次实验共写了一个具有不同优先级的多线程(双线程)程序和一个内核模块来查看程序两个进程的分别CPU虚拟运行时间和实际运行时间。

实验程序

具有不同优先级的双线程程序

程序描述

此程序只是一个简单的多线程程序,用sched_setaffinity()来对进程优先级赋值,最后执行一个while()循环使其一直运行以便比较二者的CPU实际运行时间。

重难点解析

线程与CPU绑定

因为要比较出CPU对不同等级优先级的执行情况,故不能允许其多核执行,否则若是分别在两个核执行则影响实验数据的准确。
核心方法

int sched_setaffinity(pid_t pid, unsigned int cpusetsize, cpu_set_t *mask);

其中pid代表想要为其绑定CPU的进程的进程号,为0则是当前进程。cpu_set_t是一个记录CPU集合的结构。

cpu_set_t cpuSet;
CPU_ZERO(&cpuSet);  
CPU_SET(0,&cpuSet);

sched_setaffinity(0,sizeof(cpuSet),&cpuSet);

首先定义一个CPU集合cpuSet,然后用CPU_ZERO将其清零,此时cpuSet这个集合中无CPU,再用CPU_SET将0号CPU装进cpuSet中,最后既可以点用方法绑定CPU了。

设置优先级

int setpriority(int which,int who, int prio);

戳这里了解更多此方法应用

完整代码

#include <unistd.h>
#include <stdio.h>
#ifndef __USE_GNU
#define __USE_GNU
#endif
#include <sched.h>
#include <pthread.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <errno.h>
#include <sys/time.h>


int main(){

    int i;
    pid_t pid;

    cpu_set_t cpuSet;
    CPU_ZERO(&cpuSet);
    CPU_SET(0,&cpuSet);

    //将程序绑定到0号CPU
    sched_setaffinity(0,sizeof(cpuSet),&cpuSet);

    pid=fork();
    if(pid==0){
        //设置子进程优先级-20
        setpriority(PRIO_PROCESS,0,-20);
        printf("Child: %d -20 \n",getpid());

    }else{
        //设置父进程优先级19
        setpriority(PRIO_PROCESS,0,19);
        printf("Parent: %d 19 \n",getpid());
    }

    while(1);
}

查看CPU时间的内核模块

模块描述

编写此内核模块旨在把进程实际运行时间和虚拟运行时间输出到内核缓冲区里,随后我们可以进行查看作出比较

重难点解析

遍历进程链表

调用for_each_process宏循环控制语句来遍历进程链表

for_each_process(p)

p是task_struct,Linux中用于存储pcd的结构体的指针

内核命令

//用于加载内核模块
sudo insmod filename.ko
//用于移除内核模块
sudo rmmod filename.ko
//用于打印内核缓冲区内容
dmesg

完整代码

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>

/**
 * 模块加载时执行的方法
 */
int ty_init(void){
    struct task_struct *p; //task_struct是linux存pcb的结构体
    /**
     * 对cpu中执行的进程进行遍历
     */
     for_each_process(p){
            printk("pid: %d Vtime: %lld Rtime: %lld \n",p->pid,
                p->se.vruntime,p->se.sum_exec_runtime);
     }
    return 0;
}

/**
 * 模块注销时执行,通常不使用
 */
void ty_exit(void){
    printk(KERN_INFO "Removing Module");
}


/**
 * 注册上述方法,不然无法使用
 */
module_init(ty_init);
module_exit(ty_exit);

MakeFile

内核模块并不能简单的直接编译得到,需要编写MakeFile文件后调用make命令得到filename.ko

MakeFile内容模板

obj-m += kernelmodfilename.o
all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

实验步骤

编译运行具有不同优先级的双线程程序

程序执行后打印出父子进程的进程号并开始执行死循环

加载内核

在程序运行后再打开一个任务窗口加载内核,然后用dmesg打印出所有正在执行的进程的虚拟、实际时间,找到刚刚打印出来的两个子进程的进程号所对应的时间即可。

展开阅读全文

没有更多推荐了,返回首页