具體分析contrex-A9的彙編代碼__switch_to(進程切換)

//函數原型:版本號linux-3.0.8linux

struct task_struct *__switch_to(structtask_struct *, struct thread_info *, struct thread_info *);多線程

 

#define switch_to(prev,next,last)                                       \dom

do {                                                                   \函數

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

} while (0)atom

//首先咱們看一下如下的宏:spa

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE*)0)->MEMBER)操作系統

//如下事實上就是指向相應的struct成員線程

/*code

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, \tp, 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\n",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:\n");

    err = thread_register_notifier(&test_notifier);
    if (err)
    {
        printk(KERN_ERR "register test_notifier error\n");

        goto fail1;
    }

    printk(KERN_INFO "register reboot_notifier completed\n");

    return 0;

fail1:
    return err;
}


static void __exit unreg_notifier(void)
{
    thread_unregister_notifier(&test_notifier);

    printk(KERN_INFO "Unregister finished\n");
}

module_init(reg_notifier);
module_exit(unreg_notifier);

打印:

2都是THREAD_NOTIFY_SWITCH,固然會不斷的切換!

相關文章
相關標籤/搜索