精华内容
下载资源
问答
  • 切换中断时延
    千次阅读
    2021-05-11 13:30:05

    什么是CPU上下文切换?

    现在linux是大多基于抢占式,CPU给每个任务一定的服务时间,当时间片轮转的时候,需要把当前状态保存下来,同时加载下一个任务,这个过程叫做上下文切换。时间片轮转的方式,使得多个任务利用一个CPU执行成为可能,但是保存现场和加载现场,也带来了性能消耗。 那线程上下文切换的次数和时间以及性能消耗如何看呢?

    ​ ​如何获得上下文切换的次数?

    vmstat直接运行即可,在最后几列,有CPU的context switch次数。 这个是系统层面的,加入想看特定进程的情况,可以使用pidstat。

    $ vmstat 1 100

    procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu------

    r b swpd free buff cache si so bi bo in cs us sy id wa st

    0 0 88 233484 288756 1784744 0 0 0 23 0 0 4 1 94 0 0

    4 0 88 233236 288756 1784752 0 0 0 0 6202 7880 4 1 96 0 0

    2 0 88 233360 288756 1784800 0 0 0 112 6277 7612 4 1 95 0 0

    0 0 88 232864 288756 1784804 0 0 0 644 5747 6593 6 0 92 2 0

    ​ ​执行pidstat,将输出系统启动后所有活动进程的cpu统计信息: ​

    linux:~ # pidstat

    Linux 2.6.32.12-0.7-default (linux) 06/18/12 _x86_64_

    11:37:19 PID %usr %system %guest %CPU CPU Command

    ……

    11:37:19 11452 0.00 0.00 0.00 0.00 2 bash

    11:37:19 11509 0.00 0.00 0.00 0.00 3 dd

    11:37:19: pidstat获取信息时间点

    PID: 进程pid

    %usr: 进程在用户态运行所占cpu时间比率

    %system: 进程在内核态运行所占cpu时间比率

    %CPU: 进程运行所占cpu时间比率

    CPU: 指示进程在哪个核运行

    Command: 拉起进程对应的命令

    备注:执行pidstat默认输出信息为系统启动后到执行时间点的统计信息,因而即使当前某进程的cpu占用率很高,输出中的值有可能仍为0。

    ​ ​

    ​ ​​上下文切换的性能消耗在哪里呢?

    ​ ​context switch过高,会导致CPU像个搬运工,频繁在寄存器和运行队列直接奔波 ,更多的时间花在了线程切换,而不是真正工作的线程上。直接的消耗包括CPU寄存器需要保存和加载,系统调度器的代码需要执行。间接消耗在于多核cache之间的共享数据。

    ​ ​引起上下文切换的原因有哪些?

    ​ ​对于抢占式操作系统而言, 大体有几种:

    ​ ​1、当前任务的时间片用完之后,系统CPU正常调度下一个任务;

    ​ ​2、当前任务碰到IO阻塞,调度线程将挂起此任务,继续下一个任务;

    ​ ​3、多个任务抢占锁资源,当前任务没有抢到,被调度器挂起,继续下一个任务;

    ​ ​4、用户代码挂起当前任务,让出CPU时间;

    ​ ​5、硬件中断;

    ​ ​

    ​ ​如何测试上下文切换的时间消耗?

    LMbench,知道这个工具,是在霸爷的博客上面(http://blog.yufeng.info/archives/753),然后就开始在测试环境下搞了一把,一会就出结果了。然后就搞了台线上机器安装这个工具,然后测试,后面在测试Memory的时候,直接导致Load飙升,还好没人发现,机器java进程重启就好了。这方面纯粹是业务选手。霸爷说分析的结果对于高性能C的开发同学来说,是需要熟记的,没办法,咱是搞java的,只能每个指标逐个看一下了。

    ​ ​LMbench的简单介绍?

    ​ 首先看英文介绍:LMbench -Tools for Performance Analysis,微观性能分析工具。

    ​ 官方地址:http://www.bitmover.com/lmbench/

    ​下载地址:http://www.bitmover.com/lmbench/lmbench3.tar.gz

    ​ ​LMbench​主要能干啥?

    ​主要是带宽(读取缓存文件、内存拷贝、读写内存、管道等)和反应时间(上下文切换、网路、进程创建等)的评测工具。

    ​ ​ LMbench ​安装

    #wget http://www.bitmover.com/lmbench/lmbench3.tar.gz

    #tar -zxvf lmbench3.tar.gz

    #cd lmbench3

    #make

    ​中间遇到一个问题,就是报错,在CSDN上面找到了答案,这这里贴一下。

    此时会报错:

    make[2]: *** 没有规则可以创建“bk.ver”需要的目标“../SCCS/s.ChangeSet”。 停止。

    make[2]:正在离开目录 `/home/hero/lmbench3/src'

    make[1]: *** [lmbench] 错误 2

    make[1]:正在离开目录 `/home/hero/lmbench3/src'

    make: *** [build] 错误 2

    解决办法:

    lmbench3目录下

    #mkdir SCCS

    #touch ./SCCS/s.ChangeSet

    #make

    ​ ​ LMbench关于结果解释(这次主要关注线程切换信息)

    在网上找了半天,信息很少,只能看doc下面的英文解释了。

    测试上下文切换的时间,一个上下文切换,包括保存一个进程状态的保存和恢复另外一个进程的时间。

    典型的上下文切换性能,仅仅是测量最小的线程切换时间。仅仅是做进程切换,任何实质的任务都不做。

    Context switching - times in microseconds - smaller is better

    Host OS 2p/0K 2p/16K 2p/64K 8p/16K 8p/64K 16p/16K 16p/64K

    ctxsw ctxsw ctxsw ctxsw ctxsw ctxsw ctxsw

    commonway Linux 2.6.18- 9.2400 4.0200 9.0300 7.5600 8.3800 11.6 6.28000

    时间的单位是微秒。

    LMbench是如何来测量进程切换的时间的?

    The benchmark is a ring of two to twenty processes that are connected

    with Unix pipes. A token is passed from process to process, forcing

    context switches. The benchmark measures the time it takes to pass

    the token two thousand times from process to process. Each hand off

    of the token has two costs: (a) the context switch, and (b) the cost

    of passing the token. In order to get just the context switching time,the benchmark first measures the cost of passing the token through a

    ring of pipes in a single process. This time is defined as the cost

    of passing the token and is not included in the reported context switch

    time.

    .PP

    When the processes are larger than the default baseline of ``zero''

    (where zero means just big enough to do the benchmark), the cost

    of the context switch includes the cost of restoring user level

    state (cache lines). This is accomplished by having the process

    allocate an array of data and sum it as a series of integers

    after receiving the token but before passing the token to the

    next process. Note that the overhead mentioned above includes

    the cost of accessing the data but because it is measured in

    just one address space, the cost is typically the cost with hot

    caches. So the context switch time does not include anything

    other than the context switch provided that all the processes

    fit in the cache. If there are cache misses (as is common), the

    cost of the context switch includes the cost of those cache misses.

    .PP

    首先是看任务处理的时间(通过一次任务处理,这个任务处理的时间被定义为token时间,不包括线程切换的)。

    然后多次执行,排除任务执行的时间,然后计算出CS的时间(如果有cache miss,则CS的时间也包括cache misses的时间)。

    文章参考:

    霸爷和周忱的博客

    更多相关内容
  • 快速切换技术的目标是尽可能减少切换时延并保证在切换过程中通信不中断,也没有数据包的丢失。快速切换技术主要有两种解决方案。一是切换前建立LSP机制,还有就是基于组播的快速切换机制。切换前建立LSP机制的基本...
  • 利用辅基站在切换过程中传输数据,以降低传统切换方式中因完全断开双连接导致数据传输中断所引起的时延。首先,对传统切换机制进行分析并进行问题定位;其次,提出和详细阐述新型切换机制的信令交互流程,并分别建立...
  • 在蜂窝移动通信系统中,切换可在服务小区信号变坏时把用户(UE)及其上下文从源小区迁移到目标小区,从而将UE切换到目标小区继续会话过程。LTE网络中切换仅支持与核心网连接(ECM-CONNECTED)状态的终端。终端在同一MME...

    在蜂窝移动通信系统中,切换可在服务小区信号变坏时把用户(UE)及其上下文从源小区迁移到目标小区,从而将UE切换到目标小区继续会话过程。

    LTE网络中切换仅支持与核心网连接(ECM-CONNECTED)状态的终端。

    终端在同一MME,服务网关也不更改场景下,终端切换主要流程如下:

    0816b7cb5a8369c94a3408a02f1be79d.png

    根据以上流程,终端切换可分为三阶段:

    •切换准备:源小区切换判决(UE和eNB的测量控制和测量结果评估),目标小区的资源准备(Step#1Step#6)

    •切换执行:UE接收切换命令和新小区无线资源获取(Step#7Step#11)

    •切换完成:源小区资源释放(Step#12到Step#18)

    切换时延

    切换时延是衡量系统性能指标之一。根据3GPP 36.881切换时延可定义为:终端接收到源小区切换命令的时间(RRC连接重配置中包括目标小区信息-- mobilityControlInfo)开始,到终端(UE)发送RRC连接重配置完成给目标小区截止;也就是说切换延迟是在切换执行(步骤)期间所花费的时间。

    切换时延因素

    切换过程中与延迟相关的因素包括:切换执行相关所有步骤,即从Step#7到Step#11

    e818e18a18b0198cd7b8457ba87c86eb.png

    Step#7:RRC连接重新配置消息中包括Mobilitycontrollinfo;在这一步UE接收到接入(目标小区)必要参数(即新的C-RNTI、目标eNB安全算法标识及可选的专用RACH前导-前导序列码、目标eNB SIBs等)的RRC Connection Restruction消息,并由源eNB下发命令,执行切换。切换进程中的延迟包括:RRC连接重构、移动性控制及相关重构,具体内容如下:

    • 层2 Reset/重配置;

    • MAC层Reset;

    • 对所有新建的RBs重建/重配置PDCP和RLC;

    • 对RRC消息进行加密和完整性保护;

    • 层3重配置(测量配置等);

        在TS36.331的11.2节定义,切换在RRC层最大允许时延为15 ms.

    Step#8:SN状态转移: 在这一步源eNB向目标eNB发送SN状态转移消息,以传递上行PDCP SN接收状态;下行链路PDCP SN发送使用那个E-RABs的PDCP状态 (即RLC AM)。由于这是eNB与eNB的信令与空中接口无关;其可以与后续的Step 9并行执行,此步中的延迟可被总延迟忽略。

    Step#9:终端接收切换命令后在目标小区上同步(Step#7:RRC Connection Reconfiguration  message中包括 mobilitycontrolinfo信息) ,UE此步中执行的内容如下:

    • 物理层同步和重新配置;

    • 终端开始与目标Pcell进行下行(DL)同步;

    • 物理层重配置;

    • 通过RACH接入目标小区(如果mobilityControlInfo,中指定了一个专用的RACH前导,则遵循不竞争流程;如果没有指定前导序列码,则遵循一个基于竞争接入流程);

    • 层2重配置

    • 密钥更新:UE向目标eNB索取密钥和安全算法配置,以便在目标小区使用。

    根据TS 36.133定义,当接收到RRC消息终端即开始切换,UE在最后一个包含RRC命令的TTI结束后,准备开始在新(小区)上行PRACH信道中发送数据;此处Dhandover是RRC进程延滞和(数据)“中断时间”;该中断时间定义为:旧小区PDSCH上最后一个包括RRC命令的TTI,到UE在新小区PRACH信道进行传输的时间差,其中不包括RRC进程的延迟。

    中断时间

    • 目标小区搜索;

    • 终端无线时钟处理/基带调整,根据目标eNB密钥进行加密算法应用;

    • 无线接入相关(根据随机接入序列码接入时机的获取时延);

    Step#9.1:目标小区搜索:在多数情况下目标小区是根据UE的测量报告选择,可假定是“已知的”,因此其延迟可被认为是0毫秒。

    Step#9.2: UE射频处理/基带重新调整时间,安全更新;其实际数值根据不同的参数有很大差异,为此可认为UE 处理时间(射频/基带重新调整,获取目标eNB特定密钥,配置安全算法在目标小区使用)在 TS  36.133定义中为20 ms

    Step#9.3获取目标eNB中首个可用PRACH延迟: 假设一个典型RACH配置中 每5个子帧有1 个RACH可用,该步的最小延迟为0.5ms,典型延迟为2.5 ms

    Step#9.4前导码传输:最后一个延迟单元是每次前导传输需要1个子帧,即1ms

    Step#10:上行指配+UE的TA。

    Step#10:在这一步中,目标eNB为UE的接入进行了UL资源和TA分配。假设LTE FDD和子帧数连续,如果UE在子帧 n 中发送RACH序列,根据规范eNB最早可以在子帧 n + 3中发送RAR响应 (5.1.4 of TS 36.321)。假设授权解码或TA延迟不包括在这一步,这一步的最小延迟将是3 ms,典型场景的平均延迟将是5 ms

    Step#11: UE发送RRC连接重新配置完成:当成功接入目标小区时,UE发送(包含C-RNTI)的RRCConnectionReconfigurationComplete 消息以确认切换;同时还把上行缓冲区状态报向目标eNB发送,表明UE完成了切换过程。目标eNB验证在RRCConnectionReconfigurationComplete消息中发送的C-RNTI。之后目标eNB可开始在下行向UE发送数据。

    根据TS 36.213中6.1.1节,UE最早可在 k1 > = 6个子帧之后发送RRC ConnectionReconfigurationComplete ;这一步延迟一般为6 ms,其中:包括UE处理延迟(调度授权解码和UL数据的时间对齐 + 层1数据编码)和RRC连接重配置传输完成。典型场景每Step时间延迟表:

    Component/ Step

    Description

    Time (ms)

    7

    RRC Connection Reconfiguration Incl. mobilityControlInfo

    15

    8

    SN Status Transfer

    0

    9.1

    Target cell search

    0

    9.2

    UE processing time for RF/baseband re-tuning, security  update

    20

    9.3

    Delay to acquire first available PRACH in target eNB

    0.5/2.5

    9.4

    PRACH preamble transmission

    1

    10

    UL Allocation + TA for UE

    3/5

    11

    UE sends RRC Connection Reconfiguration Complete

    6

    Minimum/Typical Total delay [ms]

    45.5/49.5

    注意:上述数值假设条件是在第一次就尝试成功。现实中接入尝试并不总是正确的,特别是在切换场景中,信道质量可能会降低;其中有些步骤可能需要重新传输,实际延迟值会更高。

    展开全文
  • 提升 5G 切换成功率!!
  • % Z队列——次用户切换队列,优先级次高,若在时延Tao内,则较次用户队列优先接入可用信道 % 2. 主用户和次用户的到达服从泊松分布,参数分别为lambdap和lambdas,平均服务时间服从参数为mup和mus的负指数分布 % 3. ...
  • 实验目的:中断方式按键(PA0)实现流水灯,每个灯之间的切换时延是2秒。 1.在文件stm32f4xx.h中找到STM32F40_41xxx系列HSE_VALUE(L144)改为8000000 在文件system_stm32f4xx.c中修改STMF40_41xxx系列 PLL_M=8 L...

    实验目的:中断方式按键(PA0)实现流水灯,每个灯之间的切换时延是2秒。
    1.在文件stm32f4xx.h中找到STM32F40_41xxx系列HSE_VALUE(L144)改为8000000
    在这里插入图片描述
    在文件system_stm32f4xx.c中修改STMF40_41xxx系列
    PLL_M=8 L371
    PLL_N=336 L401
    PLL_P=2 L403
    在这里插入图片描述
    systick.c

    #include "stm32f4xx.h"
    #include <stdint.h>
    #define rSysTickCtrl *((volatile unsigned long*)0xE000E010)
    
    static uint32_t systicknum = 0;
    
    
    //定时器中断函数
    void SysTick_Handler(void)
    {
        if(systicknum != 0)
        {
            systicknum--;
        }    
    }
    
    void delay_ms(uint32_t m)
    {
        systicknum = m;	
    		SysTick_Config(SystemCoreClock/1000);
    		NVIC_SetPriority(SysTick_IRQn, 0); 
        while(systicknum != 0);
    		rSysTickCtrl &=  ~(1 << 0) ; // bit0 -> 0  disable SysTick
    }
    
    void delay_us(uint32_t u)
    {
    	systicknum = u;
    	SysTick_Config(SystemCoreClock/1000000);
    	//NVIC_SetPriority(SysTick_IRQn, 1); 
    	while(systicknum != 0);
    	rSysTickCtrl &=  ~(1 << 0) ; // bit0 -> 0  disable SysTick
    }
    

    rj_led.c

    #include "rj_led.h"
    #include "systick.h"
    
    void led_init()
    {
       GPIO_InitTypeDef P;
    	/*使能GPIOD的时钟*/
    	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
    
    	/*配置PD0,PD1,PD2,PD3,为输出模式,输出类型为输出推挽,
    	               速率为50MHz,无上拉下拉电阻*/
    	P.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;
    	P.GPIO_Mode = GPIO_Mode_OUT;
    	P.GPIO_OType = GPIO_OType_PP;	
    	P.GPIO_Speed = GPIO_Speed_50MHz;
    	P.GPIO_PuPd = GPIO_PuPd_NOPULL;
    	GPIO_Init(GPIOD, &P);
    }
    
    void led_ctrl(int led_n)
    {	
    	switch(led_n)
    		{
    			case 1:
    				{            /*控制LED1亮,其他灯熄灭,给PD0端口置1,PD1,PD2,PD3端口置0*/
    					GPIO_SetBits(GPIOD, GPIO_Pin_0);
    					GPIO_ResetBits(GPIOD, GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3);
    					break;
    				}
    			case 2:
    				{            /*控制LED2亮,其他灯熄灭,给PD1端口置1,PD0,PD2,PD3端口置0*/
    					GPIO_SetBits(GPIOD, GPIO_Pin_1);
    					GPIO_ResetBits(GPIOD, GPIO_Pin_0|GPIO_Pin_2|GPIO_Pin_3);
    					break;
    				}
    			case 3:
    				{             /*控制LED3亮,其他灯熄灭,给PD2端口置1,PD1,PD0,PD3端口置0*/
    					GPIO_SetBits(GPIOD, GPIO_Pin_2);
    					GPIO_ResetBits(GPIOD, GPIO_Pin_1|GPIO_Pin_0|GPIO_Pin_3);
    					break;
    				}
    			case 4:
    				{              /*控制LED4亮,其他灯熄灭,给PD3端口置1,PD1,PD2,PD0端口置0*/
    					GPIO_SetBits(GPIOD, GPIO_Pin_3);
    					GPIO_ResetBits(GPIOD, GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_0);
    					break;
    				}
    			default:
    				break;
    		}
    }
    
    void delay(int num)
    {
    		while(num--);
    }
    
    void led_loop()      //LED等循环点亮;
    {
    	int led_count=1;	
    	for(led_count=1;led_count<5;led_count++)
    	{
    		led_ctrl(led_count);
    		delay_ms(200);
    	}	
    }
    
    

    rj_int_key.c

    #include "stm32f4xx.h"
    
    
    /*
    	KEY -> PA -> EXTI -> NVIC -> CPU EXTI_IRQHandler
    */
    void key_int_init(void)
    {
    	//时钟使能;
    	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOE,ENABLE);
    
    	/*配置端口,为输入模式,速率为50MHz,不上拉*/
    	GPIO_InitTypeDef p;
    	p.GPIO_Pin=GPIO_Pin_0;
    	p.GPIO_Mode=GPIO_Mode_IN;       
    	p.GPIO_Speed=GPIO_Speed_50MHz;
    	//p.GPIO_OType=GPIO_OType_PP;  //输出推挽;
    	p.GPIO_PuPd=GPIO_PuPd_NOPULL;
    
    	GPIO_Init(GPIOA,&p);
    	p.GPIO_Pin=GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4;
    	GPIO_Init(GPIOE,&p);
    	
    	/*SYSCFG选择器的配置*/
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);	//时钟使能;
    	SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA,EXTI_PinSource0);  //选择PA0作为外部中断的输入引脚;
    	SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource2);   //PE2
    	SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource3);  //PE3
    	SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource4);   //PE4
    	
    	/*外部中断控制器的配置*/
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_EXTIT, ENABLE);//时钟使能;
    	EXTI_InitTypeDef e;
    	e.EXTI_Line=EXTI_Line0|EXTI_Line2|EXTI_Line3|EXTI_Line4;
    	e.EXTI_Mode=EXTI_Mode_Interrupt;    //中断模式;
    	e.EXTI_LineCmd=ENABLE;
    	e.EXTI_Trigger=EXTI_Trigger_Rising;       //上升沿;
    	
    	EXTI_Init(&e);
    	
    	/*NVIC的控制*/
    	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//pre:2,sub:2 
    	NVIC_InitTypeDef n;
    	n.NVIC_IRQChannel=EXTI0_IRQn;
    	n.NVIC_IRQChannelCmd=ENABLE;
    	//n.NVIC_IRQChannelPreemptionPriority = 0x02;
    	//n.NVIC_IRQChannelSubPriority = 0x02;
    	NVIC_Init(&n);
    	
    	n.NVIC_IRQChannel=EXTI2_IRQn;
    	NVIC_Init(&n);
    	
    	n.NVIC_IRQChannel=EXTI3_IRQn;
    	NVIC_Init(&n);
    	
    	n.NVIC_IRQChannel=EXTI4_IRQn;
    	NVIC_Init(&n);
    	
    	NVIC_SetPriority(EXTI0_IRQn,0x07);
    	
    	
    }
    
    
    uint8_t	status = 0;
    void key_irqHandler(void)
    {
    	//	1.EXTI_GetITStatus 
    	if (EXTI_GetITStatus(EXTI_Line0) == SET)
    	{
    		status = 1;
    		//EXTI_ClearFlag 清除中断标识,否则会一直产生中断;
    		EXTI_ClearFlag(EXTI_Line0);
    	}
    
    }
    
    
    //中断函数,按一下亮,再按一下灭
    //int f1 = 0, f2 = 0, f3 = 0, f4 = 0;
    
    //void key_irqHandler(void)
    //{
    //	if (EXTI_GetITStatus(EXTI_Line0) == SET)
    //	{
    //		if (f1 == 0)
    //		{
    //			GPIO_SetBits(GPIOD, GPIO_Pin_0);
    //			f1 = 1;
    //		}
    //		else
    //		{
    //			GPIO_ResetBits(GPIOD, GPIO_Pin_0);
    //			f1 = 0;
    //		}
    //		EXTI_ClearITPendingBit(EXTI_Line0);
    //	}
    //	if (EXTI_GetITStatus(EXTI_Line2) == SET)
    //	{
    //		if (f2 == 0)
    //		{
    //			GPIO_SetBits(GPIOD, GPIO_Pin_1);
    //			f2 = 1;
    //		}
    //		else
    //		{
    //			GPIO_ResetBits(GPIOD, GPIO_Pin_1);
    //			f2 = 0;
    //		}
    //		EXTI_ClearITPendingBit(EXTI_Line2);
    //	}
    //	if (EXTI_GetITStatus(EXTI_Line3) == SET)
    //	{
    //		if (f3 == 0)
    //		{
    //			GPIO_SetBits(GPIOD, GPIO_Pin_2);
    //			f3 = 1;
    //		}
    //		else
    //		{
    //			GPIO_ResetBits(GPIOD, GPIO_Pin_2);
    //			f3 = 0;
    //		}
    //		EXTI_ClearITPendingBit(EXTI_Line3);
    //	}
    //	if (EXTI_GetITStatus(EXTI_Line4) == SET)
    //	{
    //		if (f4 == 0)
    //		{
    //			GPIO_SetBits(GPIOD, GPIO_Pin_3);
    //			f4 = 1;
    //		}
    //		else
    //		{
    //			GPIO_ResetBits(GPIOD, GPIO_Pin_3);
    //			f4 = 0;
    //		}
    //		EXTI_ClearITPendingBit(EXTI_Line4);
    //	}
    //}
    

    在这里插入图片描述
    实验框图:
    在这里插入图片描述
    演示视频:https://www.bilibili.com/video/BV1Pi4y1x7q1
    在这里插入图片描述

    展开全文
  • 4G网络LTE切换及重选

    2020-09-25 19:26:00
    4GLTE切换及重选资料,详细描述了LTE网络切换及重新的基本概念。空闲状态下的UE需要完成的过程包括公共陆地移动网络(PLMN)选择、小区选择/重选、位置登记等。一旦完成驻留,UE可以读取系统信息(如驻留、接入和重...
  • 本方案通过将多播技术应用于移动锚点(MAP),并且在切换完成之前提前完成新链路上标签交换路径(LSP)的建设,实现了移动节点(MN)移交切换时延的最小化,使得在切换过程中大大降低服务中断现象,最大限度地保证新链路...
  • 本文在预注册方法与后注册方法的...可以进一步减少切换时延。此外,还提出将NAT穿透技术与移动节点通信相结合,解决了私网条件下移动节点的互相通信问题。文章末尾还利用petri网建模的方式,验证了改进方法的有效性。
  • 微移动切换算法提出了一个交叉路由的切换方案,该方案可以减少延迟服务中断期间所发生的切换过程. 微移动切换算法的实现是移动节点在向前推进的过程中,预先为下一个节点发送资源请求以保留该移动节点切换 发生,...
  • 目前单个接入点所能覆盖的范围十分有限,移动节点发生跨区域切换和频繁切换的可能性较高。在文章的第二部分总结了现有的解决方案,它们都可以显著减少移动节点的切换时间。  NAT实现网络地址转换功能。通过NAT可以...
  • 因此,结合IEEE 802.21媒体独立切换(MIH)协议提出一种基于L2触发的异构网络切换方案,通过NS-2仿真验证了L2触发的切换方案能有效减少切换时延和丢包,并评价了不同速度、不同预测系数对切换期间的中断概率、丢包率...
  • 针对密集异构网络中用户更容易发生频繁切换的问题,提出了一种基于参考信号接收功率(Reference Signal ...仿真结果表明,相比于当前的切换算法,所提的切换算法能够获得更低的中断概率和乒乓切换率以及更高的吞吐量。
  • LTE切换、重选.docx

    2019-07-05 08:48:57
    小区重选: 空闲态下选择最优小区进行驻留,由UE控制。无信令交互。 小区切换: 连接态下选择最优小区进行业务,由ENB控制。
  • Mobile IP在IP层提供移动支持,移动主机可以从一个地方移动到另一个...介绍了其中的几种,并提出了改进的方案:采用预先建立的隧道来降低切换时延,应用本地广播和FA处的缓存来减少丢包,利用提前的资源预留来保证QoS。
  • 本文在分析目前采用的WLAN切换技术,即站点(STA)主动切换技术,的基础上,为了保证了切换的效率和安全性,提出了可管理快速切换 (MFHO)技术。该技术可以通过两种方法实现,即切换指示方法和切换申请方法。两种切换...
  • 切换 WCDMA 硬切换切换 更软切换 切换 WCDMA 硬切换切换 更软切换 切换 WCDMA 硬切换切换 更软切换
  • 低轨卫星通信系统中的一种波束小区切换算法.pdf
  • 基于扩展EAP协议的异构网络切换协同认证机制,王磊,王莉,在异构无线融合网络中,当移动设备发生切换过程时,接入系统时的认证过程会带来的严重时延问题,甚至会造成通信中断。本文在研究
  • LTEFDD网络性能优化指导书—切换性能优化.doc
  • 原标题:linux中断编程、中断编程详解Linux中断处理驱动程序编写中断处理是操作系统必须具备的上要功能之一,下面我们一起来探讨一下Linux中的中断处理。1. 什么是中断中断就是CPU正常运行期间,由于内、外部事件...

    原标题:linux中断编程、中断编程详解

    Linux中断处理驱动程序编写

    中断处理是操作系统必须具备的上要功能之一,下面我们一起来探讨一下Linux中的中断处理。

    1. 什么是中断

    中断就是CPU正常运行期间,由于内、外部事件引起的CPU暂时停止正在运行的程序,去执行该内部事件或外部事件的引起的服务中去,服务执行完毕后再返回断点处继续执行的情形。这样的中断机制极大的提高了CPU运行效率。

    1.1. 中断的分类:

    1) 根据中断的来源可分为内部中断和外部中断,内部中断的中断源来自于CPU内部(软件中断指令、溢出、除法错误等),例如操作系统从用户态切换到内核态需要借助CPU内部的软件中断,外部中断的中断源来自于CPU外部,由外设触发。

    2) 根据中断是否可以被屏蔽,中断可分为可屏蔽中断和不可屏蔽中断,可屏蔽中断可以通过设置中断控制器寄存器等方法被屏蔽,屏蔽后,该中断不再得到响应,而不可屏蔽中断不能被屏蔽。

    3) 根据中断入口跳转方式的不同,中断可分为向量中断和非向量中断。采用向量中断的CPU通常为不同的中断分配不同的中断号,当检测到中断的中断号到来时,就自动跳转到该中断对应的地址处去执行程序。不同的中断号对应不同的中断入口地址。非向量中断的多个中断共享一个入口程序处理入口地址,中断程序跳转到该入口地址执行时,再通过中断程序来判断中断标志来识别具体是哪一个中断,也就是说向量中断由硬件提供中断服务程序入口地址,非向量中断由软件提供中断服务程序入口地址。

    4) 非向量中断处理流程:

    /*典型的非向量中断首先会判断中断源,然后调用不同中断源的中断处理程序*/

    irq_handler()

    {

    ...

    int int_src = read_int_status();/*读硬件的中断相关寄存器*/

    switch(int_src)

    {

    //判断中断标志

    case DEV_A:

    dev_a_handler();

    break;

    case DEV_B:

    dev_b_handler();

    break;

    ...

    default:

    break;

    }

    ...

    }

    2. linux中断顶部、底部概念

    为保证系统实时性,中断服务程序必须足够简短,但实际应用中某些时候发生中断时必须处理大量的工作,这时候如果都在中断服务程序中完成,则会严重降低中断的实时性,基于这个原因,linux系统提出了一个概念:把中断服务程序分为两部分:顶半部、底半部。

    2.1. 顶半部

    完成尽可能少的比较急的功能,它往往只是简单的读取寄存器的中断状态,并清除中断标志后就进行“中断标记”(也就是把底半部处理程序挂到设备的底半部执行队列中)的工作。特点是响应速度快。

    2.2. 底半部

    中断处理的大部分工作都在底半部,它几乎做了中断处理程序的所有事情。 特点:处理相对来说不是非常紧急的事件 ,底半部机制主要有:tasklet、工作队列和软中断。

    Linux中查看/proc/interrupts文件可以获得系统中断的统计信息:

    3. Linux中断编程

    3.1. 申请和释放中断

    3.1.1. 申请中断:

    int request_irq(unsigned int irq,irq_handler_t handler,unsigned long irqflags,const char *devname,void *dev_id);

    参数介绍:irq是要申请的硬件中断号。

    Handler:是向系统登记的中断处理程序(顶半部),是一个回调函数,中断发生时,系统调用它,将dev_id参数传递给它。

    irqflags:是中断处理的属性,可以指定中断的触发方式和处理方式:

    触发方式:IRQF_TRIGGER_RISING、IRQF_TRIGGER_FALLING、IRQF_TRIGGER_HIGH、IRQF_TRIGGER_LOW,处理方式:IRQF_DISABLE表明中断处理程序是快速处理程序,快速处理程序被调用时屏蔽所有中断,IRQF_SHARED表示多个设备共享中断,dev_id在中断共享时会用到,一般设置为NULL。

    返回值:为0表示成功,返回-EINVAL表示中断号无效,返回-EBUSY表示中断已经被占用,且不能共享。

    顶半部的handler的类型irq_handler_t定义为:

    typedef irqreturn_t (*irq_handler_t)(int,void*);

    typedef int irqreturn_t;

    3.1.2. 释放IRQ

    有请求当然就有释放。中断的释放函数为:

    void free_irq(unsigned int irq,void *dev_id);

    参数定义与request_irq类似。

    3.1.3. 中断的使能和屏蔽

    void disable_irq(int irq);//等待目前中断处理完成(最好别在顶板部使用,你懂得)

    void disable_irq_nosync(int irq);//立即返回

    void enable_irq(int irq);//

    3.1.4. 屏蔽本CPU内所有中断:

    #define local_irq_save(flags)...//禁止中断并保存状态。

    void local_irq_disable(void); //禁止中断,不保存状态。

    下面来分别介绍一下顶半部和底半部的实现机制

    3.1.5. 底半部机制:

    简介:底半部机制主要有tasklet、工作队列和软中断

    3.1.5.1. 底半部实现方法之一tasklet

    (1) 我们需要定义tasklet机器处理器并将两者关联,例如:

    void my_tasklet_func(unsigned long);/*定义一个处理函数*/

    DECLARE_TASKLET(my_tasklet,my_tasklet_func,data);

    /*上述代码定义了名为my_tasklet的tasklet并将其与my_tasklet_func()函数绑定,传入的参数为data*/

    (2)调度

    tasklet_schedule(&my_tasklet);

    //使用此函数就能在是当的时候进行调度运行

    (3)tasklet使用模板:

    /*定义tasklet和底半部函数并关联*/

    void xxx_do_tasklet(unsigned long);

    DECLARE_TASKLET(xxx_tasklet, xxx_do_tasklet,0);

    /*中断处理底半部*/

    void xxx_do_tasklet(unsigned long)

    {

    ...

    }

    /*中断处理顶半部*/

    irqreturn_t xxx_interrupt(int irq,void *dev_id)

    {

    ...

    tasklet_schedule(&xxx_tasklet);//调度地半部

    ...

    }

    /*设备驱动模块加载函数*/

    int __init xxx_init(void)

    {

    ...

    /*申请中断*/

    result = request_irq(xxx_irq,xxx_interrupt, IRQF_DISABLED,"xxx",NULL);

    ...

    return IRQ_HANDLED;

    }

    /*设备驱动模块卸载函数*/

    void __exit xxx_exit(void)

    {

    ...

    /*释放中断*/

    free_irq(xxx_irq,xxx_interrupt);

    ...

    }

    3.1.5.2. 底半部实现方法之二---工作队列

    使用方法和tasklet类似,相关操作:

    struct work_struct my_wq;/*定义一个工作队列*/

    void my_wq_func(unsigned long);/*定义一个处理函数*/

    通过INIT_WORK()可以初始化这个工作队列并将工作队列与处理函数绑定INIT_WORK(&my_wq,(void (*)(void *))my_wq_func,NULL);/*初始化工作队列并将其与处理函数绑定*/

    schedule_work(&my_wq);/*调度工作队列执行*/

    /*工作队列使用模板*/

    /*定义工作队列和关联函数*/

    struct work_struct(unsigned long);

    void xxx_do_work(unsigned long);

    /*中断处理底半部*/

    void xxx_do_work(unsigned long)

    {

    ...

    }

    /*中断处理顶半部*/

    irqreturn_t xxx_interrupt(int irq,void *dev_id)

    {

    ...

    schedule_work(&my_wq);//调度底半部

    ...

    return IRQ_HANDLED;

    }

    /*设备驱动模块加载函数*/

    int xxx_init(void)

    {

    ...

    /*申请中断*/

    result = request_irq(xxx_irq,xxx_interrupt,IRQF_DISABLED,"xxx",NULL);

    ...

    /*初始化工作队列*/

    INIT_WORK(&my_wq,(void (*)(void *))xxx_do_work,NULL);

    }

    /*设备驱动模块卸载函数*/

    void xxx_exit(void)

    {

    ...

    /*释放中断*/

    free_irq(xxx_irq,xxx_interrupt);

    ...

    }

    4. 中断共享

    中断共享是指多个设备共享一根中断线的情况,中断共享的使用方法:

    (1).在申请中断时,使用IRQF_SHARED标识;

    (2).在中断到来时,会遍历共享此中断的所有中断处理程序,直到某一个函数返回IRQ_HANDLED,在中断处理程序顶半部中,应迅速根据硬件寄存器中的信息参照dev_id参数判断是否为本设备的中断,若不是立即返回IR1_NONE

    /*共享中断编程模板*/

    irqreturn_t xxx_interrupt(int irq,void *dev_id,struct pt_regs *regs)

    {

    ...

    int status = read_int_status();/*获知中断源*/

    if(!is_myint(dev_id,status))/*判断是否为本设备中断*/

    return IRQ_NONE;/*不是本设备中断,立即返回*/

    /*是本设备中断,进行处理*/

    ...

    return IRQ_HANDLED;/*返回IRQ_HANDLER表明中断已经被处理*/

    }

    /*设备模块加载函数*/

    int xxx_init(void)

    {

    ...

    /*申请共享中断*/

    result = request_irq(sh_irq,xxx_interrupt,IRQF_SHARE,"xxx",xxx_dev);

    ...

    }

    /*设备驱动模块卸载函数*/

    void xxx_exit()

    {

    ...

    /*释放中断*/

    free_irq(xxx_irq,xxx_interrupt);

    ...

    }

    5. 内核定时器

    内核定时器编程:

    简介:软件意义上的定时器最终是依赖于硬件定时器实现的,内核在时钟中断发生后检测各定时器是否到期,到期后定时器处理函数作为软中断在底半部执行。

    Linux内核定时器操作:

    5.1. timer_list结构体

    每一个timer_list对应一个定时器

    struct timer_list{

    struct list_head entry;/*定时器列表*/

    unsigned long expires;/*定时器到期时间*/

    void (*function)(unsigned long);/*定时器处理函数*/

    unsigned long data;/*作为参数被传递给定时器处理函数*/

    struct timer_base_s *base;

    ...

    };

    当定时器满的时候,定时器处理函数将被执行

    5.2. 初始化定时器

    void init_timer(struct timer_list * timer);

    //初始化timer_list的entry的next为NULL,并给base指针赋值。

    TIMER_INITIALIZER(_function,_expires,_data);//此宏用来

    //赋值定时器结构体的function、expires、data和base成员

    #define TIMER_INITIALIZER(function,_expires,_data)

    {

    .entry = {.prev = TIMER_ENTRY_STATIC},\

    .function= (_function), \

    .expires = (_expire), \

    .data = (_data), \

    .base = &boot_tvec_bases,\

    }

    DEFINE_TIMER(_name,_function,_expires,_data)//定义一个定时器结构体变量//并为此变量取名_name

    //还有一个setup_timer()函数也可以用于定时器结构体的初始化。

    5.3. 增加定时器

    void add_timer(struct timer_list * timer);//注册内核定时器,也就是将定时器加入到内核动态定时器链表当中。

    5.4. 删除定时器

    del_timer(struct timer_list *timer);

    del_timer_sync()//在删除一个定时器时等待删除操作被处理完(不能用于中断上下文中)

    5.5. 修改定时器expires

    int mod_timer(struct timer_list * timer,unsigned long expires);//修改定时器的到期时间

    /*内核定时器使用模板*/

    /*xxx设备结构体*/

    struct xxx_dev

    {

    struct cdev cdev;

    ...

    timer_list xxx_timer;/*设备要使用的定时器*/

    };

    /*xxx驱动中的某函数*/

    xxx_funcl(...)

    {

    struct xxx_dev *dev = filp->private_data;

    ...

    /*初始化定时器*/

    init_timer(&dev->xxx_timer);

    dev->xxx_timer.function = &xxx_do_timer;

    dev->xxx_timer.data = (unsigned long)dev;

    /*设备结构体指针作为定时器处理函数参数*/

    dev->xxx_timer.expires = jiffes + delays;

    /*添加(注册)定时器*/

    add_timer(&dev->xxx_timer);

    ...

    }

    /*xxx驱动中的某函数*/

    xxx_func2(...)

    {

    ...

    /*删除定时器*/

    del_timer(&dev->xxx_timer);

    ...

    }

    /*定时器处理函数*/

    static void xxx_do_timer(unsigned long arg)

    {

    struct xxx_device *dev = (struct xxx_device *)(arg);

    ...

    /*调度定时器再执行*/

    dev->xxx_timer.expires = jiffes + delay;

    add_timer(&dev -> xxx_timer);

    ...

    }

    //定时器到期时间往往是在jiffies的基础上添加一个时延,若为HZ则表示延迟一秒

    5.6. 内核中的延迟工作:

    简介:对于这种周期性的工作,Linux提供了一套封装好的快捷机制,本质上利用工作队列和定时器实现,这其中用到两个结构体:

    (1)struct delayed_work

    {

    struct work_struct work;

    struct timer_list timer;

    };

    (2) struct work_struct

    {

    atomic_long_t data;

    ...

    }

    相关操作:

    int schedule_delay_work(struct delayed_work *work,unsigned long delay);//当指定的delay到来时delay_work中的work成员的work_func_t类型成员func()会被执行work_func_t类型定义如下:

    typedef void (*work_func_t)(struct work_struct *work);//delay参数的单位是jiffes

    mescs_to_jiffies(unsigned long mesc);//将毫秒转化成jiffes单位

    int cancel_delayed_work(struct delayed_work *work);

    int cancel_delayed_work_sync(struct delayed_work *work);//等待直到删除(不能用于中断上下文)

    内核延迟的相关函数:

    短延迟:

    Linux内核提供了如下三个函数分别进行纳秒、微妙和毫秒延迟:

    void ndelay(unsigned long nsecs);

    void udelay(unsigned long usecs);

    void mdelay(unsigned long msecs);

    机制:根据CPU频率进行一定次数的循环(忙等待)

    注意:在Linux内核中最好不要使用毫秒级的延时,因为这样会无谓消耗CPU的资源。

    对于毫秒以上的延时,Linux提供如下函数:

    void msleep(unsigned int millisecs);

    unsigned long msleep_interruptible(unsigned int millisecs);//可以被打断

    void ssleep(unsigned int seconds);

    //上述函数使得调用它的进程睡眠指定的时间

    长延迟:

    机制:设置当前jiffies加上时间间隔的jiffies,直到未来的jiffies达到目标jiffires

    /*实例:先延迟100个jiffies再延迟2s*/

    unsigned long delay = jiffies + 100;

    while(time_before(jiffies,delay));

    /*再延迟2s*/

    unsigned long delay = jiffies + 2*Hz;

    while(time_before(jiffies,delay));//循环直到到达指定的时间与timer_before()相对应的还有一个time_after

    睡着延迟:

    睡着延迟是比忙等待更好的一种方法

    机制:在等待的时间到来之前进程处于睡眠状态,CPU资源被其他进程使用,实现函数有:

    schedule_timeout()

    schedule_timeout_uninterruptible()

    其实在短延迟中的msleep() msleep_interruptible()

    本质上都是依赖于此函数实现的,下面两个函数可以让当前进程加入到等待队列中,从而在等待队列上睡眠,当超时发生时,进程被唤醒

    sleep_on_timeout(wait_queue_head_t *q,unsigned long timeout);

    interruptible_sleep_on_timeout(wait_queue_head_t *q,unsigned long timeout);返回搜狐,查看更多

    责任编辑:

    展开全文
  • Linux中断与时钟

    2022-05-01 15:30:00
    内部(软件中断指令、溢出、除法错误等,例如,操作系统从用户态切换到内核态需 借助 CPU 内部的软件中断),外部中断中断源来自 CPU 外部,由外设提出请求。根据是否可以屏蔽中断分为可屏蔽中断与不屏蔽中断(NMI) 可...
  • 中断 为了对计算机的硬件(键盘,硬盘,鼠标等)进行管理,内核需要和这些硬件通信。一种方式是使用轮训(polling)的方式,这种方式周期性地查看所有硬件设备的状态并做相应处理,这会造成很多不必要的系统开销。...
  • 有的时候也会发生进程上下文的切换切换的过程见100us的日志。   日志1 -- -i为100us的日志: <...>-1874 0d..3 13773888us : CoBaLt_clock_nanosleep  <...>-1874 0d..3 13773890us : __cobalt_clock_...
  • 根据阿里云底层监控,当一条跨可用区链路中断时,通常会导致持续3-5秒的1%左右的丢包(具体需要看中断链路数量占总链路数量的占比),而反映在业务上,则有可能造成时延敏感业务接近1分钟的部分超时报错。...
  • 异构无线网络中基于模糊逻辑的分级垂直切换算法.pdf

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,778
精华内容 1,911
关键字:

切换中断时延