FreeRTOS-任务创建源码分析

2023-05-16

任务创建是FreeRTOS系统启动的第一个步骤,前面在启动调度器的时候先创建了空闲任务,然后再由调度器跳到任务里面去执行。任务创建函数里面做了很多的工作,先会为任务堆栈和任务控制块分配内存并初始化它们,然后将任务添加到就绪列表里面,等待调度器来调用。
任务创建的接口定义如下:

BaseType_t xTaskCreate(	TaskFunction_t pxTaskCode,
							const char * const pcName,	
							const configSTACK_DEPTH_TYPE usStackDepth,
							void * const pvParameters,
							UBaseType_t uxPriority,
							TaskHandle_t * const pxCreatedTask )
	

形参代表的意义如下:

pxTaskCode		任务函数入口地址		
pcName			任务名称
usStackDepth	任务堆栈大小	
pvParameters	传递给任务函数的参数
uxPriority		任务优先级
pxCreatedTask	任务控制块句柄

源码分析:

BaseType_t xTaskCreate(	TaskFunction_t pxTaskCode,
							const char * const pcName,		
							const configSTACK_DEPTH_TYPE usStackDepth,
							void * const pvParameters,
							UBaseType_t uxPriority,
							TaskHandle_t * const pxCreatedTask )
	{
	TCB_t *pxNewTCB;
	BaseType_t xReturn;
	
		#if( portSTACK_GROWTH > 0 )
		{
			/* 如果堆栈向上增长,则先分配任务控制块 */
			pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );

			if( pxNewTCB != NULL )
			{
				/* 分配任务堆栈,具体内存分配实现在内存管理章节分析 */
				pxNewTCB->pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); 

				if( pxNewTCB->pxStack == NULL )
				{
					/* 堆栈分配失败就释放之前分配的任务控制块内存 */
					vPortFree( pxNewTCB );
					pxNewTCB = NULL;
				}
			}
		}
		#else
		{
		StackType_t *pxStack;

			/* 如果堆栈向下增长,则先分配任务堆栈 */
			pxStack = pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) );

			if( pxStack != NULL )
			{
				/* 堆栈分配成功后分配任务控制块空间 */
				pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
				if( pxNewTCB != NULL )
				{
					/* 初始化任务控制块的堆栈指针 */
					pxNewTCB->pxStack = pxStack;
				}
				else
				{
					/* 任务控制块分配失败就释放之间分配的任务堆栈内存 */
					vPortFree( pxStack );
				}
			}
			else
			{
				pxNewTCB = NULL;
			}
		}
		#endif

		if( pxNewTCB != NULL )
		{
			#if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) 
			{
				/* 可以静态也可以动态分配的情况下默认使用动态分配内存 */
				pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB;
			}
			#endif 

			/* 初始化任务 */
			prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );
			/* 把列表项添加到就绪列表中,优先级高过原来的话就请求一次任务切换 */
			prvAddNewTaskToReadyList( pxNewTCB );
			xReturn = pdPASS;
		}
		else
		{
			xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
		}

		return xReturn;
	}

上面任务创建的过程中先分配任务堆栈和任务控制块,然后初始化任务并把任务添加到就绪列表,分别调用了如下接口来实现

pvPortMalloc
vPortFree
prvInitialiseNewTask
prvAddNewTaskToReadyList

关于内存分配和释放的接口等后面内存管理章节再进行分析,这里分析下任务初始化和添加到就绪列表的过程。
下面任务初始化源码去掉了部分宏定义的代码:

static void prvInitialiseNewTask( 	TaskFunction_t pxTaskCode,
									const char * const pcName,
									const uint32_t ulStackDepth,
									void * const pvParameters,
									UBaseType_t uxPriority,
									TaskHandle_t * const pxCreatedTask,
									TCB_t *pxNewTCB,
									const MemoryRegion_t * const xRegions )
{
StackType_t *pxTopOfStack;
UBaseType_t x;

	configASSERT( pcName );

	#if( portSTACK_GROWTH < 0 )
	{
		/* 如果栈的生长方便是向下生长的话执行这段代码 */
		/* 将栈顶指针取出,减1是因为数组小标从0开始计的 */
		pxTopOfStack = &( pxNewTCB->pxStack[ ulStackDepth - ( uint32_t ) 1 ] );
		/* 将栈顶指针8字节对齐 */
		pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); 

	}
	#else/* 如果栈的生长方向是向上生长的话执行这段代码 */
	{
		pxTopOfStack = pxNewTCB->pxStack;
		/* 计算栈的结束地址 */
		pxNewTCB->pxEndOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );
	}
	#endif 

	for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ )
	{
		/* 初始化任务控制块成员任务名称 */
		pxNewTCB->pcTaskName[ x ] = pcName[ x ];
		/* 等于0x00代表名字获取结束 */
		if( pcName[ x ] == ( char ) 0x00 )
		{
			break;
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}

	/* 避免名字过长,强制裁剪添加字符串结束符'\0' */
	pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0';

	if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES )
	{
		/* 优先级最高不能超过configMAX_PRIORITIES (这里定义的是5,即最高优先级不超过4) */
		uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}

	/* 初始化任务控制块成员任务优先级,最大不能超过4 */
	pxNewTCB->uxPriority = uxPriority;
	#if ( configUSE_MUTEXES == 1 )
	{
		/* 使能了互斥信号量功能 */
		pxNewTCB->uxBasePriority = uxPriority;
		pxNewTCB->uxMutexesHeld = 0;
	}
	#endif 

	/* 初始化状态列表项,前面章节已经分析过了 */
	vListInitialiseItem( &( pxNewTCB->xStateListItem ) );
	/* 初始化事件列表项 */
	vListInitialiseItem( &( pxNewTCB->xEventListItem ) );

	/* 设置状态列表项属于当前任务控制块 */
	listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB );

	/* 设置事件列表项值(xItemValue越大,优先级越小,因为插入是按xItemValue升序排序的,先插入的优先级高先执行,所以需要做个减法来反转) */
	listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority );
	/* 设置事件列表项属于当前任务控制块 */
	listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB );

	/* 使能临界区嵌套 */
	#if ( portCRITICAL_NESTING_IN_TCB == 1 )
	{
		pxNewTCB->uxCriticalNesting = ( UBaseType_t ) 0U;
	}
	#endif 

	/* 使能任务标签功能 */
	#if ( configUSE_APPLICATION_TASK_TAG == 1 )
	{
		pxNewTCB->pxTaskTag = NULL;
	}
	#endif

	/* 使能时间统计功能 */
	#if ( configGENERATE_RUN_TIME_STATS == 1 )
	{
		pxNewTCB->ulRunTimeCounter = 0UL;
	}
	#endif

	/* 使能MPU相关 */
	#if ( portUSING_MPU_WRAPPERS == 1 )
	{
		vPortStoreTaskMPUSettings( &( pxNewTCB->xMPUSettings ), xRegions, pxNewTCB->pxStack, ulStackDepth );
	}
	#else
	{
		/* 避免报错 */
		( void ) xRegions;
	}
	#endif

	/* 如果使能了相应的宏,初始化线程本地存储指针 */
	#if( configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0 )
	{
		for( x = 0; x < ( UBaseType_t ) configNUM_THREAD_LOCAL_STORAGE_POINTERS; x++ )
		{
			pxNewTCB->pvThreadLocalStoragePointers[ x ] = NULL;
		}
	}
	#endif

	#if ( configUSE_TASK_NOTIFICATIONS == 1 )
	{
		/* 初始化通知状态为没等待 */
		pxNewTCB->ulNotifiedValue = 0;
		pxNewTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION;
	}
	#endif

	/* 使能NEWLIB */
	#if ( configUSE_NEWLIB_REENTRANT == 1 )
	{
		_REENT_INIT_PTR( ( &( pxNewTCB->xNewLib_reent ) ) );
	}
	#endif

	/* 初始化任务堆栈(xpsr、pc、lr、r0-r12),在下面分析 */
	pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );

	if( pxCreatedTask != NULL )
	{
		/* 返回任务控制块指针 */
		*pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}
}

上面设置列表项的两个函数定义如下:

#define listSET_LIST_ITEM_OWNER( pxListItem, pxOwner )		( ( pxListItem )->pvOwner = ( void * ) ( pxOwner ) )
#define listSET_LIST_ITEM_VALUE( pxListItem, xValue )	( ( pxListItem )->xItemValue = ( xValue ) )

初始化任务堆栈的函数定义如下,如果不了解寄存器的具体作用建议参考一下Cortex-M3权威手册:

StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
{
	/* xPSR程序状态寄存器,bit24为1表示使用Thumb指令 */
	/* #define portINITIAL_XPSR			( 0x01000000 ) */
	pxTopOfStack--; 
	*pxTopOfStack = portINITIAL_XPSR;	
	
	/* PC程序计数器,向PC写数据会引发一次程序分支 */
	pxTopOfStack--;
	*pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK;	
	
	/* LR连接寄存器 */
	pxTopOfStack--;
	*pxTopOfStack = ( StackType_t ) prvTaskExitError;

	/* 通用目的寄存器R0-R12 */
	pxTopOfStack -= 5;	/* R12, R3, R2 and R1. */
	*pxTopOfStack = ( StackType_t ) pvParameters;	/* R0 */
	pxTopOfStack -= 8;	/* R11, R10, R9, R8, R7, R6, R5 and R4. */

	/* 返回栈剩余空间的起始地址 */
	return pxTopOfStack;
}

在初始化完任务后会将任务加入到就绪列表,函数实现如下:

static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB )
{
	/* 进入临界区 */
	taskENTER_CRITICAL();
	{
		/* 当前任务数量递增 */
		uxCurrentNumberOfTasks++;
		if( pxCurrentTCB == NULL )
		{
			/* 如果这是第一个创建的任务执行下面的内容 */
			pxCurrentTCB = pxNewTCB;

			if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 )
			{
				/* 初始化所有任务列表 */
				prvInitialiseTaskLists();
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		else	/* 如果之前已经初始化过任务列表了 */
		{
			if( xSchedulerRunning == pdFALSE )
			{
				/* 如果调度器还未开启 */
				if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority )
				{
					/* 新任务的任务优先级大过之前创建任务的优先级,则修改一下 */
					pxCurrentTCB = pxNewTCB;
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}

		/* 总任务数量递增 */
		uxTaskNumber++;

		/* 启动了追踪或调试 */
		#if ( configUSE_TRACE_FACILITY == 1 )
		{
			pxNewTCB->uxTCBNumber = uxTaskNumber;
		}
		#endif
		traceTASK_CREATE( pxNewTCB );

		/* 将新创建的任务添加到就绪列表中 */
		prvAddTaskToReadyList( pxNewTCB );

		portSETUP_TCB( pxNewTCB );
	}
	taskEXIT_CRITICAL(); /* 退出临界区 */

	if( xSchedulerRunning != pdFALSE )
	{
		/* 如果调度器已经开启 */
		if( pxCurrentTCB->uxPriority < pxNewTCB->uxPriority )
		{
			/* 如果新的任务优先级高于当前任务的优先级则切换一次任务 */
			taskYIELD_IF_USING_PREEMPTION();
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}
}

将任务添加到就绪列表实际上是一个宏定义

#define prvAddTaskToReadyList( pxTCB )																\
	traceMOVED_TASK_TO_READY_STATE( pxTCB );														\
	taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority );												\
	vListInsertEnd( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) ); \
	tracePOST_MOVED_TASK_TO_READY_STATE( pxTCB )

taskRECORD_READY_PRIORITY 用于修改当前已就绪的任务里面优先级最高的那个任务

#define taskRECORD_READY_PRIORITY( uxPriority )														\
	{																									\
		if( ( uxPriority ) > uxTopReadyPriority )														\
		{																								\
			uxTopReadyPriority = ( uxPriority );														\
		}																								\
	} /* taskRECORD_READY_PRIORITY */

vListInsertEnd 将任务插入到相应优先级就绪列表中,这个函数前面列表和列表项章节已经分析了,这里就不放解析了。上面在将任务加入到就绪列表之前进去了临界区,进去临界区(taskENTER_CRITICAL)主要是为能够互斥访问共享资源,在访问完后需要退出临界区(taskEXIT_CRITICAL),它们的实现如下:

#define taskENTER_CRITICAL()		portENTER_CRITICAL()
#define portENTER_CRITICAL()					vPortEnterCritical()
void vPortEnterCritical( void )
{
	/* 进去临界区实际上就是去关闭中断 */
	portDISABLE_INTERRUPTS();
	/* 变量递增,用于记录临界区的嵌套次数 */
	uxCriticalNesting++;
	
	if( uxCriticalNesting == 1 )
	{
		configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );
	}
}

#define portDISABLE_INTERRUPTS()				vPortRaiseBASEPRI()
static portFORCE_INLINE void vPortRaiseBASEPRI( void )
{
/* 定义BASEPRI这个特殊功能寄存器的值,BASEPRI即除能所有优先级不高于某个具体数值的中断*/
/* #define configMAX_SYSCALL_INTERRUPT_PRIORITY 	191  */
uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;

	__asm
	{
		/* (msr:写通用寄存器的值到特殊功能寄存器)中断优先级号大于191的都会被关闭 */
		msr basepri, ulNewBASEPRI	
		dsb	(数据同步隔离(与流水线、 MPU 和 cache 等有关))
		isb (指令同步隔离(与流水线和 MPU 等有关))
	}
}
#define taskEXIT_CRITICAL()			portEXIT_CRITICAL()
#define portEXIT_CRITICAL()						vPortExitCritical()
void vPortExitCritical( void )
{
	configASSERT( uxCriticalNesting );
	/* 变量递减,用于记录临界区的嵌套次数 */
	uxCriticalNesting--;
	if( uxCriticalNesting == 0 )
	{
		portENABLE_INTERRUPTS();
	}
}
#define portENABLE_INTERRUPTS()					vPortSetBASEPRI( 0 )
static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI )
{
	__asm
	{
		/* (msr:写通用寄存器的值到特殊功能寄存器)设置为0即打开所有中断 */
		msr basepri, ulBASEPRI
	}
}

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

FreeRTOS-任务创建源码分析 的相关文章

  • 心跳跟随的心形灯(STM32(HAL)+WS2812+MAX30102)

    文章目录 前言 介绍 系统框架 原项目地址 本项目开发开源地址 硬件PCB 软件功能 详细内容 硬件外壳制作 WS2812级联及控制 MAX30102血氧传感器 0 96OLED FreeRTOS 效果视频 总结 前言 在好几年前 我好像就
  • FreeRTOS例程4-串口DMA收发不定长数据

    FreeRTOS例程4 串口DMA收发不定长数据 知乎 zhihu com
  • STM32CubeMX+FreeRTOS学习笔记(一)

    嵌入式实时操作系统FreeRTOS 基本概述 在嵌入式领域当中 实时操作系统的应用越来越广泛了 目前嵌入式操作系统种类很多 例如 Clinux C OS II C OS III FreeRTOS RT Thread等等 这篇文章所记录的就是
  • FreeRTOS软件定时器创建、复位、开始和停止(备忘)

    目录 一 简介 1 1 开发环境 1 2 摘要 二 STM32CubeIDE配置 三 创建定时器 3 1 头文件声明 3 2 工程文件定义 3 3 创建定时器 3 4 开启 复位 和关闭定时器 四 定时器回调函数 一 简介 1 1 开发环境
  • 解决错误“ #error “include FreeRTOS.h“ must appear in source files before “include event_groups.““例子分享

    今天来给大家分享一下 关于之前自己在学习FreeRTOS过程中遇到的一个错误提示 话不多说 我们直接来看 错误分析 首先 我们看一下错误的提示 error 35 error directive include FreeRTOS h must
  • FreeRTOS简述和移植文档

    FreeRTOS简述和移植文档 文章目录 FreeRTOS简述和移植文档 1 前言 2 FreeRTOS简述 1 概述 2 实现 3 主要特色 4 支持平台 3 移植FreeRTOS 4 最后 1 前言 目前由于IOT的飞速发展 针对单片机
  • 【FreeRTOS】多任务创建

    作者主页 凉开水白菜 作者简介 共同学习 互相监督 热于分享 多加讨论 一起进步 专栏资料 https pan baidu com s 1nc1rfyLiMyw6ZhxiZ1Cumg pwd free 点赞 收藏 再看 养成习惯 订阅的粉丝
  • FreeRTOS打印任务对CPU的占有率

    1 配置RTOS 1 打开RTOS Config Parameter 找到Run Time And Task States gathering related definitions 使能GENERATE RUN TIME STATS US
  • FreeRTOS+CubeMX系列第一篇——初识FreeRTOS

    文章目录 一 关于FreeRTOS 二 FreeRTOS的特点 三 如何在CubeMX上配置FreeRTOS 四 FreeRTOS文档资料 五 同系列博客 一 关于FreeRTOS 1 什么是FreeRTOS FreeRTOS是一个迷你的实
  • Error: L6218E: Undefined symbol vApplicationGetIdleTaskMemory (referred from tasks.o).

    我用的是F103ZET6的板子 移植成功后 编译出现两个错误是关于stm32f10x it c 里 void SVC Handler void void PendSV Handler void 两个函数的占用问题 随后编译出现以下两个问题
  • FreeRTOS笔记(一)简介

    这个笔记主要依据韦东山freertos快速入门系列记录 感谢韦东山老师的总结 什么是实时操作系统 操作系统是一个控制程序 负责协调分配计算资源和内存资源给不同的应用程序使用 并防止系统出现故障 操作系统通过一个调度算法和内存管理算法尽可能把
  • 【FreeRTOS 事件】任务通知事件

    普通任务通知事件创建创建及运行 参阅安富莱电子demo define BIT 0 1 lt lt 0 define BIT 1 1 lt lt 1 static TaskHandle t xHandleTaskUserIF NULL sta
  • FreeRTOS临界段

    1 临界段 在访问共享资源时不希望被其他任务或者中断打断的代码 这段要执行的代码称为临界段代码 2 设置临界段的目的 保护共享资源 例如 全局变量 公共函数 不可重入函数 函数里面使用 了一些静态全局变量 malloc 等 保护外设的实时性
  • STM32 Freertos 添加 外部sram heap_5.c

    1 添加外部SRAM 初始化 2 添加heap 5 c 3 初始化heap 5 c 外部堆栈 Define the start address and size of the two RAM regions not used by the
  • FreeRTOS多任务调度器基础

    Cortex M4中SysTick调度器核心 Cortex M4中的中断管理 Cortex M4中影子栈指针 Cortex M4中SVC和PendSV异常 1 Cortex M4中SysTick调度器核心 systick每一次中断都会触发内
  • 当一个任务写入变量而其他任务读取该变量时,我们是否需要信号量?

    我正在研究 freeRtos 并且我有一个名为 x 的变量 现在 每秒只有一个任务正在写入该变量 而其他任务正在读取该变量值 我需要用互斥锁来保护变量吗 如果变量为 32 位或更小 并且其值是独立的并且不与任何其他变量一起解释 则不需要互斥
  • 防止GCC LTO删除函数

    我使用 GCC ARM Embedded 和 FreeRTOS FreeRTOS具有的功能vTaskSwitchContext 仅在某些情况下使用 内联汇编代码 问题是 当我使用LTO时 GCC不考虑内联汇编代码并认为该函数没有被使用 因此
  • 有可用的 FreeRTOS 解释语言库吗?

    我在一家公司工作 该公司使用 FreeRTOS 为多个设备创建固件 最近 我们对新功能的要求已经超出了我们固件工程师的工作能力 但我们现在也无力雇用任何新人 即使进行微小的更改 也需要固件人员在非常低的级别上进行修改 我一直在为 FreeR
  • C++ freeRTOS任务,非静态成员函数的无效使用

    哪里有问题 void MyClass task void pvParameter while 1 this gt update void MyClass startTask xTaskCreate this gt task Task 204
  • 小型 ARM 微控制器的 RTOS 内核之间的可量化差异 [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 有许多不同的 RTOS 可用于微控制器 我专门寻找支持 ARM Cortex M 处理器的 RTOS 另外 我对闭源解决方案不感兴趣 试图从网站

随机推荐

  • ROS 小技巧 - OpenCV4 与 CV_Bridge 配合使用

    1 现象 ROS默认的Python版本是3 3 xff0c 但我系统安装的是OpenCV4 5 如果直接在pkg中使用cv bridge和opencv4 5就会有问题 会有一些undefined reference问题 参考资料 xff1a
  • 【做题系统】后端设计

    目录 一 设计思路 1 项目背景 2 技术栈选择 二 系统设计 1 系统结构图 2 项目结构 3 数据建模 4 数据流图 5 主要流程图 三 问题及解决办法 1 实现安全登录 访问 2 数据库中的信息安全问题 3 Mybatis plus如
  • C/C++字符串查找函数

    C C 43 43 string库 xff08 string h xff09 提供了几个字符串查找函数 xff0c 如下 xff1a memchr在指定内存里定位给定字符strchr在指定字符串里定位给定字符strcspn返回在字符串str
  • ssh命令-manpage

    SSH Section User Commands 1 Index Return to Main Contents BSD mandoc NAME ssh OpenSSH SSH 客户端 远程登录程序 总览 SYNOPSIS ssh l l
  • 一小时做出Java实战项目——飞翔的小鸟

    学姐又来啦 xff0c 今日分享一个Java实战项目 飞翔的小鸟 相信大家都玩过这个游戏 xff0c 这个游戏陪伴了我们整整一个童年 xff0c 是我们青春的回忆 飞翔的小鸟 xff0c 游戏中玩家只需通过点击方向键操纵让小鸟避开绿色管道等
  • 搭建本地仓库源

    一 如何搭建仓库源 之前讲了定制ISO的方法 xff1a 使用chroot定制系统 xff0c 但有时候我们想自定义的安装包不在上游的仓库源中 xff0c 在我们本地应该怎么办呢 xff1f 如果我们将deb包拷贝到iso目录再安装有点过于
  • 节点操作案例

    1 下拉菜单 xff08 仿微博 xff09 lt DOCTYPE html gt lt html lang 61 34 en 34 gt lt head gt lt meta charset 61 34 UTF 8 34 gt lt me
  • document获取对象的三种三方法

    Document对象中有几个常用的方法 xff0c 我们在Dom简介中提到过 说到获取JavaScript对象的方法 xff0c 最常用的可能就是getElementById了 xff0c 它是Document中最常用的获取对象的方式之一
  • 程序员,最关键的跨越是什么?做到了月薪可能翻上几番~

    黑马程序员视频库 播妞微信号 xff1a boniu236 传智播客旗下互联网资讯 学习资源免费分享平台 作为一名程序员 xff0c 最关键的跨越是什么 xff1f 从普通程序员进阶为熟练开发者 xff0c 从熟练开发者跃升到技术专家或架构
  • 黑马程序员:3分钟带你读懂C/C++学习路线

    随着互联网及互联网 43 深入蓬勃的发展 xff0c 经过40余年的时间洗礼 xff0c C C 43 43 俨然已成为一门贵族语言 xff0c 出色的性能使之成为高级语言中的性能王者 而在今天 xff0c 它又扮演着什么样重要的角色呢 x
  • 数据归一化

    原文链接 xff1a 从公式出发 xff1a 什么是模型收敛的有效方法 xff1f 大家好 xff0c 我是泰哥 数据归一化在模型收敛中起着至关重要的作用 xff0c 从经典机器学习到深度学习的数据归一化方法是如何一步步演变的呢 xff1f
  • 【Python面试】 说说Python变量、函数、类的命名规则?

    最近公众号新增加了一个栏目 xff0c 就是每天给大家解答一道Python常见的面试题 xff0c 反正每天不贪多 xff0c 一天一题 xff0c 正好合适 xff0c 只希望这个面试栏目 xff0c 给那些正在准备面试的同学 xff0c
  • ​LeetCode刷题实战46:全排列

    算法的重要性 xff0c 我就不多说了吧 xff0c 想去大厂 xff0c 就必须要经过基础知识和业务逻辑面试 43 算法面试 所以 xff0c 为了提高大家的算法能力 xff0c 这个公众号后续每天带大家做一道算法题 xff0c 题目就从
  • Android硬件访问服务-Service

    Android有四大组件 xff1a 一 Activity 二 Service 三 Broadcast Receiver 四 Content Provider Service是Android中一个类 xff0c 它是Android四大组件之
  • android6.0第三方APP获得设备节点的访问权限

    之前使用android4 4的系统进行开发时 system app xff08 系统自带APP xff09 目录下的 app 可以直接访问 dev 目录下的设备节点 xff0c Android 5 0 以后 xff0c 因为采取了 SEAn
  • U-boot取消或修改启动延时bootdelay

    在我们的实际项目中都希望uboot尽量能够快速启动 xff0c 这就涉及到uboot的裁剪工作 xff0c 由于裁剪的工作量和内容比较多 xff0c 这里暂不描述 但是uboot有个启动延时bootdelay xff0c 在我们进入linu
  • uboot启动分析第一阶段(start.S)

    前面分析了启动脚本 Makefile mkconfig xff0c 接下来就是uboot的start S这个启动代码了 xff0c 下面是本章的平台介绍 xff1a 单板 xff1a 迅为4412开发板 Exynos 4412 SDRAM
  • Android使用串口(基于android-serialport-api)

    运行平台 xff1a CPU xff1a 全志V40 Android版本 xff1a 6 0 1 关于安卓设备上使用串口 xff0c 谷歌官方在github上有提供代码实例 xff0c 里面有JNI的代码和串口API的java文件 xff0
  • FreeRTOS-启动任务调度器源码分析

    本章基于FreeRTOS的启动任务调度器源码分析 xff0c 后续将会上传其它我对FreeRTOS的源码分析过程及理解 xff0c 首先来认识一下任务调度器 任务调度器 xff1a 任务调度器主要用于实现任务的切换 xff0c 任务并不是我
  • FreeRTOS-任务创建源码分析

    任务创建是FreeRTOS系统启动的第一个步骤 xff0c 前面在启动调度器的时候先创建了空闲任务 xff0c 然后再由调度器跳到任务里面去执行 任务创建函数里面做了很多的工作 xff0c 先会为任务堆栈和任务控制块分配内存并初始化它们 x