精华内容
下载资源
问答
  • MHA 在线切换过程

    千次阅读 2015-04-22 07:58:47
    MySQL MHA 在线切换是MHA除了自动监控...该方式提供快速切换和优雅的阻塞写入,无关关闭原有服务器,整个切换过程在0.5-2s 的时间左右,大大减少了停机时间。本文演示了MHA 在线切换并给出了在线切换的基本步骤......

        MySQL MHA 在线切换是MHA除了自动监控切换换提供的另外一种方式,多用于诸如硬件升级,MySQL数据库迁移等等。该方式提供快速切换和优雅的阻塞写入,无关关闭原有服务器,整个切换过程在0.5-2s 的时间左右,大大减少了停机时间。本文演示了MHA 在线切换并给出了在线切换的基本步骤。

     

    1、MHA在线切换方式及要求
        $ masterha_master_switch --master_state=alive --conf=/etc/app1.cnf --new_master_host=host2
      
        a、SQL threads on all slaves are running
        b、Seconds_Behind_Master on all slaves are less or equal than --running_updates_limit seconds
        c、On master, none of update queries take more than --running_updates_limit seconds in the show processlist output
      

    2、在线进行切换基本步骤
        a、检测MHA配置置及确认当前master
        b、决定新的master
        c、阻塞写入到当前master
        d、等待所有从服务器与现有master完成同步
        e、在新master授予写权限,以及并行切换从库
        f、重置原master为新master的slave

     

    3、演示在线切换
    ###获取masterha_master_switch帮助
    [root@vdbsrv4 ~]# masterha_master_switch --help
    Usage:
        # For master failover

        masterha_master_switch --master_state=dead
        --global_conf=/etc/masterha_default.cnf
        --conf=/usr/local/masterha/conf/app1.cnf --dead_master_host=host1

        # For online master switch

        masterha_master_switch --master_state=alive
        --global_conf=/etc/masterha_default.cnf
        --conf=/usr/local/masterha/conf/app1.cnf

        See online reference
        (
    http://code.google.com/p/mysql-master-ha/wiki/masterha_master_switch)
        for details.

    ###校验当前是否启用masterha_manager
    [root@vdbsrv4 ~]# masterha_check_status --conf=/etc/masterha/app1.cnf
    app1 is stopped(2:NOT_RUNNING).

    ###切换前
    mysql> show slave hosts;
    +-----------+---------+------+-----------+--------------------------------------+
    | Server_id | Host    | Port | Master_id | Slave_UUID                           |
    +-----------+---------+------+-----------+--------------------------------------+
    |      1001 | vdbsrv2 | 3306 |         1 | 75bef614-e342-11e4-921d-000c295fb2eb |
    |      1002 | vdbsrv3 | 3306 |         1 | 091f79b8-e386-11e4-93d5-000c2943c830 |
    +-----------+---------+------+-----------+--------------------------------------+

    ###实施在线切换
    [root@vdbsrv4 ~]# masterha_master_switch --conf=/etc/masterha/app1.cnf --master_state=alive --new_master_host=vdbsrv3 \
    > --orig_master_is_new_slave --running_updates_limit=10000 --interactive=0
    Tue Apr 21 15:42:13 2015 - [info] MHA::MasterRotate version 0.56.
    Tue Apr 21 15:42:13 2015 - [info] Starting online master switch..
    Tue Apr 21 15:42:13 2015 - [info]
    Tue Apr 21 15:42:13 2015 - [info] * Phase 1: Configuration Check Phase..
    Tue Apr 21 15:42:13 2015 - [info]
    Tue Apr 21 15:42:13 2015 - [warning] Global configuration file /etc/masterha_default.cnf not found. Skipping.
    Tue Apr 21 15:42:13 2015 - [info] Reading application default configuration from /etc/masterha/app1.cnf..
    Tue Apr 21 15:42:13 2015 - [info] Reading server configuration from /etc/masterha/app1.cnf..
    Tue Apr 21 15:42:13 2015 - [info] GTID failover mode = 0
    Tue Apr 21 15:42:13 2015 - [info] Current Alive Master: vdbsrv1(192.168.1.6:3306)
    Tue Apr 21 15:42:13 2015 - [info] Alive Slaves:
    Tue Apr 21 15:42:13 2015 - [info]   vdbsrv2(192.168.1.7:3306)  Version=5.6.22-log (oldest major version between slaves) log-bin:enabled
    Tue Apr 21 15:42:13 2015 - [info]     Replicating from 192.168.1.6(192.168.1.6:3306)
    Tue Apr 21 15:42:13 2015 - [info]   vdbsrv3(192.168.1.8:3306)  Version=5.6.22-log (oldest major version between slaves) log-bin:enabled
    Tue Apr 21 15:42:13 2015 - [info]     Replicating from 192.168.1.6(192.168.1.6:3306)
    Tue Apr 21 15:42:13 2015 - [info] Executing FLUSH NO_WRITE_TO_BINLOG TABLES. This may take long time..
    Tue Apr 21 15:42:13 2015 - [info]  ok.
    Tue Apr 21 15:42:13 2015 - [info] Checking MHA is not monitoring or doing failover..
    Tue Apr 21 15:42:13 2015 - [info] Checking replication health on vdbsrv2..   ###Author : Leshami
    Tue Apr 21 15:42:13 2015 - [info]  ok.                                                             ###Blog    : http://blog.csdn.net/leshami
    Tue Apr 21 15:42:13 2015 - [info] Checking replication health on vdbsrv3..
    Tue Apr 21 15:42:13 2015 - [info]  ok.
    Tue Apr 21 15:42:13 2015 - [info] vdbsrv3 can be new master.
    Tue Apr 21 15:42:13 2015 - [info]
    From:
    vdbsrv1(192.168.1.6:3306) (current master)
     +--vdbsrv2(192.168.1.7:3306)
     +--vdbsrv3(192.168.1.8:3306)

    To:
    vdbsrv3(192.168.1.8:3306) (new master)
     +--vdbsrv2(192.168.1.7:3306)
     +--vdbsrv1(192.168.1.6:3306)
    Tue Apr 21 15:42:13 2015 - [info] Checking whether vdbsrv3(192.168.1.8:3306) is ok for the new master..
    Tue Apr 21 15:42:13 2015 - [info]  ok.
    Tue Apr 21 15:42:13 2015 - [info] vdbsrv1(192.168.1.6:3306): SHOW SLAVE STATUS returned empty result. To check replication filtering rules,
      temporarily executing CHANGE MASTER to a dummy host.
    Tue Apr 21 15:42:13 2015 - [info] vdbsrv1(192.168.1.6:3306): Resetting slave pointing to the dummy host.
    Tue Apr 21 15:42:13 2015 - [info] ** Phase 1: Configuration Check Phase completed.
    Tue Apr 21 15:42:13 2015 - [info]
    Tue Apr 21 15:42:13 2015 - [info] * Phase 2: Rejecting updates Phase..
    Tue Apr 21 15:42:13 2015 - [info] ###建议部署master_ip_online_change_script 脚本,该脚本会自动阻塞以及kill原master session,置原master为只读
    Tue Apr 21 15:42:13 2015 - [warning] master_ip_online_change_script is not defined. Skipping disabling writes on the current master.#此演示未指定
    Tue Apr 21 15:42:13 2015 - [info] Locking all tables on the orig master to reject updates from everybody (including root):
    Tue Apr 21 15:42:13 2015 - [info] Executing FLUSH TABLES WITH READ LOCK..
    Tue Apr 21 15:42:13 2015 - [info]  ok.
    Tue Apr 21 15:42:13 2015 - [info] Orig master binlog:pos is mysql-bin.000023:651371104.
    Tue Apr 21 15:42:13 2015 - [info]  Waiting to execute all relay logs on vdbsrv3(192.168.1.8:3306)..
    Tue Apr 21 15:42:13 2015 - [info]  master_pos_wait(mysql-bin.000023:651371104) completed on vdbsrv3(192.168.1.8:3306). Executed 0 events.
    Tue Apr 21 15:42:13 2015 - [info]   done.
    Tue Apr 21 15:42:13 2015 - [info] Getting new master's binlog name and position..
    Tue Apr 21 15:42:13 2015 - [info]  mysql-bin.000016:301477519
    Tue Apr 21 15:42:13 2015 - [info]  All other slaves should start replication from here. Statement should be:
        CHANGE MASTER TO MASTER_HOST='vdbsrv3 or 192.168.1.8', MASTER_PORT=3306, MASTER_LOG_FILE='mysql-bin.000016',
        MASTER_LOG_POS=301477519, MASTER_USER='repl', MASTER_PASSWORD='xxx';
    Tue Apr 21 15:42:13 2015 - [info]
    Tue Apr 21 15:42:13 2015 - [info] * Switching slaves in parallel..
    Tue Apr 21 15:42:13 2015 - [info]
    Tue Apr 21 15:42:13 2015 - [info] -- Slave switch on host vdbsrv2(192.168.1.7:3306) started, pid: 13099
    Tue Apr 21 15:42:13 2015 - [info]
    Tue Apr 21 15:42:13 2015 - [info] Log messages from vdbsrv2 ...
    Tue Apr 21 15:42:13 2015 - [info]
    Tue Apr 21 15:42:13 2015 - [info]  Waiting to execute all relay logs on vdbsrv2(192.168.1.7:3306)..
    Tue Apr 21 15:42:13 2015 - [info]  master_pos_wait(mysql-bin.000023:651371104) completed on vdbsrv2(192.168.1.7:3306). Executed 0 events.
    Tue Apr 21 15:42:13 2015 - [info]   done.
    Tue Apr 21 15:42:13 2015 - [info]  Resetting slave vdbsrv2(192.168.1.7:3306) and starting replication from the new master vdbsrv3(192.168.1.8:3306)..
    Tue Apr 21 15:42:13 2015 - [info]  Executed CHANGE MASTER.
    Tue Apr 21 15:42:13 2015 - [info]  Slave started.
    Tue Apr 21 15:42:13 2015 - [info] End of log messages from vdbsrv2 ...
    Tue Apr 21 15:42:13 2015 - [info]
    Tue Apr 21 15:42:13 2015 - [info] -- Slave switch on host vdbsrv2(192.168.1.7:3306) succeeded.
    Tue Apr 21 15:42:13 2015 - [info] Unlocking all tables on the orig master:
    Tue Apr 21 15:42:13 2015 - [info] Executing UNLOCK TABLES..
    Tue Apr 21 15:42:13 2015 - [info]  ok.
    Tue Apr 21 15:42:13 2015 - [info] Starting orig master as a new slave..
    Tue Apr 21 15:42:13 2015 - [info]  Resetting slave vdbsrv1(192.168.1.6:3306) and starting replication from the new master vdbsrv3(192.168.1.8:3306)..
    Tue Apr 21 15:42:13 2015 - [info]  Executed CHANGE MASTER.
    Tue Apr 21 15:42:13 2015 - [info]  Slave started.
    Tue Apr 21 15:42:13 2015 - [info] All new slave servers switched successfully.
    Tue Apr 21 15:42:13 2015 - [info]
    Tue Apr 21 15:42:13 2015 - [info] * Phase 5: New master cleanup phase..
    Tue Apr 21 15:42:13 2015 - [info]
    Tue Apr 21 15:42:13 2015 - [info]  vdbsrv3: Resetting slave info succeeded.
    Tue Apr 21 15:42:13 2015 - [info] Switching master to vdbsrv3(192.168.1.8:3306) completed successfully.

    ###切换结果
    mysql> show slave hosts;
    +-----------+---------+------+-----------+--------------------------------------+
    | Server_id | Host    | Port | Master_id | Slave_UUID                           |
    +-----------+---------+------+-----------+--------------------------------------+
    |         1 | vdbsrv1 | 3306 |      1002 | f2824060-e2cb-11e4-8f18-000c2926f457 |
    |      1001 | vdbsrv2 | 3306 |      1002 | 75bef614-e342-11e4-921d-000c295fb2eb |
    +-----------+---------+------+-----------+--------------------------------------+

    展开全文
  • 进程切换过程之一:上下文切换 注:下面给出的源码均出自https://github.com/mit-pdos/xv6-public 在进程进行切换时,需要进行上下文切换,也就是寄存器的值的保存。 对于上下文的切换,有两种情况:硬件自动...

    进程切换过程之一:上下文切换

    注:下面给出的源码均出自https://github.com/mit-pdos/xv6-public

     

     

    在进程进行切换时,需要进行上下文切换,也就是寄存器的值的保存。

    对于上下文的切换,有两种情况:硬件自动(隐式)完成、操作系统显式保存。

    下面分析两段xv6的源代码,都是上下文切换部分,版本有所不同:

    注:每个进程都有一个用来维护过程调用的栈,有的书把这个栈叫做内核栈;在编译原理中,这个栈的术语叫活动记录,在下文中,提到的栈都是说的这个保存活动记录的栈。每个进程都有自己的活动记录(当然,如果是多CPU的情况下,就会引入线程的概念,则每个线程有一个活动记录)

    较老的版本

    #   void swtch(struct context *old, struct context *new);
    #  
    # Save current register context in old
    # and then load register context from new.
    
    .globl swtch
    swtch:
      # Save old registers
      movl 4(%esp), %eax
      popl 0(%eax)  # %eip
      movl %esp, 4(%eax)
      movl %ebx, 8(%eax)
      movl %ecx, 12(%eax)
      movl %edx, 16(%eax)
      movl %esi, 20(%eax)
      movl %edi, 24(%eax)
      movl %ebp, 28(%eax)
    
      # Load new registers
      movl 4(%esp), %eax  # not 8(%esp) - popped return address above
    
      movl 28(%eax), %ebp
      movl 24(%eax), %edi
      movl 20(%eax), %esi
      movl 16(%eax), %edx
      movl 12(%eax), %ecx
      movl 8(%eax), %ebx
      movl 4(%eax), %esp
      pushl 0(%eax)  # %eip   
    

    这个版本的上下文结构struct context的内容如下:

    struct context {
        int eip;    //程序计数器
        int esp;    //栈指针
        int ebp;
        int ecx;
        int edx;
        int esi;
        int edi;
        int ebp;
    };
    

    分析一下上下文切换的源代码:

    源代码的上半段是将“老”的进程的上下文保存,下半段将“新”的进程(要切换到的)的上下文恢复。

    执行swtch的指令时,栈的状态:

                                                             

    movl 4(%esp), %eax
    

    将old(参数)的值放入寄存器%eax。

    popl 0(%eax)
    

    然后将栈顶的值保存到0(%eax)的位置。也就是将返回地址(%eip)的值保存到old指向的context空间中

    之后,就是将其他的寄存器放入old指向的context空间中。

    通过上面的源码,可以得到context在内存中分配空间的方式:

                                          

    下面的代码就是从new指向的context空间中,将各个寄存器的值恢复,就不详细说明了。

    较新的版本:就像一开始提到的,硬件会自动保存一部分

    硬件会保存一部分的寄存器,还有剩余的需要进行手动保存。

    # Context switch
    #
    #   void swtch(struct context **old, struct context *new);
    # 
    # Save the current registers on the stack, creating
    # a struct context, and save its address in *old.
    # Switch stacks to new and pop previously-saved registers.
    
    .globl swtch
    swtch:
      movl 4(%esp), %eax
      movl 8(%esp), %edx
    
      # Save old callee-saved registers
      pushl %ebp
      pushl %ebx
      pushl %esi
      pushl %edi
    
      # Switch stacks
      movl %esp, (%eax)
      movl %edx, %esp
    
      # Load new callee-saved registers
      popl %edi
      popl %esi
      popl %ebx
      popl %ebp
      ret
    

    开始的两条代码,将old与new放入%eax和%edx,方便后面通过指针的方式访问内存。

    接下来的四条压栈指令,直接将要手动保存的寄存器压入栈中。

    执行到这里,“老”进程和“新”进程对应的栈的示意图如下:

                                                  

    与之前旧版本的有些许不同,新版本的直接将上下文保存在进程自己的栈中;旧版本的保存在了其他的地方(暂时就理解到这个程度,由于没有分析过所有的源码,这里的分析可能比较片面,但这是我整个的分析过程,如有有大佬看出哪里不会,请指正)

    展开全文
  • FreeRTOS原理剖析:任务切换过程

    千次阅读 2019-10-04 16:35:49
    讲解任务切换的基本概念和PendSV任务切换过程,也分析了任务切换的两种方法。

    1. 任务切换相关API函数

    函数 描述
    xPortPendSVHandler() PendSV中断服务函数,其实函数原型为PendSV_Handler()
    vTaskSwitchContext() 检查任务堆栈使用是否溢出,和查找下一个优先级高的任务,如果使能运行时间统计功能,会计算任务运行时间

    2. 任务切换的基本知识

    在FreeRTOS任务管理中,最主要的目的就是找到就绪态优先级最高的任务,然后执行任务切换,从而能保持优先级最高的任务一直占用CPU资源。为了达到最优性能,任务切换部分程序使用汇编代码编写。

    FreeRTOS有两种方法触发任务切换:

    • 系统节拍时钟中断(SysTick定时器)。切换过程参考《FreeRTOS原理剖析:系统节拍时钟分析》
    • 执行系统调用代码。普通任务使用taskYIELD()强制任务切换;中断服务程序使用portYIELD_FROM_ISR()强制任务切换;在应用程序里也可以通过设置xYieldPending的值来通知调度器进行任务切换。

    其中:

    // 对于普通任务时
    #define taskYIELD()				portYIELD()
    #define portYIELD_WITHIN_API 	portYIELD
    
    // 对于中断服务程序时
    #define portEND_SWITCHING_ISR( xSwitchRequired ) if( xSwitchRequired != pdFALSE ) portYIELD()
    #define portYIELD_FROM_ISR( x ) portEND_SWITCHING_ISR( x )
    

    可以看出,最终执行的代码段为:

    #define portYIELD()											\
    {															\
    	/* 														\
    	 * 通过向中断控制及状态寄存器ICSR的第28位写入1,触发PendSV中断	\
    	 * 地址为0xE000 ED04 									\
    	 */														\
    	portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;			\
    															\
    	/* dsb和isb 完成数据同步隔离和指令同步隔离					\
    	 * 保证之前存储器访问操作和指令都执行完						\
    	 */														\
    	__dsb( portSY_FULL_READ_WRITE );						\
    	__isb( portSY_FULL_READ_WRITE );						\
    }
    

    通过向中断控制及状态寄存器ICSR(地址:0xE000 ED04)的第28位写入1,触发PendSV中断,从而执行任务切换。

    3. 任务切换过程

    3.1 PendSV中断服务函数分析

    在FreeRTOS中,有:

    #define xPortPendSVHandler 	PendSV_Handler
    

    源代码如下:

    __asm void xPortPendSVHandler( void )
    {
    	extern uxCriticalNesting;
    	extern pxCurrentTCB;		/* 永远会指向当前激活的任务 */
    	extern vTaskSwitchContext;
    
    	PRESERVE8
    
    	mrs r0, psp					/* 读取进程栈指针PSP,保存在R0中,此时SP的值为MSP */
    	isb							/* 指令同步隔离 */
    
    	/* 这两句使R2中保存当前激活的任务TCB首地址 */
    	ldr	r3, =pxCurrentTCB		/* 将pxCurrentTCB储存的地址保存到R3,注意的是pxCurrentTCB的储存地址是固定不变的,但指向是可变的 */
    	ldr	r2, [r3]				/* 将R3地址所处数据保存在R2,即TCB的首地址 */
    
    	/* 
    	 * 前两句判断是否使能了FPU,如果使能了,则手动将s16~s31压入栈中
    	 * 其中s0~s15和FPSCR硬件自动完成 
    	 */
    	tst r14, #0x10
    	it eq
    	vstmdbeq r0!, {s16-s31}
    
    	/* 将当前激活任务的寄存器值入栈,并更新R0,另外硬件自动将xPSR、PC、LR、R12、R0~R3入栈 */
    	stmdb r0!, {r4-r11, r14}	
    
    	/* R0为PSP地址,R2为激活任务的TCB地址,R0的值写入R2所保存的地址去,即TCB第一个成员指向线程堆栈指针,在每次任务切换最后都会更新PSP */
    	str r0, [r2]				
    
    	stmdb sp!, {r3}				/* 将R3临时压入堆栈,R3保存了pxCurrentTCB地址,函数调用后会用到,因此要入栈保护 */
    
    	/* 关中断,中断优先级号大于等于configMAX_SYSCALL_INTERRUPT_PRIORITY的中断都会被屏蔽 */
    	mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY	
    	msr basepri, r0				
    	
    	dsb							/* 数据同步隔离 */
    	isb							/* 指令同步隔离 */
    	
    	bl vTaskSwitchContext		/* 切换到vTaskSwitchContext,查找下一个任务 */
    
    	/* 开中断 */	
    	mov r0, #0	
    	msr basepri, r0				
    	
    	ldmia sp!, {r3}				/* 恢复R3,R3保存了pxCurrentTCB的地址,这里pxCurrentTCB的地址固定,但指向改变了 */
    
    	/* 当前激活的TCB栈顶值存入R0 */
    	ldr r1, [r3]				/* 将pxCurrentTCB指向的地址赋值给R1,即将TCB的首地址赋值给R1 */
    	ldr r0, [r1]				/* 将R1地址所处的数据赋值给R0,即当前激活任务TCB的第一项的值赋给R0 */
    
    	ldmia r0!, {r4-r11, r14}	/* 将寄存器R4~R11出栈,并同时更新R0的值 */
    
    
    	/* 
    	 * 前两句判断是否使能了FPU,如果使能了,手动恢复s16-s31浮点寄存器
    	 * 其中s0~s15和FPSCR硬件自动完成 
    	 */
    	tst r14, #0x10
    	it eq
    	vldmiaeq r0!, {s16-s31}
    
    	msr psp, r0					/* 将最新的任务堆栈栈顶赋值给线程堆栈指针PSP */
    	isb							/* 指令同步隔离,清流水线 */
    	
    	#ifdef WORKAROUND_PMU_CM001
    		#if WORKAROUND_PMU_CM001 == 1
    			push { r14 }
    			pop { pc }
    			nop
    		#endif
    	#endif
    	
    	bx r14	/* 当调用 bx r14指令退出中断,堆栈指针PSP指向了新任务堆栈的正确位置 */
    
    }
    

    说明:

    • 在Cortex-M处理器中有两个栈指针,一个是主栈指针(Main Stack Pointer,即MSP),它可用于线程模式,在中断模式下只能用MSP;另一个是进程堆栈指针(Processor Stack Pointer,即PSP),PSP总是用于线程模式。在任何时刻只能使用到其中一个。
    • 复位后处于线程模式特权级,默认使用MSP。在FreeRTOS中,MSP用于OS内核和异常处理,PSP用于应用任务。
    • 通过设置CONTROL寄存器的bit[1]选择使用哪个堆栈指针。CONTROL[1]=0选择主堆栈指针;CONTROL[1]=1选择进程堆栈指针。

    3.2 函数vTaskSwitchContext()

    该函数会更新当前任务运行时间,检查任务堆栈使用是否溢出,然后调用宏 taskSELECT_HIGHEST_PRIORITY_TASK()获取更高优先级的任务。

    函数源代码如下:

    void vTaskSwitchContext( void )
    {
    	/* 如果任务调度器已经挂起 */
    	if( uxSchedulerSuspended != ( UBaseType_t ) pdFALSE )
    	{
    		xYieldPending = pdTRUE;		/* 标记任务调度器挂起,不允许任务切换 */
    	}
    	else
    	{
    		xYieldPending = pdFALSE;	/* 标记任务调度器没有挂起 */
    		
    		traceTASK_SWITCHED_OUT();
    
    		/* 
    		 * 如果启用运行时间统计功能,设置configGENERATE_RUN_TIME_STATS为1
    		 * 如果使用了该功能,要提供以下两个宏:
    		 * portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()
    		 * portGET_RUN_TIME_COUNTER_VALUE()
    		 */
    		#if ( configGENERATE_RUN_TIME_STATS == 1 )
    		{
    				#ifdef portALT_GET_RUN_TIME_COUNTER_VALUE
    					portALT_GET_RUN_TIME_COUNTER_VALUE( ulTotalRunTime );
    				#else
    					ulTotalRunTime = portGET_RUN_TIME_COUNTER_VALUE();
    				#endif
    
    				/*  
    				 * ulTotalRunTime记录系统的总运行时间,ulTaskSwitchedInTime记录任务切换的时间
    				 * 如果系统节拍周期为1ms,则ulTotalRunTime要497天后才会溢出
    				 * ulTotalRunTime < ulTaskSwitchedInTime表示可能溢出
    				 */
    				if( ulTotalRunTime > ulTaskSwitchedInTime )
    				{
    					/* 记录当前任务的运行时间 */
    					pxCurrentTCB->ulRunTimeCounter += ( ulTotalRunTime - ulTaskSwitchedInTime );
    				}
    				else
    				{
    					mtCOVERAGE_TEST_MARKER();
    				}
    
    				/* 更新ulTaskSwitchedInTime,下个任务时间从这个值开始 */
    				ulTaskSwitchedInTime = ulTotalRunTime;	
    		}
    		#endif /* configGENERATE_RUN_TIME_STATS */
    
    		/* 核查堆栈是否溢出 */
    		taskCHECK_FOR_STACK_OVERFLOW();
    
    		/* 寻找更高优先级的任务 */
    		taskSELECT_HIGHEST_PRIORITY_TASK();
    		
    		traceTASK_SWITCHED_IN();
    
    		/* 如果使用Newlib运行库,你的操作系统资源不够,而不得不选择newlib,就必须打开该宏 */
    		#if ( configUSE_NEWLIB_REENTRANT == 1 )
    		{
    			_impure_ptr = &( pxCurrentTCB->xNewLib_reent );
    		}
    		#endif /* configUSE_NEWLIB_REENTRANT */
    	}
    }
    

    3.2 寻找下一个任务的方式

    PendSV中会调用vTaskSwitchContext(),最后调用函数taskSELECT_HIGHEST_PRIORITY_TASK()寻找优先级最高的任务。

    对于FreeRTOS的调度器,它有两种方式寻找下一个最高优先级的任务,分别为特殊方式和常用方式,在FreeRTOSConfig.h中可通过宏定义设置,如下:

    /* 0:使用常用方式来选择下一个要运行的任务;1:使用特殊方法来选择下一个要运行的任务 */
    #define configUSE_PORT_OPTIMISED_TASK_SELECTION	1
    

    在FreeRTOS中,通用方法不依赖某些硬件等限制,适用于多种MCU中。特殊方式是使用了某些硬件的特性,只针对部分MCU而使用。

    3.2.1 常用方法

    uxTopReadyPriority 记录就绪态中最高优先级值,创建任务时会更新值,有任务添加到就绪表时也会更新值。这种方法对任务的数量无限制。

    #define taskSELECT_HIGHEST_PRIORITY_TASK()												\
    {																						\
    	UBaseType_t uxTopPriority = uxTopReadyPriority;										\
    																						\
    	while( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxTopPriority ] ) ) )				\
    	{																					\
    		configASSERT( uxTopPriority );													\
    		--uxTopPriority;																\
    	}																					\
    																						\
    	/* 获取优先级最高任务的任务控制块 */														\
    	listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) );\
    	
    	uxTopReadyPriority = uxTopPriority;	 												\
    }
    

    3.2.2 特殊方式

    使用此方法,uxTopReadyPriority 每个bit位表示一个优先级,bit0表示优先级0,bit31表示优先级31,使用此方式优先级最大只能是32个。

    #define taskSELECT_HIGHEST_PRIORITY_TASK()														\
    {																								\
    	UBaseType_t uxTopPriority;																		\
    																								\
    	/* 获取优先级最高的任务 */								\
    	portGET_HIGHEST_PRIORITY( uxTopPriority, uxTopReadyPriority );								\
    	configASSERT( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ uxTopPriority ] ) ) > 0 );		\
    	
    	/* 获取优先级最高任务的任务控制块 */
    	listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) );		\
    } 
    
    

    其中:

    #define portGET_HIGHEST_PRIORITY( uxTopPriority, uxReadyPriorities ) uxTopPriority = ( 31UL - ( uint32_t ) __clz( ( uxReadyPriorities ) ) )
    

    __clz( ( uxReadyPriorities ) 是计算uxReadyPriorities 的前导零个数,如:
    二进制0001 1010 0101 1111的前导零个数为3,可以知道,最高优先级uxTopPriority 等于 31减去前导零个数。
    知道最高优先级的优先级,则通过listGET_OWNER_OF_NEXT_ENTRY()对应最高优先级的列表项,将pxCurrentTCB指向对应的控制块。


    参考资料:

    【1】: 正点原子:《STM32F407 FreeRTOS开发手册V1.1》
    【2】: 野火:《FreeRTOS 内核实现与应用开发实战指南》
    【3】: 《Cortex M3权威指南(中文)》

    展开全文
  • 进程切换过程详解

    2015-04-03 21:24:00
    进程切换过程详解 /* 注:在学习内核的时候有一个困难,那就是任何一个模块都不是孤立的,比如进程的调度就设计到中断处理、信号处理还有进程上下文的切换等等。作为一个初学者,想一下子把操作系统...

    /*

    注:在学习内核的时候有一个困难,那就是任何一个模块都不是孤立的,比如进程的调度就设计到中断处理、信号处理还有进程上下文的切换等等。作为一个初学者,想一下子把操作系统的整个运行过程都清晰地展现在脑海是不现实的。尽管如此,每个模块还是有它所关注的焦点,我们所采取的策略是把整个操作系统分为几个大模块,比如:进程的管理、内存管理、文件系统等等。然后把这些大模块进一步分解成一个个小模块,比如进程的管理可以细分为进程的创建、进程的切换、系统调用的处理、信号的处理等等。在分析每一个模块时,先把其他的模块抽象化,千万不要陷入其他模块的细节当中,,也可以说这是一种各个击破的方法,当你把每个小模块的功能搞清楚后,到最后整个操作系统的运行过程就很清晰了!

    */

     

     

     

    在上一篇博客中,我们提到当一个任务从系统调用处理函数返回之前会检查是否需要进行进程切换。那么什么时候会发生进程的切换呢(任务调度)?当系统发生硬件中断、系统调用或者时钟中断时,就有可能发生进程的切换。 下面我们以时钟中断为例来看看进程的切换是如何进行的。

    在此之前,我们先要做一个说明,由于我们并没有开始介绍进程的详细知识,对进程的详细介绍将放在进程的创建这一篇博客中(还没开始写O(∩_∩)O~),因此在这里我们先对进程做一个粗略的抽象:一个任务(就是进程)含有代码段、数据段、堆栈段,还有一个任务状态段TSS。这个任务状态段TSS记录当前任务的所有状态信息,包括寄存器、系统参数等等。TSS段的描述符放在TR寄存器中(也就是说访问TR就能访问当前任务的TSS段了)。

    假设此刻CPU正在执行进程1,我们知道:系统有一个时钟频率,每隔一段时间就会发生一次时钟中断,这个时间段我们称为一个滴答。假设经过了一个滴答,系统发生时钟中断,此时时钟中断处理程序就会被自动调用(timer_interrupt),timer_interrupt定义在kernel/System_call.s中,如下图所示:

    同我们上一篇讲的_system_call一样,它首先会执行一些保护现场的工作,接着在第189行代码中把_jiffies的值加1(_jiffies表示自系统启动以来经过的滴答数),接下来第192-194的代码将执行此次时钟中断的特权级CPL压入堆栈,用来作为后面do_timer的参数,接下来开始执行do_timer,do_timer函数定义在Kernel/Sched.c中,这个函数的主要作用是将当前进程的用户态执行时间或内核态执行时间加1,然后将当前进程的剩余时间片减1.

    如果当前进程的时间片还有剩余,那么直接return返回继续执行,接下来判断当前任务的CPL是否是0,如果是0,说明当前任务是在内核态被中断的,而Linux0.11中内核态是不能被抢占的,所以直接返回执行,如果不是0,则执行进程调度程序schedule()。接下来我们来分析schedule()这个函数,schedule函数同样定义在Sched.c中,它里面包含下面两段代码:

    这段代码的作用是:遍历任务数组,检查它们的报警定时值,如果该值小于jiffies,说明该任务的alarm时间已经过了,那么就在它的信号位图中置SIGALRM信号,表示向任务发送SIGALARM信号,然后将alarm清零,接下来检查是不是还有别的未被阻塞的信号,如果有并且当前的进程状态是可以被打断的,那么把这个任务置为就绪态。

    第124-142行的代码重新遍历整个任务数组,找出任务状态处于TASK_RUNING并且时间片最长的那个任务。并调用swith_to()函数切换到那个任务。swith_to函数定义在include/Linux/Sched.h中。

    switch_to是一段汇编代码,下面来解释一下这段代码的含义:首先检查要切换的任务是不是当前任务,如果是则直接退出。接下来把任务n(要切换去的任务)的TSS段放到_tmp.b中,然后把任务n放入_current中,把当前任务放入%ecx中切换出来,然后执行一个长跳转到*&_tmp的位置(这是新任务的TSS地址处),此时CPU会把所有寄存器的内容保存到当前任务TR执行的TSS段中,然后把新任务的TSS段中的寄存器信息恢复到CPU的各个寄存器中,这样系统就正式开始执行新的任务了。第178-180的代码是判断原任务是否使用过协处理器,如果没有则直接结束。

     

    posted on 2015-04-03 21:24 愚&道 阅读(...) 评论(...) 编辑 收藏

    转载于:https://www.cnblogs.com/yudao/p/4388575.html

    展开全文
  • 关于vue单选和多选切换过程中的坑 问题:vue单选多选切换时,会报错, children must be keyed: 原因是切换过程中错误栈都在Vue内部 解决办法:select标签加v-if判断,multiple属性加条件multiple,watch监控...
  • 任务切换过程

    2012-04-17 14:25:50
    任务切换过程如下: 1.保存当前任务状态到当前的TSS中。 2.装载新任务的段选择子到任务寄存器。 3.通过GDT中的段描述符访问新的TSS。 4.将新TSS中新任务的的状态装载到通用寄存器,段寄存器,LDTR,CR3,EIP中。...
  • 进程与线程-3、内核级线程切换过程

    千次阅读 2017-09-04 17:14:46
    内核级线程切换过程
  • 进程的切换过程

    千次阅读 2019-10-17 11:03:20
    切换方式 进程的切换,实质上就是被中断运行进程与待运行进程的上下文切换。从主观上来理解。只分为两步: 1.切换新的页表,然后使用新的虚拟地址空间 2.切换内核栈,加入新的内容(PCB控制块,资源相关),实现上下文...
  • 用户级线程切换过程
  • Linux中进程调度与切换过程

    千次阅读 2017-04-10 09:54:29
     今天我们要学习的是Linux中进程调度与切换过程。有关进程的知识在前面的博客中已经提到了,有不懂的地方请参考我前面的博客,今天我直接从进程调度和切换开始讲。  Linux一个较大的优势就是进程调度,因为
  • ART运行时Foreground GC和Background GC切换过程分析

    万次阅读 热门讨论 2015-05-04 00:57:34
    通过前面一系列文章的学习,我们知道了ART运行时既支持Mark-Sweep GC,又支持Compacting GC。其中,Mark-Sweep GC执行效率更高,但是存在内存碎片问题;...本文就详细分析它们的执行过程以及切换过程
  • 操作系统用户态和内核态之间的切换过程 1. 用户态和内核态的概念区别 究竟什么是用户态,什么是内核态,这两个基本概念以前一直理解得不是很清楚,根本原因个人觉得是在于因为大部分时候我们在写程序时关注的...
  • 目录1、ARM Core的扩展 : 增加SCR.NS bit位2、ELx级别的切换过程 1、ARM Core的扩展 : 增加SCR.NS bit位 在ARMv8中,增加了SCR_EL3寄存器 只有SCR_EL3,没有SCR_EL1和SCR_EL2 只有EL3等级下才能读写此寄存器. NS ...
  • 分析 ARM 处理器不同工作模式的作用,举例说明切换过程。 ARM 处理器(以 ARM7TDMI 为例)的 7 中工作模式为: (1) 用户模式(usr):运行应用程序的普通模式。 (2) 系统模式(sys):运行具有特权的操作系统...
  • Linux进程上下文切换过程context_switch详解 日期 内核版本 架构 作者 GitHub CSDN 2016-06-14 Linux-4.6 X86 & arm gatieme LinuxDeviceDrivers Linux进程管理与调度 前面我们了解了linux进程调度器的...
  • 进程切换过程分析

    千次阅读 2013-06-27 14:21:59
    这种行为被称为进程切换,任务切换或上下文切换。下面几节描述在Linux中进行进程切换的主要内容。 硬件上下文 尽管每个进程可以拥有属于自己的地址空间,但所有进程必须共享CPU寄存器。因此要恢复一个...
  • 基于动态信令的BWP切换,是指基于DCI进行BWP切换,此外,该DCI同时对切换后的"新BWP“的数据信道进行资源分配。当使用DCI进行BWP切换时,如果”新BWP“同时配置了type 0和type 1频域资源分配,当原BWP小于新BWP的...
  • LTE小区切换过程

    千次阅读 2014-05-07 21:11:36
    TODO 。。。
  • android 语言切换过程分析

    万次阅读 2014-02-27 18:13:04
    最近在看一个bug,系统切换语言后,本来退到后台的音乐,会在通知栏上显示通知。为了解决这个bug,我学习了下android的语言切换流程,也参考了大量其他人的资料。 在这里我将自己的探索记录下来,作为自己的学习记录...
  • 停掉slave1 服务器 查看master1服务器信息 查看master2服务器的信息,其他服务器都是一样的信息,只是有一条记录而已。 **重新连上slave1 ** 查看master1服务器信息,同步成功 ...将master1 6379服务器停掉 ...
  • ocm学习中发现vip的切换是有个过程的, 期间的ifconfig 会发现该开始仅仅本机ip,vip是没有绑定到eth0,刚启动居然会vip1,2都绑定到了本地eth0,最后才仅剩本地的vip 期间的crs_stat的 刚才始都是offline。启动...
  • linux 进程调度切换过程分析

    千次阅读 2017-12-23 19:13:47
    //体系结构体相关的切换,目前此函数什么都没做 #define arch_start_context_switch(prev) do {} while (0) 2878 2879 if (!mm) { //如果next是内核线程,则使用prev所使用的地址空间 2880 next->active_mm = ...
  • 内容: (1):从schedule()开始,几种不同类型的进程之间的调度选择;在相同类型的进程之间的调度选择算法 ...(4):地址空间发生切换,解释地址空间的切换不会影响后续切换代码的执行 (5):current宏
  • 在进程从用户态到内核态切换过程中,Linux主要做的事: 1:读取tr寄存器,访问TSS段 2:从TSS段中的sp0获取进程内核栈的栈顶指针 3: 由控制单元在内核栈中保存当前eflags,cs,ss,eip,esp寄存器的值。   4:由...
  • 在相同类型的进程之间的调度选择算法 (2):从CPU的IP值的变化上,说明在switch_to宏执行后,执行分析 (3):堆栈发生切换位置,在切换堆栈前后,current_thread_info变化 (4):地址空间发生切换,解释地址空间...
  • 虚拟控制台的切换过程

    千次阅读 2009-08-21 13:42:00
    1.虚拟终端的切换在控制台软中断中运行,当按"ALT F1" 时,键盘中断设置变量want_console为0,然后激发控制台软中断(console_softint),如果请求的控制台存在并且不等于当前控制台,这时调用change_console(want_...
  • 初学Linux中进程调度与进程切换过程

    千次阅读 2015-04-26 10:14:03
    孙业毅 原创作品 转载请注明出处 《Linux内核分析》MOOC课程:http://mooc.study.163.com/course/USTC-1000029000 第八讲 进程的切换和系统的一般执行过程  @2015.04 一、理论知识 正在更新……

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 22,576
精华内容 9,030
关键字:

切换过程