android4.0 input子系统分析(kernel部分)

本文详细剖析了Android 4.0系统中Input子系统的内核层面实现,涵盖其工作原理、驱动架构以及与用户空间的交互过程,为理解Android设备输入事件处理提供了关键洞察。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、前言
   前面我们分析了android的input子系统的android部分的代码,下面我们继续来分析kernel部分的,对于这个系统kernel部分和标准linux差别不大,
   google在原linux基础上增加了一些代码以使其更适合手持设备,比如支持多点触控设备,支持android特有的4个按键等等。我们会一步一步的分析
   内核代码,来分析出input系统的工作原理。
二、input设备类的注册
    在input系统android部分我们知道了,在android层的eventhub中,建立了一个epoll,来监控/dev/input目录下有效的input设备所有事件,如果有
    事件输入的话就会读出当前事件,然后交给android上层处理。所使用的方法也是 open(),read(),close()的标准方法。因此我们要分析这个input
    系统的话,要从/dev/input下的设备文件分的实现析其,他的实现代码在android\kernel\drivers\input\input.c文件中,input也是内核的一个模块
    我们从模块的初始化看起subsys_initcall(input_init);

    static int __init input_init(void)
    {
        int err;
    
        err = class_register(&input_class);    //注册input设备类--------
        if (err) {                                                  |
            pr_err("unable to register input_dev class\n");         |
            return err;                                             |    
        }                                                           V
        /****************************************************************
        struct class input_class = {
            .name        = "input",
            .devnode    = input_devnode,
        };
        ********************************************************************/
        err = input_proc_init();    //建立input类proc文件系统   见下面
        if (err)
            goto fail1;
    
        err = register_chrdev(INPUT_MAJOR, "input", &input_fops);//注册字符设备驱动,此中包含了设备的具体操作函数 见下面
        if (err) {
            pr_err("unable to register char major %d", INPUT_MAJOR);
            goto fail2;
        }
    
        return 0;
    
     fail2:    input_proc_exit();
     fail1:    class_unregister(&input_class);
        return err;
    }

1.proc文件系统建立

   我们首先对proc文件系统做一个介绍:/proc 文件系统是一个特殊的软件创建的文件系统, 内核用来输出消息到外界./proc 下的每个
   文件都绑到一个内核函数上, 当文件被读的时候即时产生文件内容. 我们已经见到一些这样的文件起作用; 例如, /proc/modules, 常
   常返回当前已加载的模块列表./proc 在 Linux 系统中非常多地应用. 很多现代 Linux 发布中的工具, 例如ps, top, 以及 uptime, 
   从 /proc 中获取它们的信息. 一些设备驱动也通过/proc 输出信息, 你的也可以这样做. /proc 文件系统是动态的, 因此你的模块
   可以在任何时候添加或去除条目.-------Linux 设备驱动 第三版
   input_proc_init()代码在android\kernel\drivers\input\input.c中

  
    static int __init input_proc_init(void)
    {
        struct proc_dir_entry *entry;
    
        proc_bus_input_dir = proc_mkdir("bus/input", NULL);//为input类创建/proc/bus/input目录
        if (!proc_bus_input_dir)        |
            return -ENOMEM;             V
    /*********************************************************************************
    proc_mkdir()函数定义在android\kernel_imx\fs\proc\generic.c中,这个函数调用
    调用proc_mkdir_mode(name, S_IRUGO | S_IXUGO, parent);
    这个函数定义也在这个文件中
    struct proc_dir_entry *proc_mkdir_mode(const char *name, mode_t mode,
    struct proc_dir_entry *parent)
       {
           struct proc_dir_entry *ent;
       
           ent = __proc_create(&parent, name, S_IFDIR | mode, 2);
           if (ent) {
               if (proc_register(parent, ent) < 0) {
                   kfree(ent);
                   ent = NULL;
               }
           }
           return ent;
       }
    函数调用__proc_create()定义在本文件中
        
    static struct proc_dir_entry *__proc_create(struct proc_dir_entry **parent,
                          const char *name,
                          mode_t mode,
                          nlink_t nlink)
    {
        ..................
        if (xlate_proc_name(name, parent, &fn) != 0)//将name整理成/proc/name
            goto out;
    ...................
        //分配内存
        ent = kmalloc(sizeof(struct proc_dir_entry) + len + 1, GFP_KERNEL);
        if (!ent) goto out;
        //初始化这个这个proc子系统
        memset(ent, 0, sizeof(struct proc_dir_entry));
        memcpy(((char *) ent) + sizeof(struct proc_dir_entry), fn, len + 1);
        ent->name = ((char *) ent) + sizeof(*ent);
        ent->namelen = len;
        ent->mode = mode;
        ent->nlink = nlink;
        atomic_set(&ent->count, 1);
        ent->pde_users = 0;
        spin_lock_init(&ent->pde_unload_lock);
        ent->pde_unload_completion = NULL;
        INIT_LIST_HEAD(&ent->pde_openers);//得到队列回首地址
     out:
        return ent;
    }
    到了这里我们就创建好了这个proc文件系统的子目录,下面继续创建设备文件
    **************************************************************************************/
        entry = proc_create("devices", 0, proc_bus_input_dir,   //创建devices这个设备文件
                    &input_devices_fileops);
        if (!entry)
            goto fail1;
    
        entry = proc_create("handlers", 0, proc_bus_input_dir,//创建handlers这个设备文件
                    &input_handlers_fileops);
        if (!entry)
            goto fail2;
    
        return 0;
    
     fail2:    remove_proc_entry("devices", proc_bus_input_dir);
     fail1: remove_proc_entry("bus/input", NULL);
        return -ENOMEM;
    }
    
    这里创建了两个proc文件

a)  我们先看entry = proc_create("devices", 0, proc_bus_input_dir,&input_devices_fil
3、uifirst的优化⼿段 3.1 ⼿段A-运⾏队列任务的优先选取 uifirst的优化对象基本都是cfs线程,这类线程有⼀个特点,就是选择⼀个cpu后,先是放到cpu对应的运⾏队列上,然后等待被调度到,因此会出现调度时延的问题。如下: 图片: https://odocs.myoas.com/uploader/f/J6Sz2Prolvglwu6p.png?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjE3NDE4NTEyOTQsImZpbGVHVUlEIjoicVZ0eXRqdHJ2OGtXM3ZqUSIsImlhdCI6MTc0MTg1MDY5NCwiaXNzIjoidXBsb2FkZXJfYWNjZXNzX3Jlc291cmNlIiwidXNlcklkIjozMjU4NjV9.348PeUOiyT8aGuz2nynlM1IcuJU_BqdGipzxCzN-yFg 3.1.1 uifirst 3.0 - 链表pick uifirst 3.0,在每个cpu的运⾏队列中,增加成员 list_head *ux_thread_list,凡是挂⼊运⾏队列的ux线程,都会加⼊这个链表。在每⼀次调度器选择到下⼀个待执⾏的任务A时,我们都会从ux_thread_list中挑出ux线程,替代掉任务A。关键调⽤: pick_ux_1 thread(rq, &p, &se); 3.1.2 uifirst 4.0 - 虚拟补偿 uifirst 4.0,基本上只使⽤虚拟补偿的⽅式。cfs线程挂⼊cpu的运⾏队列时,会加⼊红⿊树中,每个线程拥有虚拟时间的概念,⽤来决定线程挂在红⿊树中的位置,⽽原⽣的cfs调度器,优先选择红⿊树最左端的节点,作为下⼀个执⾏的线程。利⽤这⼀点,在唤醒时(从sleep、blocked状态返回),有机会对ux线程,进⾏更多的虚拟时间补偿,使ux线程在⼀定时间内,基本保证在红⿊树最左端。关键调⽤: place_entity_adjust_ux_task(cfs_rq, se, initial); 在打开组调度的版本(kernel-5.10 及以后)上,恢复到了链表的方式。原因是因为组调度(CONFIG_FAIR_GROUP_SCHED)的组的概念会使得虚拟补偿被弱化。 3.2 ⼿段B-锁竞争队列的优先排序6 uifirst对于ux线程的锁竞争⽅⾯,有两点调度优化,⽬前包含mutex、rwsem、futex: (1)将持锁的owner动态提升为ux,释放锁后将取消ux身份。关键调⽤: mutex_set_inherit_ux(lock, current); mutex_unset_inherit_ux(lock, current); (2)ux线程第⼀时间拿不到锁,挂⼊等待链表时,插⼊到其它等锁线程的前⾯。关键调⽤: mutex_list_add(current, &waiter->list, list, lock); PS:由于⾕歌GKI及ART修改限制,futex将在coloros 11.3上取消,理论上讲,从根源减少锁竞争才是正道! 3.3 ⼿段C-选择⾼算⼒CPU uifirst对ux线程(主要是重载类型),在选核时更倾向于选择⼤核。关键调⽤: set_ux_task_to_prefer_cpu(p, &best_energy_cpu); slide_set_task_cpu(p, &best_energy_cpu); PS:选择⼤核会对功耗造成影响,因此需要指定场景,⽬前主要是启动场景、滑动场景。 3.4 ⼿段D-适时提频 滑动期间,对于ux线程,会采⽤更激进的提频⽅式,在input boost⽅案中实现。关键调⽤: flag |= (SCHED_CPUFREQ_WALT | SCHED_1 CPUFREQ_BOOST);
最新发布
03-14
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值