linux系统学习之实践--虚拟多个cpu核心

应用背景:

        目前android系统的游戏朝着重型手游方法发展,部分apk游戏对硬件配置要求极高,甚至为求极致的用户体验不惜限制低配置手机用户的使用,当游戏检测到硬件配置不符合最低要求时直接闪退.  apk应用检测手机硬件配置主要还是检测cpu核心数,内存大小,显存大小等信息.

        android模拟器采用virtualbox作为虚拟器在windows等平台上运行android镜像, 而在主机端cpu大部分是4核的,为了实现多开模拟器(每开一个模拟器至少需要1个cpu核),将每个开启的virtualbox模拟器设置为单核(实际上virtualbox多核存在bug,即多核情况下vboxheadless这个进程存在高cpu占用率的问题). 而当模拟器设置为一核时,部分重型游戏检测配置为单核直接闪退或者采用低配置运行,影响模拟器兼容性和用户体验.

        综上: 采用设置虚拟机单核来避免virtualbox的多核bug; 模拟器内部采用虚拟多个cpu核心的方法来反上层apk对cpu核心数的检测.


实现原理:

        android系统采用linux内核,而上层apk检查cpu个数主要采用如下两个方法:

1>  扫描/sys/devices/system/cpu目录下cpuX文件夹的个数,每一个cpuX代表一个cpu,如cpu0 cpu1等文件夹

2>  读取/sys/devices/system/cpu/online文件值,此值代表目前激活的可用cpu的个数.经过实践发现,主要还是通过查看此属性值来最终确定cpu的核心数.

       为了实现虚拟cpu核心数,我们需要虚拟出包含cpu0的四个cpuX文件夹,即cpu0-cpu3,并且修改/sys/devices/system/cpu/online文件的值为0-3. linux启动至少保证一个cpu在运行,所以cpu0在设置单核启动的时候就真实存在.实际要虚拟cpu1-cpu3这三个文件夹.


具体步骤:

        linux在启动初始化阶段会对cpu的硬件信息进行初始化,主要通过sysfs文件系统提交给上层应用接口,如应用程序可以直接通过'cat  /sys/devices/system/cpu/online'命令查看当前cpu个数. 这里问题就转为了修改sysfs文件系统建立cpu信息的函数了.

        通过逆向或者对sysfs属性或者对cpu信息初始化流程熟悉的同学就幸福了,比较容易找到设置/sys/devices/system/cpu/online文件的值和虚拟cpuX文件夹.这里就不卖关子,马上给各位客观呈上我的处理方法.


        在呈现实际修改内容的通过还需要说下cpu信息初始化的大概流程,以便大家深入看. 大致过程如下,如果有啥理解错误的地方,还请大家拍砖:

1>  init/main.c: start_kernel()-->boot_cpu_init();此函数初始化cpu0,不作改动.

2>  init/main.c: start_kernel()-->rest_init()--> kernel_init-->do_basic_setup()-->driver_init()-->cpu_dev_init()

这一系列的调用最终调用到cpu_dev_init(),此函数在drivers/base/cpu.c文件中. 在此函数中可以hotplug启动未启动的其他cpu,我们就在这个函数中来虚拟cpu1-cpu3文件夹

3>  显示/sys/devices/system/cpu/online的值的函数为show_cpus_attr, 如下,online,present,possible三个文件属性的值都是通过此函数来显示的,实际上也需要present,possible和值不比online小,所以这里假设都设置为0-3,表示有4个cpu核存在:

##################BEGIN#######################

#define _CPU_ATTR(name, map) \
{ __ATTR(name, 0444, show_cpus_attr, NULL), map }

/* Keep in sync with cpu_subsys_attrs */
static struct cpu_attr cpu_attrs[] = {
    _CPU_ATTR(online, &cpu_online_mask),
    _CPU_ATTR(possible, &cpu_possible_mask),
    _CPU_ATTR(present, &cpu_present_mask),
};

###################END######################


实际修改如下:        (改动出标记为红色)

show_cpus_attr函数:

##################BEGIN#######################

static ssize_t show_cpus_attr(struct device *dev,
                  struct device_attribute *attr,
                  char *buf)
{
    struct cpu_attr *ca = container_of(attr, struct cpu_attr, attr);
    int n = cpulist_scnprintf(buf, PAGE_SIZE-2, *(ca->map));

/* 新增编译宏控制编译,写死直接设置online值为0-3 */
#ifdef FAKE_CPUS_CORES
        sprintf(buf, "0-3\n");
        n = 4;
#else

        buf[n++] = '\n';
        buf[n] = '\0';
#endif
    return n;
}

###################END######################

cpu_dev_init()函数改动:

##################BEGIN#######################

void __init cpu_dev_init(void)
{
    if (subsys_system_register(&cpu_subsys, cpu_root_attr_groups))
        panic("Failed to register CPU subsystem");

    cpu_dev_register_generic();

#if defined(CONFIG_SCHED_MC) || defined(CONFIG_SCHED_SMT)
    sched_create_sysfs_power_savings_entries(cpu_subsys.dev_root);
#endif

/* 如下为增加部分 */
#ifdef FAKE_CPUS_CORES    /*编译宏控制 */
    int i;
    int cpu_real_count = 0;
     for_each_possible_cpu(i) {        /*此循环查看已经存在的实际物理核心数 */
            cpu_real_count++;
     }

/*此函数把需要模拟的cpu文件夹全部链接到cpu0. register_cpu_fake函数根据register_cpu函数改造而来*/
  register_cpu_fake(&per_cpu(cpu_devices_fake, 0), 0,cpu_real_count);
#endif


}

##################END#######################

新增的register_cpu_fake函数:

#################BEGIN########################

#ifdef FAKE_CPUS_CORES      /* 编译宏控制 */
static DEFINE_PER_CPU(struct cpu, cpu_devices_fake);


/* register_cpu_fake有函数register_cpu函数改造而来,此函数标红部分为相对register_cpu函数的新增 */
int __cpuinit register_cpu_fake(struct cpu *cpu, int num, int  cpu_real_count)
{
    int error =0;

    cpu->node_id = cpu_to_node(num);
    memset(&cpu->dev, 0x00, sizeof(struct device));
    cpu->dev.id = num;
    cpu->dev.bus = &cpu_subsys;
    cpu->dev.release = cpu_device_release;
#ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE
    cpu->dev.bus->uevent = arch_cpu_uevent;
#endif

    error = device_register(&cpu->dev);
    if (!error && cpu->hotpluggable)
        register_cpu_control(cpu);
    if (!error)
        per_cpu(cpu_sys_devices, num) = &cpu->dev;
    if (!error)
        register_cpu_under_node(num, cpu_to_node(num));

#ifdef CONFIG_KEXEC
    if (!error)
        error = device_create_file(&cpu->dev, &dev_attr_crash_notes);
#endif

/* 此处代码建立cpuX到cpu0的链接,单核时建立cpu1-cpu3到cpu0的链接,
*双核存在cpu0和cpu1,所以只建立cpu2,cpu3到cpu0的链接
*sysfs_create_link(&cpu_subsys.dev_root->kobj, &cpu->dev.kobj, buf);函数建立sysfs文件系统链接
*/

    int cpu_num = cpu_real_count;

    if(cpu_num < 4){
        for(;cpu_num < 4;cpu_num++){
            char buf[16];
            sprintf(buf,"cpu%d",cpu_num);
            sysfs_create_link(&cpu_subsys.dev_root->kobj, &cpu->dev.kobj, buf);
        }
    }

    return error;
}
#endif

##################END#######################


        经过上路改动,重新编译内核后启动,直接进入/sys/devices/system/cpu目录,cat online值发现时0-3,且有cpu0-cpu3四个文件夹,说明成功修改. andorid系统可以运行安兔兔来检测,检测得到的结果是4核. 至此,虚拟多个cpu内核个数的实践就完满了.


  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值