zoukankan      html  css  js  c++  java
  • 具体分析contrex-A9的汇编代码__switch_to(进程切换)

    //函数原型:版本号linux-3.0.8

    struct task_struct *__switch_to(structtask_struct *, struct thread_info *, struct thread_info *);

     

    #define switch_to(prev,next,last)                                      

    do {                                                                   

            last =__switch_to(prev,task_thread_info(prev), task_thread_info(next));       

    } while (0)

    //首先我们看一下以下的宏:

    #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE*)0)->MEMBER)

    //以下事实上就是指向相应的struct成员

    /*

    CC_STACKPROTECT补丁是Tejun Heo在年给主线kernel提交的一个用来防止内核堆栈溢出的补丁。

    默认的config是将这个选项关闭的,能够在编译内核的时候。改动.config文件为CONFIG_CC_STACKPROTECTOR=y

    来启用。未来飞天内核能够将这个选项开启来防止利用内核stack溢出的day攻击。这个补丁的防

    溢出原理是:在进程启动的时候,在每一个buffer的后面放置一个预先设置好的stack canary。你

    能够把它理解成一个哨兵,当buffer发生缓冲区溢出的时候。肯定会破坏stack canary的值,当

    stack canary的值被破坏的时候。内核就会直接当机。

    那么是怎么推断stack canary被覆盖了呢?

    事实上这个事情是gcc来做的,内核在编译的时候给gcc加了个-fstack-protector參数.

    */

    DEFINE(TSK_STACK_CANARY,     offsetof(struct task_struct,stack_canary));

    //task_struct

    DEFINE(TI_TASK,              offsetof(struct thread_info, task));

    //

    /*

     * Domain types

     */

    /*

    #define DOMAIN_NOACCESS 0

    #define DOMAIN_CLIENT  1//是用户的域(运行程序,訪问数据),以及由所述接入加以防护

                            //个别章节和页面组成域的权限。

    #ifdef CONFIG_CPU_USE_DOMAINS

    #define DOMAIN_MANAGER 3//控制域的行为(当前域的sections和page。以及域訪问)。

    #else

    #define DOMAIN_MANAGER 1

    #endif

    */

    //相应图

    //这个domain通过协处理器设置寄存器DomainAccess Control

    DEFINE(TI_CPU_DOMAIN,        offsetof(struct thread_info,cpu_domain));

    /*

    struct cpu_context_save {

            __u32   r4;

            __u32   r5;

            __u32   r6;

            __u32   r7;

            __u32   r8;

            __u32   r9;

            __u32   sl;

            __u32   fp;

            __u32   sp;

            __u32   pc;

            __u32   extra[2];               /* Xscale 'acc' register, etc */

    };

    */

    DEFINE(TI_CPU_SAVE,          offsetof(struct thread_info,cpu_context));

     

    /*

    在以下有个set_tls,相应我的平台set_tls_v6k

            .macroset_tls_v6k, tp, tmp1, tmp2

            mcr     p15, 0, p, c13, c0, 3         @ set TLS register

            .endm

     

    tp_value就是为了设置TLS register的值

    在多线程应用程序。当中一个进程共享同样的地址空间中的全部线程。还有常常出现须要维护的数据是唯一

    的一个线程。TLS或线程本地存储。由于你或许能够从它的名字如今弄清楚。是用于线程抽象的概念。它是

    一种高速和有效的方式来存储每一个线程的本地数据。

    线程的本地数据的偏移量是通过TLS寄存器(H / W或S

    / W块),它指向线程各自的线程控制块訪问。

    之前ARM内核。甚至ARM9和ARM11核心的一些不具备这样的TLS注冊物理上可用。

    操作系统(Linux从这里開始)

    须要效仿的软件。新一代的ARM内核。Cortex-AX起,确实有这TLS的寄存器可用(CP15)。

     

    内核对TLS须要做的事情是可以让用户态程序(一般是nptl——一个pthread的实现)在某个时刻可以设置

    线程唯一的基址值到内核的线程信息结构内。

    */

    DEFINE(TI_TP_VALUE,          offsetof(struct thread_info, tp_value));

     

    /*

     * These are the reasoncodes for the thread notifier.

     */

    #define THREAD_NOTIFY_FLUSH    0

    #define THREAD_NOTIFY_EXIT     1

    #define THREAD_NOTIFY_SWITCH   2

    #define THREAD_NOTIFY_COPY     3

    /*
     * Register switch for ARMv3 and ARMv4 processors
     * r0 = previous task_struct, r1 = previous thread_info, r2 = next thread_info
     * previous and next are guaranteed not to be the same.
     */
    ENTRY(__switch_to)
     UNWIND(.fnstart        )
     UNWIND(.cantunwind     )
            //ip就是上一个线程的thread_info里面的cpu_context的地址
            add     ip, r1, #TI_CPU_SAVE
    		//r3里面存着下一个线程tp值
            ldr     r3, [r2, #TI_TP_VALUE]
     //存储r4 - sl, fp, sp, lr到thread_info->cpu_context里。

    分别使用arm和thumb实现 //这就是保存现场。 ARM( stmia ip!, {r4 - sl, fp, sp, lr} ) @ Store most regs on stack THUMB( stmia ip!, {r4 - sl, fp} ) @ Store most regs on stack THUMB( str sp, [ip], #4 ) THUMB( str lr, [ip], #4 ) #ifdef CONFIG_CPU_USE_DOMAINS //r6存着下一个线程的DOMAIN属性 ldr r6, [r2, #TI_CPU_DOMAIN] #endif //set_tls 上面已分析 set_tls r3, r4, r5 #if defined(CONFIG_CC_STACKPROTECTOR) && !defined(CONFIG_SMP) ldr r7, [r2, #TI_TASK]//下一个线程的task_struct ldr r8, =__stack_chk_guard//r8里面是__stack_chk_guard地址 ldr r7, [r7, #TSK_STACK_CANARY]//到这里。r7里面是stack_canary值 #endif #ifdef CONFIG_CPU_USE_DOMAINS //设置domain寄存器。 mcr p15, 0, r6, c3, c0, 0 @ Set domain register #endif //r5里面是上一个线程的task_struct mov r5, r0 //r4就是下一个线程的thread_info里面的cpu_context的地址 add r4, r2, #TI_CPU_SAVE //r4 r5仅仅是暂时保存一下 //以下的thread_notify_head通知链,以下样例说明 ldr r0, =thread_notify_head mov r1, #THREAD_NOTIFY_SWITCH bl atomic_notifier_call_chain #if defined(CONFIG_CC_STACKPROTECTOR) && !defined(CONFIG_SMP) str r7, [r8]//__stack_chk_guard = (next)threadinfo->task->stack_canary #endif THUMB( mov ip, r4 )//ip指向线程的thread_info里面的cpu_context的地址 mov r0, r5//r0从新指向上一个线程的task_struct //以下相应了上面的保存现场,这里就是恢复现场。

    pc相应了下个进程的cpu_context->pc //从上面看到这个cpu_context->pc就是之前保存现场的lr,就是下个线程要运行的地方。 ARM( ldmia r4, {r4 - sl, fp, sp, pc} ) @ Load all regs saved previously THUMB( ldmia ip!, {r4 - sl, fp} ) @ Load all regs saved previously THUMB( ldr sp, [ip], #4 ) THUMB( ldr pc, [ip] ) UNWIND(.fnend ) ENDPROC(__switch_to)

    实验代码:

    #include <linux/kernel.h>
    #include <linux/notifier.h>
    #include <linux/module.h>
    #include <asm/thread_notify.h>
    
    MODULE_LICENSE("GPL");
    
    static int test_event(struct notifier_block *this, unsigned long event, void *ptr)
    {
        printk(KERN_INFO "In Event: Event Number is %ld
    ",event);
    
        return NOTIFY_DONE;
    }
    
    static struct notifier_block test_notifier =
    {
        .notifier_call = test_event,
    };
    
    static int __init reg_notifier(void)
    {
        int err = 0;
        printk(KERN_INFO "Begin to register:
    ");
    
        err = thread_register_notifier(&test_notifier);
        if (err)
        {
            printk(KERN_ERR "register test_notifier error
    ");
    
            goto fail1;
        }
    
        printk(KERN_INFO "register reboot_notifier completed
    ");
    
        return 0;
    
    fail1:
        return err;
    }
    
    
    static void __exit unreg_notifier(void)
    {
        thread_unregister_notifier(&test_notifier);
    
        printk(KERN_INFO "Unregister finished
    ");
    }
    
    module_init(reg_notifier);
    module_exit(unreg_notifier);
    

    打印:

    2都是THREAD_NOTIFY_SWITCH,当然会不断的切换!


  • 相关阅读:
    spring cloud config 属性加解密
    IntelliJ IDEA 快捷键
    SQL Server 2012 安装图解教程(附sql2012下载地址)
    spring cloud 启动报错-must be declared as an @AliasFor [serviceId], not [name].
    MySQL主从复制配置
    恢复MySQL数据库删除的数据
    java.lang.IllegalStateException: No instances available for localhost
    蜘蛛牌 (DFS)
    加油站问题 (优先队列)
    堆的操作的复杂度
  • 原文地址:https://www.cnblogs.com/gcczhongduan/p/5071882.html
Copyright © 2011-2022 走看看