多任务系统概述

news2024/11/17 4:54:21

一个例子:

int Main(void) 
{ 
    TargetInit(); //初始化目标板
    OSInit(); //初始化操作系统 
    OSTaskCreate(Task0,&StackTask0[StackSizeTask0 - 1],PrioTask0); // 创建一个任务
    Uart_Printf("Ready to start OS\n"); 
    OSStart(); //运行操作系统
    return 0; //程序不会运行至此 
} 

void Task0(void) 
{ 
    TargetStart(); //设置中断向量,启动操作系统的硬件定时器中断
    Uart_Printf("Start OS\n"); 
    // 创建其他任务
    OSTaskCreate(Task1,&StackTask1[StackSizeTask1 - 1],PrioTask1); 
    OSTaskCreate(Task2,&StackTask2[StackSizeTask2 - 1],PrioTask2); 
    OSTaskCreate(Task3,&StackTask3[StackSizeTask2 - 1],PrioTask3); 
    while(1) 
    { 
        Uart_Printf("Task0\n"); 
        OSTimeDly(100); //1 秒运行一次
    } 
} 

void Task1(void) 
{ 
    while(1) 
    { 
        Uart_Printf("Task1\n"); 
        OSTimeDly(300); //3 秒运行一次
    } 
}


void Task2(void) 
{ 
    while(1) 
    { 
        Uart_Printf("Task2\n"); 
        OSTaskSuspend(PrioTask2); // 使自己进入挂起状态 
    } 
} 


void Task3(void) 
{ 
    while(1) 
    { 
        Uart_Printf("Resume Task2\n"); 
        OSTaskResume(PrioTask2); // 恢复任务 2 
        OSTimeDly(800); 
    } 
}

程序中创建了四个任务,任务 0 每 1 秒运行一次,任务 1 每 3 秒运行一次,任务 2 运行一次即把自己挂起,任务 3 每 8 秒运行一次并把任务 2 恢复。

在多任务系统中,可以同时执行多个并行任务,各个任务之间互相独立。通过操作系统执行任务调度而实现宏观上的“并发运行”。从宏观上不同的任务并发运行,好像每个任务都有自己的 CPU 一样。

其实在单一 CPU 的情况下,是不存在真正的多任务机制的存在的只有不同的任务轮流使用 CPU,所以本质上还是单任务的。但由于 CPU 执行速度非常快,加上任务切换十分频繁并且切换的很快,所以我们感觉好像有很多任务同时在运行一样。这就是所谓的多任务机制。

多任务的最大好处是充分利用硬件资源,如在单任务时(大循环结构,如大部分 51程序)遇到 delay 函数时,CPU 在空转;而在多任务系统,遇到 delay 或需等待资源时系统会自动运行下一个任务,等条件满足再回来运行先前的任务,这样就充分利用了 CPU,提高了效率。

任务

任务有下面的特性:

动态性

任务并不是随时都可以运行的,而一个已经运行的任务并不能保证一直占有 CPU 直到运行完。一般有就绪态,运行态,挂起态等。

  • 运行态。一个运行态的任务是一个正在使用 CPU 的任务。任何时刻有且只有一个运行着的任务。
  • 就绪态。一个就绪态任务是可运行的,等待占有 CPU 的任务释放 CPU。
  • 挂起态。某些条件不满足而挂起不能运行的状态。

如何实现状态的转换?

其实就和排值日表一样,在不同的表里面跳来跳去。

INT32U OSRdyTbl; /* 就绪任务表 */

/* 在就绪表中登记就绪任务 */ 
#define OSSetPrioRdy(prio) \ 
{ \ 
	OSRdyTbl |= 0x01<<prio; \ 
} 
 
/* 从就绪表中删除任务 */ 
#define OSDelPrioRdy(prio) \ 
{ \ 
	OSRdyTbl &= ~(0x01<<prio); \ 
}

上面定义一个 32 位变量,每一位代表一个任务,0 表示挂起状态,1 表示就绪状态。它记录了各任务的就绪与否状态,称它为就绪表。OSRdyTbl 定义为 32 位变量,对应 32 个任务。当然,定义为 64 位的话,便最多能支持 64 个任务。

独立性

任务之间互相独立,不存在互相调用的关系。所有任务在逻辑上都是平等的。

由于任务之间互相看不见,所以他们之间的信息传输就无法当面完成。这就需要各种通信机制如信号量,消息邮箱,队列等来实现。

并发性

由同一个处理器轮换地运行多个程序。或者说是由多个程序轮班地占用处理器这个资源。且在占用这个资源期间,并不一定能够把程序运行完毕。

抢占式调度

调度的概念,通俗的说就是系统在多个任务中选择合适的任务执行。系统如何知道何时该执行哪个任务?可以为每个任务安排一个唯一的优先级别,当同时有多个任务就绪时,优先运行优先级较高的任务。

同时,任务的优先级也作为任务的唯一标识号。代码中都是对标识号来完成对任务的操作的。如 OSDelPrioRdy(prio),OSSetPrioRdy(prio)等。

不同的优先级对应就绪表中的每一位。低位对应高优先级。优先级 0 的优先权最高,优先级 31 的优先权最低。

所以在程序一开始就要先为每一个任务分配一个唯一的优先级:

///**************定义任务优先级*************/// 
#define PrioTask0 0 
#define PrioTask1 1 
#define PrioTask2 2 
#define PrioTask3 3

所谓“抢占式调度”是指:一旦就绪状态中出现优先权更高的任务,便立即剥夺当前任务的运行权,把 CPU 分配给更高优先级的任务。这样 CPU 总是执行处于就绪条件下优先级最高的任务。

在程序中查找最高优先级的任务代码如下:

/* 在就绪表中查找更高级的就绪任务 */ 
#define OSGetHighRdy() 
{ 
    for( OSNextTaskPrio = 0; (OSNextTaskPrio < OS_TASKS) && (!(OSRdyTbl & (0x01<<OSNextTaskPrio))); OSNextTaskPrio ++ ); 
    OSPrioHighRdy = OSNextTaskPrio; 
}

多任务系统的时间管理

多任务系统自身也是需要一个时钟的,滴答定时器经常被拿来充当这一角色。

与人一样,多任务系统也需要一个“心跳”来维持其正常运行,这个心跳叫做时钟节拍,通常由定时器产生一个固定周期的中断来充当,频率一般为 50-100Hz。在TargetInit.c 文件中有下面的定时器 0 初始化函数,T0 用作系统心跳计时,产生时钟节拍。

#define OS_TICKS_PER_SEC 100 /* 设置一秒内的时钟节拍数*/
void StartTicker(INT32U TicksPerSec) 
{ 
    rTCFG0 = 99; //Prescaler0 = 99 
    rTCFG1 = 0x03; //Select MUX input for PWM Timer0:divider=16 
    rTCNTB0 = 31250 / TicksPerSec; //设置中断频率
    rTCON |= (1<<1); //Timer 0 manual update 
    rTCON = 0x09; //Timer 0 auto reload on 
    //Timer 0 output inverter off 
     //清"Timer 0 manual update" 
    //Timer 0 start */ 
    BIT_CLR(rINTMSK, BIT_TIMER0); // Enable WatchDog interrupts 
}

OSTimeDly 函数就是以时钟节拍为基准来延时的。这个函数完成功能很简单,就是先挂起当起当前任务,设定其延时节拍数,然后进行任务切换,在指定的时钟节拍数到来之后,将当前任务恢复为就绪状态。任务必须通过 OSTimeDly 或 OSTaskSuspend 让出 CPU的使用权,使更低优先级任务有机会运行。

void OSTimeDly(INT32U ticks) 
{ 
    if( ticks > 0 ) /* 当延时有效 */ 
    { 
        OS_ENTER_CRITICAL(); 
        OSDelPrioRdy(OSPrioCur); /* 把任务从就绪表中删去 */ 
        TCB[OSPrioCur].OSTCBDly = ticks; /* 设置任务延时节拍数 */ 
        OS_EXIT_CRITICAL(); 
        OSSched(); /* 重新调度 */ 
    } 
}

在 T0 的中断服务函数中,依次对各个延时任务的延时节拍数减 1。若发现某个任务的延时节拍数变为 0,则把它从挂起态置为就绪态。

void TickInterrupt(void) 
{ 
    static INT8U i; 
     
    OSTime ++; 
    //Uart_SendByte('I'); 
    for(i = 0; i < OS_TASKS; i++) /* 刷新各任务时钟 */ 
    { 
        if(TCB[i].OSTCBDly ) 
        { 
            TCB[i].OSTCBDly --; 
            if(TCB[i].OSTCBDly == 0) /* 当任务时钟到时,必须是由定时
            器减时的才行*/ 
            { 
            	OSSetPrioRdy(i); /* 使任务可以重新运行 */ 
            } 
        } 
    } 
    rSRCPND |= BIT_TIMER0; 
    rINTPND |= BIT_TIMER0; 
}

系统自身创建了一个空闲任务,并设它为最低优先级,当系统没有任何任务就绪时,则运行这个任务,让 CPU“有事可干”。用户程序可以在这个任务中加入一些“无关紧要”的功能,如统计 CPU 使用率等。

void IdleTask(void) 
{ 
    IdleCount = 0; 
    while(1) 
    { 
   	 	IdleCount++; 
    	//Uart_Printf("IdleCount %d\n",IdleCount); 
    } 
}

多任务除了需要优先级还需要什么?

只有一个 CPU,如何在同一时间实现多个独立程序的运行?要实现多任务,条件是每个任务互相独立。人如何才能独立,有自己的私有财产。任务也一样,如果一个任务有自己的 CPU,堆栈,程序代码,数据存储区,那这个任务就是一个独立的任务

下面我们来看看是任务是如何“独立”的。

任务程序怎么写的?

void task ( ) 
{ 
     //initialize 代码
    while(1) 
    { 
    	//your code 
    } 
}

每个任务的程序代码与函数一样,与 51 的裸奔程序一样,每个任务都是一个大循环。

如果一个任务正在运行某个公共函数时(如 Printf),被另一个高优先级的任务抢占,那么当这个高优先级的任务也调用同一个公共函数时,极有可能破坏原任务的数据。因为两个任务可能共用一套数据。为了防止这种情况发生,常采用两种措施:可重入设计和互斥调用。

可重入函数中所有的变量均为局部变量,局部变量在调用时临时分配空间,所以不同的任务在不同的时刻调用该函数时,它们的同一个局部变量所分配的存储空间并不相同(任务私有栈中),互不干扰。另外,如果可重入函数调用了其他函数,则这些被调用的函数也必须是可重入函数。

数据存储

然后是数据存储区,由于全局变量是系统共用的,各个任务共享,不是任务私有,所以这里的数据存储区是指任务的私有变量,如何变成私有?局部变量也。编译器是把局部变量保存在栈里的,所以好办,只要任务有个私有的栈就行

临界资源

临界资源是一次仅允许一个任务使用的共享资源。每个任务中访问临界资源的那段程序称为临界区。

在多任务系统中,为保障数据的可靠性和完整性,共享资源要互斥(独占)访问,所以全局变量(只读的除外)不能同时有多个任务访问,即一个任务访问的时候不能被其他任务打断。共享资源是一种临界资源。

实现互斥(独占)访问的方法有关中断,关调度,互斥信号量,计数信号量等。

定义了如下处理临界资源访问的代码, 主要用于在进入临界区之前关闭中断,在退出临界区后恢复原来的中断状态。

INT32U cpu_sr; /* 进入临界代码区时保存 CPU 状态*/ 
/* 进入临界资源代码区 */ 
#define OS_ENTER_CRITICAL() (cpu_sr = OSCPUSaveSR()) /* 关总中断 */ 
/* 退出临界代码区 */ 
#define OS_EXIT_CRITICAL() (OSCPURestoreSR(cpu_sr)) /* 恢复原来中断状态*/
OSCPUSaveSR 
     mrs r0,CPSR 		; 保存当前中断状态,参阅汇编子程序调用 ATPCS 规则, 
    					; r0 保存传递的参数
     orr r1,r0,#NOINT 	; 关闭所有中断
     msr CPSR_c,r1 
     mov pc,lr 
 
OSCPURestoreSR 
     msr CPSR_c,r0 		; 恢复原来的中断状态
     mov pc,lr

为了不影响程序当前的中断状态,先当前的 CPSR 保存起来,退出临界区后再恢复。

注意 OS_ENTER_CRITICAL()和 OS_EXIT_CRITICAL()要成对使用。

示例:

OS_ENTER_CRITICAL();
Printf(…);//临界代码
OS_EXIT_CRITICAL();

这样,在临界区内,只要任务不主动放弃 CPU 使用权,别的任务就没有占用 CPU 的机会,相当与独占 CPU 了。临界区的代码要尽量短,因为它会使系统响应性能降低。

私有栈

私有栈的作用是存放局部变量,函数的参数,它是一个线性的空间,所以可以申请一个静态数组,把栈顶指针 SP 指向栈的数组的首元素(递增栈)或最后一个元素(递减栈)。即可打造一个人工的栈出来。

///******************任务堆栈大小定义***********/// 
#define StackSizeTask0 512 
///******************建立任务堆栈***************/// 
INT32U StackTask0[StackSizeTask0];

若在任务中使用 Printf 函数,栈要设得大些,否则栈越界溢出,会有意想不到的问题。

任务控制块

每个任务还要有记录自己栈顶指针的变量,保存在任务控制块(TCB)中。

什么是任务控制块?系统中的每个任务具有一个任务控制块,任务控制块记录任务执行的环境,这里的任务控制块比较简单,只包含了任务的堆栈指针和任务延时节拍数。

struct TaskCtrBlock /* 任务控制块数据结构 */ 
{ 
    INT32U OSTCBStkPtr; /* 保存任务的堆栈顶 */ 
    INT32U OSTCBDly; /* 任务延时时钟 */ 
}; 

struct TaskCtrBlock TCB[OS_TASKS + 1]; /* 定义任务控制块 */

任务控制块是任务的身份证。它把任务的程序与数据联系起来,找到它就可以得到任务的所有资源。

这里把 OSTCBStkPtr 放在结构体的最前面是有原因的,目的是使得在汇编中访问这个变量比较容易。因为结构体的地址就是它的首元素的地址,要在汇编中访问 OSTCBStkPtr这个变量,只需取得结构体的地址即可。

在 C 语言中不能直接实现如 TCB. TCB[OSPrioCur].OSTCBStkPtr = SP 的功能,只能用汇编语言完成。

定义如下指向结构体的指针变量:

struct TaskCtrBlock *p_OSTCBCur ; /* 指向当前任务控制块的指针 */

在 C 语言中,先让 p_OSTCBCur 指向当前运行任务的 TCB。

p_OSTCBCur = &TCB[OSPrioCur]; /* 目的是在汇编中引用任务的 TCB 地址取得栈顶指针 */

这样,运行下面的汇编代码即可把当前任务的堆顶指针取出到 SP 中。

SaveSPToCurTcb 			; 保存当前任务的堆顶指针到它的
     ldr r4,=p_OSTCBCur ; 取出当前任务的 PCB 地址
     ldr r5,[r4] 
     str sp,[r5] 		; 保存当前任务的堆顶指针到它的
                        ; TCB(因为 TaskCtrBlock 地址亦即
                        ; OSTCBStkPtr 的地址)

任务切换

最后来看看任务是如何“拥有”自己的 CPU 的。只有一个 CPU,各个任务共享,轮流使用。如何才能实现?我们先来看看中断的过程,当中断来临时,CPU 把当前程序的运行地址,寄存器等现场数据保存起来(一般保存在栈里),然后跳到中断服务程序执行。待执行完毕,再把先前保存的数据装回 CPU 又回到原来的程序执行。这样就实现了两个不同程序的交叉运行。

借鉴这种思想不就能实现多任务了吗?模仿中断的过程就可以实现任务切换运行。

任务切换时把当前任务的现场数据保存在自己的任务栈里面,再把待运行的任务的数据从自己的任务栈装载到 CPU 中,改变 CPU 的 PC,SP,寄存器等

可以说,任务的切换是任务运行环境的切换。而任务的运行环境保存在任务栈中,也就是说,任务切换的关键是把任务的私有堆栈指针赋予处理器的堆栈指针 SP

程序调用下面函数进行任务切换:

void OSSched (void) 
{ 
    OS_ENTER_CRITICAL(); 
    OSGetHighRdy(); /* 找出就绪表中优先级最高的任务 */ 
    if(OSPrioHighRdy != OSPrioCur) /* 如果不是当前运行的任务,进行任务调度 */ 
    { 
        p_OSTCBCur = &TCB[OSPrioCur]; /* 目的是在汇编中引用任务的 TCB 地址取得栈顶指针 */ 
        p_OSTCBHighRdy = &TCB[OSPrioHighRdy]; 
        OSPrioCur = OSPrioHighRdy; /* 更新 OSPrioCur */ 
        OS_TASK_SW(); /* 调度任务 */ 
    } 
    OS_EXIT_CRITICAL(); 
}
OS_TASK_SW 						; 任务级的任务切换 
 								; 
        stmfd sp!,{lr} 			; PC 入栈
        stmfd sp!,{r0-r12,lr} 	; r0-r12,lr 入栈
PUAH_PSR 
        mrs r4,cpsr 
        stmfd sp!,{r4} 			; cpsr 入栈
SaveSPToCurTcb 					; 保存当前任务的堆顶指针到它的 TCB. 
        						; TCB[OSPrioCur].OSTCBStkPtr = SP; 
         ldr r4,=p_OSTCBCur 	; 取出当前任务的 PCB 地址
         ldr r5,[r4] 
         str sp,[r5] 			; 保存当前任务的堆顶指针到它的 TCB(因为
       							 ;TaskCtrBlock 地址亦即 OSTCBStkPtr 的地址)
 
GetHigTcbSP 					; 取出更高优先级任务的堆顶指针到 SP , 
        						; SP = TCB[OSPrioCur].OSTCBStkPtr 
         ldr r6,=p_OSTCBHighRdy ; 取出更高优先级就绪任务的 PCB 的地址
         ldr r6,[r6] 
         ldr sp,[r6] 			; 取出更高优先级任务的堆顶指针到 SP 
         b POP_ALL 				; 根据设定的栈结构顺序出栈

如何建立任务?

OSTaskCreate(Task0,&StackTask0[StackSizeTask0 - 1],PrioTask0); // 创建一个任务

任务未运行或被剥夺运行权后,它的运行环境以一定的结构保存在私有栈中。这个规定的顺序是十分重要的,以后任务切换时都要按这个顺序入栈出栈。建立任务时栈结构的初始化如下:

上面函数的作用就是创建一个任务。它接收三个参数,分别是任务的入口地址,任务堆栈的首地址和任务的优先级。

调用本函数后,系统会根据用户给出的参数初始化任务栈,并把栈顶指针保存到任务控制块中,在任务就绪表标记该任务为就绪状态。最后返回,这样一个任务就创建成功了。

可见,初始化后的任务堆栈空间由高到低将依次保存着 PC,LR,R12…R0,CPSR。当一个任务将要运行时,便通过取得它的堆栈指针(保存在任务控制块中)将这些寄存器出栈装入 CPU 相应的位置即可。

挂起/恢复任务

挂起

通过 OSTaskSuspend()可以主动挂起一个任务。OSTaskSuspend()会把任务从任务就绪表中移出,最后重新启动系统调度。这个函数可以挂起任务本身也可以挂起其他任务。

void OSTaskSuspend(INT8U prio) 
{ 
    OS_ENTER_CRITICAL(); 
    TCB[prio].OSTCBDly = 0; 
    OSDelPrioRdy(prio); /* 从任务就绪表上去除标志位 */ 
    OS_EXIT_CRITICAL(); 
     
    if(OSPrioCur == prio) /* 当要挂起的任务为当前任务 */ 
    { 
    	OSSched(); /* 重新调度 */ 
    } 
}

恢复

可以让被 OSTaskSuspend 或 OSTimeDly 挂起的任务恢复就绪态,然后进行任务调度。

void OSTaskResume(INT8U prio) 
{ 
    OS_ENTER_CRITICAL(); 
    OSSetPrioRdy(prio); /* 从任务就绪表上重置标志位 */ 
    TCB[prio].OSTCBDly = 0; /* 将时间计时设为 0,延时到 */ 
    OS_EXIT_CRITICAL(); 
     
    if(OSPrioCur > prio) /* 当前任务的优先级低于重置位的任务的优先级 */ 
    { 
    	OSSched(); /* 重新调度 */ 
    } 
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/149963.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AtCoder Beginner Contest 284(A~E)

比赛名称&#xff1a;AtCoder Beginner Contest 284 比赛链接&#xff1a;AtCoder Beginner Contest 284 A - Sequence of Strings 输入若干字符串&#xff0c;再把这些字符串按输入顺序倒序输出 #include <bits/stdc.h> using namespace std; signed main() {ios::sy…

年终盘点(二)丨2022计讯物联荣誉资质大盘点

峥嵘岁月&#xff0c;奋力前行。2022年&#xff0c;计讯物联积极发扬实干精神&#xff0c;聚力做强做精做专物联网产业&#xff0c;全面助力数字化转型升级&#xff0c;以硬核的实力揽获多项殊荣。 每一项荣誉的背后是计讯领导的的正确指导与全力支持&#xff0c;更是全体计讯人…

综合项目 旅游网【2. 优化servlet】没有指定的js文件读不到文件 错误

优化servlet目的减少Servlet的数量&#xff0c;现在是一个功能一个Servlet&#xff0c;将其优化为一个模块一个Servlet&#xff0c;相当于在数据库中一张表对应一个Servlet&#xff0c;在Servlet中提供不同的方法&#xff0c;完成用户的请求。如何解决测试时控制台中文乱码&…

VS2019+Opencv3.4+Win10配置详解

一.下载opencv 官网&#xff1a;Releases - OpenCV 不同版本vs对应不同版本的opencv,其中高版本vs可以配置低版本vc&#xff0c;低版本不能配置高版本vc。 windows系统直接下载Windows版本就可以&#xff08;下载的文件是一个exe文件&#xff0c;运行相当于解压缩&#xff0…

1143汉诺塔

题目描述汉诺塔问题是这样的&#xff1a;有3根柱子A,B,C&#xff0c;其中A柱上有64个盘子&#xff0c;盘子大小不等&#xff0c;大的在下&#xff0c;小的在上。要求把这64个盘子从A柱移到C柱上&#xff0c;在移动过程中可以借助B柱&#xff0c;每次只允许移动一个盘子&#xf…

什么是 Java 泛型?怎样使用 Java 泛型?

目录 1、为什么使用泛型&#xff1f; 2、什么是泛型类&#xff1f;如何定义一个泛型类&#xff1f; 泛型的命名约定 3、什么是泛型方法&#xff1f;如何定义一个泛型方法&#xff1f; 4、什么是有界类型参数&#xff1f;如何定义有界类型参数&#xff1f; &#xff08;1&…

Maven高级-私服

分模块合作开发 9.2)Nexus Nexus是Sonatype公司的一款maven私服产品 下载地址&#xff1a;https://help.sonatype.com/repomanager3/download Nexus*安装、启动与配置** 启动服务器&#xff08;命令行启动&#xff09; nexus.exe /run nexus访问服务器&#xff08;默认端口…

linux安装部署vsftpd

yum直接安装yum -y install vsftpd ftp 创建新用户&#xff1a;ftpd更新ftpd密码&#xff1a;echo "123456" |passwd --stdin ftpd创建ftp目录&#xff1a;mkdir -p /home/ftpd/test授权&#xff1a;chown -R ftpd:ftpd /home/ftpd/testchmod 777 -R /home/ftpd/test…

某程序员哀叹:最近阳的人越来越多,面对员工们纷纷倒下,公司领导公然宣称“发烧请病假不等于在家睡大觉,再不回复工作就滚蛋”...

最近阳的人越来越多&#xff0c;面对员工们纷纷倒下&#xff0c;有的公司通情达理&#xff0c;有的公司却开始“不当人”了。一位网友曝光公司领导在群里所有人&#xff0c;称“发烧请病假不意味着在家睡大觉&#xff0c;啥也不管&#xff0c;联系不上&#xff0c;安排不予响应…

【SAP Hana】SAP HANA SQL 基础教程

SAP HANA SQL 基础教程1、SQL 标准简介2、HANA STUDIO 的安装3、HANA STUDIO 的设置4、HANA SQL 基础教程&#xff08;1&#xff09;查看表数据&#xff08;2&#xff09;查看表结构&#xff08;3&#xff09;SELECT&#xff08;4&#xff09;WHERE&#xff08;5&#xff09;WH…

B站直播带货,带货直播数据如何查看?

随着时代发展&#xff0c;直播电商带货也是越来越火&#xff0c;在这个直播带货火热期&#xff0c;B站也是当仁不让的加入到直播带货行业中&#xff0c;在今年双11中&#xff0c;B站第一次参加双十一直播电商混战&#xff0c;但是并未像其他电商平台一般&#xff0c;趁双十一流…

【自学Python】Python浮点型(float)

Python浮点型(float) Python浮点型(float)教程 Python 浮点型数值用于保存带小数点的数值。Python 的浮点数有两种表示形式&#xff0c;即十进制形式和科学计数法形式。 Python浮点型(float)详解 十进制形式 Python 最常见的浮点型数就是十进制形式的浮点型数。Python 中的…

Java-类加载

静态加载和动态加载 4种加载时机&#xff0c;只有反射是动态加载 静态加载举个例子 Cat父类Animal mao是Cat类独有方法 Animal anew Cat(); a.mao();//编译看左边 //左边类型为Animal&#xff08;会加载Animal类&#xff0c;编译时进行加载叫静态加载&#xff09; //然后加载…

OpenShift 容器平台企业版 OCP 4.11.9 部署(基于KVM,CentOS)

参考&#xff1a; 阿里云上Openshift-4.10.5搭建 OpenShift4.8在oVirt下的自动化安装 红帽OpenShift安装部署-阿里云帮助中心 安装配置操作节点&#xff08;Operator&#xff09;&#xff0c;并获取OCP离线安装文件 OCP安装定制文件准备_frank0521的博客-CSDN博客 第 23 章…

【Java数据结构与算法】第二十一章 元组

【Java数据结构与算法】第二十一章 元组 文章目录【Java数据结构与算法】第二十一章 元组1.概念2.自定义元组3.第三方Jar包1.概念 元组&#xff08;Tuple&#xff09;是一种数据结构&#xff0c;可以存放多个元素&#xff0c;每个元素的数据类型可以不同。用List与Tuple类比&a…

深入了解Netty,这一篇就够了

一、Netty简介 Netty是由JBOSS提供的一个java开源框架&#xff0c;现为 Github上的独立项目。Netty提供异步的、事件驱动的网络应用程序框架和工具&#xff0c;用以快速开发高性能、高可靠性的网络服务器和客户端程序。 也就是说&#xff0c;Netty 是一个基于NIO的客户、服务器…

微分方程(人口预测+传染病模型)

一、定义 微分方程&#xff1a;含导数或微分的方程 微分方程的阶数&#xff1a;所含导数或微分的最高阶数&#xff0c;如y’’’2y’’-2x0是三阶微分方程 微分方程的解&#xff1a;使得微分方程成立的函数 例如y’-2x0的解可以为x或者x1 微分方程的通解和特解&#xff1a;特…

【pat】分而治之【图】

分而治之&#xff0c;各个击破是兵家常用的策略之一。在战争中&#xff0c;我们希望首先攻下敌方的部分城市&#xff0c;使其剩余的城市变成孤立无援&#xff0c;然后再分头各个击破。为此参谋部提供了若干打击方案。本题就请你编写程序&#xff0c;判断每个方案的可行性。输入…

MySQL触发器相关知识

1、什么是触发器 触发器&#xff08;trigger&#xff09;是mysql的数据库对象之一&#xff0c;是一种与表操作有关的数据库对象&#xff0c;当触发器所在表上出现指定事件时&#xff08;这些事件包括insert、update、delete三种&#xff09;&#xff0c;将调用该对象&#xff0…

2023年安装Flutter开发环境_在C盘空间占用空间

2023年安装Flutter开发环境&#xff0c;C盘空间还剩多少&#xff1f; 1&#xff1a;Flutter开发对磁盘空间的要求 2&#xff1a;其余日常辅助软件安装D盘&#xff08;占用8GB&#xff09; 3&#xff1a;消耗时间&#xff08;3天–网络有时会中断&#xff09;–【劝退提示】 安…