linux驱动源码阅读之情景分析法实践指南

1. 情景分析法必须确立一个情景,此情景满足以下条件:


#    a.满足自己的学习目标
#   b.该情景内的代码,应该只能被执行1次.譬如,分析usb进行数据传输的过程. 那么在执行该情景前,应该将
#   所以的USB设备拔出, 或者将它们对应的驱动取消执行.
#   再譬如, 要分析kmalloc()的执行过程, 那么我应该在该kmalloc第一次被执行处,加上一句while(1), 防止后续又
#   有kmalloc()被执行.
#   c.若该情景需要用户应用, 譬如应用获取按键事件等. 所要执行的应用程序应尽量得到其源码, 或者自行编写或搜索相关例程.
#   拥有源码后, 就可以将不必要的代码删除, 减少关联驱动的阅读.

2. 情景分析法实践流程:


#   a. 由上至下的代码简化. 由上至下,即由应用程序=>驱动框架=>硬件驱动.
#   应用程序的一句ioctl(), 可能涉及驱动的几百个函数的执行. 若是我们能先尽量地简化应用程序,或驱动框架,会大大地
#   减少代码阅读量.
#   b. 批量添加函数名打印. 根据打印, 可以一一对被执行的函数进行简化. 具体添加打印方法,见
#   https://blog.csdn.net/weixin_45154862/article/details/127192739
#   c. 集合所有被简化的函数, 进行代码分析.

3. 细节说明之 函数简化:


#   去掉判断语句,循环语句, 宏定义的语句, 总之去掉所以需要我们进行分析判断, 耗费脑力的代码.
#   而最终简化的结果是, 我们可以无需依靠打印,开发板的运行输出, 便可以靠阅读代码,知道代码的执行流程.

#   函数简化的范围: 我们分析的是驱动代码, 其他非该驱动框架或驱动的代码无需简化分析. 譬如kmalloc.
#   一般,范围会限制在该驱动框架或驱动所在文件夹内.

#  z. 直接删除函数
#  在对某个函数进行简化前, 我们可以试着将该函数删除, 会有30%的概率,情景依旧正常执行!
#  这样的结果是喜闻乐见的,因为一个函数,可能会嵌套几十个函数, 那么我们就可以少简化几十个函数,
#  少分析几十个函数. 大大地减少工作量.
#  因此简化前,请先执行此步骤.

#   a. 保留副本
#   需要简化的函数, 应保留其副本(函数原文件处,复制一份,并重命令), 方便代码分析时回看.
#   而我们的简化工作就直接在原函数进行.

#   b. 独立函数的执行.
#   若某函数被执行了多次, 且每次的执行, 执行流程的不同.可以将该函数的代码复制多份, 并重命名,
#   以独立每次函数的执行. 这样,简化工作就在其函数副本内执行, 这区别于步骤a. 每一个函数的副本,
#   若想它们被执行, 需要在头文件添加声明, 将会增加工作量. 这也就是步骤a直接修改原函数,而非副本函数的理由.
#   譬如:
#   处理前:
    test();
    test();
    test();
#    处理后:
    test1();
    test2();
    test3();

#   c. 去掉所有的判断语句的分支, 仅保留被执行的代码,及其判断条件.
#   譬如:
#    简化前:
    void test(int a,int b)
    {
        int c;

        if(a>9) {
            xxxx; /* 被执行 */
        } else if (b<9) {
            xxxx;
        }
    }
#   简化后
    void test(int a,int b)
    {
        int c;

        if(a>9) {
            xxxx; /* 被执行 */
        }
    }

#   d. 去掉循环
#   譬如:
#   简化前
    i=0;
    while(i<get_count()){
        set_index(i);
        i++;
    }
#   简化后
    /* get_count()的返回值为3 */
    i=0;
    set_index(i);
    i=1;
    set_index(i);
    i=2;
    set_index(i);
#   这时, set_index()函数被执行了多次.可以使用b步骤进行处理.

#   e. 去掉宏定义
#   譬如:
#   简化前:
#   xxx.h
#define TEST(a,b) a>b:3?4
    xxx.c
    TEST(5,6);
#简化后:
    xxx.c
    5>6:3?4
    
#  f. 删除指针赋值语句
#  太多的指针变量, 使得结构体间的关系错综复杂.但其实许多的指针,在当前情景内,是不被使用的,
#  将它们删除, 可以减少代码分析时的工作量. 不仅是指针, 含有队列的入队,出队等语句, 也可删除的
#  都尽量删除.

4. 细节说明之 代码分析

# a. 集合所有简化后的,需要被执行的, 该驱动相关的, 函数到一个文件内. 依据函数的嵌套关系,将函数源码补充完全.
#     主要目的是方便分析, 和进行代码标注.
#     譬如:
    int test(void)
    {
        get_index();
        void get_index(void)
        {
            set_type(c);
            void set_type(struct type a)
            {
                g_type = a;
            }
        }
    }

# b. 结构体成员关系标注
#   使用<tagx> 指示 某一操作的对象,以及操作过程.
#   对象或成员处注有<tagx>, 操作处也注有 <tagx>, 可以实现跳转,通过手动搜索<tagx>.
#    譬如, 这里的<tagx> 可以记录该成员在何处被分配内存, 或被赋值.
    struct machine {
        struct tv b;
    };
    struct machine a;
        -> struct tv *b; <tag3>

    int machine_type_create(struct machine *mac)
    {
        mac->b = kmalloc(); <tag3>
    }
#    <point to objx> 意为此指针指向objx, <objx> 在objx 处有标注.
#   譬如一下, <point to obj6> 表明b成员指向sony_tv. 而<tag5> 标注此赋值是在何处进行的.
    strct machine a;
        ->struc tv *b; <point to obj6> <tag5>

    struct tv sony_tv; <obj6>

    int get_tv(struct machine *mac)
    {
        mac->b = &sony_tv; <tag5>
    }
#    <add to listx> 意为将此list_head加入 listx, <listx> 在链表头处有标注.


#https://gitee.com/suiren/usb2hdmi/blob/master/drm_read.c
#drm_read.c一文,是完全使用本文的代码阅读方法, 而获得的驱动阅读总结, 也可作为参考.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值