co_routine.cpp分析

C++20中的协程(Coroutine) 目录C++20中的协程(Coroutine)那么,什么是协程?那么这么好用的协程,是不是只要C++20一推出,我们加上一个关键字就能直接把异步调用转化为同步调用呢?协程函数和Awaitable类那么,到底要如何使用协程呢?写在最后 C++20中的协程(Coroutine) 从2017年开始, 协程(Coroutine)的概念就开始被建议加入C++20的标准中了,并已经开始有人对C++20协程的提案进行了介绍。1事实上,协程的概念在很早就出现了,甚至其他语言(JS,Python,C#等)早就已经支持了协程。 阅读详情

co_routine.cpp


#include "co_routine.h"
#include "co_routine_inner.h"
#include "co_epoll.h"

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <map>

#include <poll.h>
#include <sys/time.h>
#include <errno.h>

#include <assert.h>

#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <limits.h>

extern "C"
{
	extern void coctx_swap( coctx_t *,coctx_t* ) asm("coctx_swap");
};//声明coctx_swap函数,调用coctx_swap汇编代码。
using namespace std;
stCoRoutine_t *GetCurrCo( stCoRoutineEnv_t *env );//获取当前协程
struct stCoEpoll_t;//管理定时事件的结构体

struct stCoRoutineEnv_t //管理协程环境的结构体
{
	stCoRoutine_t *pCallStack[ 128 ];//调用栈,添加协程则入栈,销毁协成则出栈。
	int iCallStackSize;//栈的大小
	stCoEpoll_t *pEpoll;//指向程序中负责管理定时事件的变量

	//for copy stack log lastco and nextco
	stCoRoutine_t* pending_co;//等待的协程
	stCoRoutine_t* occupy_co;//正在运行的协程
};
//int socket(int domain, int type, int protocol);
void co_log_err( const char *fmt,... )//错误日志
{
}


#if defined( __LIBCO_RDTSCP__)
static unsigned long long counter(void) 
{
	register uint32_t lo, hi;
	register unsigned long long o;
	__asm__ __volatile__ (
			"rdtscp" : "=a"(lo), "=d"(hi)::"%rcx"
			);
	o = hi;
	o <<= 32;
	return (o | lo);

}
static unsigned long long getCpuKhz()
{
	FILE *fp = fopen("/proc/cpuinfo","r");
	if(!fp) return 1;
	char buf[4096] = {0};
	fread(buf,1,sizeof(buf),fp);
	fclose(fp);

	char *lp = strstr(buf,"cpu MHz");
	if(!lp) return 1;
	lp += strlen("cpu MHz");
	while(*lp == ' ' || *lp == '\t' || *lp == ':')
	{
		++lp;
	}

	double mhz = atof(lp);
	unsigned long long u = (unsigned long long)(mhz * 1000);
	return u;
}
#endif

static unsigned long long GetTickMS()//获取时间
{
#if defined( __LIBCO_RDTSCP__)
	static uint32_t khz = getCpuKhz();
	return counter() / khz;
#else
	struct timeval now = { 0 };
	gettimeofday( &now,NULL );
	unsigned long long u = now.tv_sec;
	u *= 1000;
	u += now.tv_usec / 1000;
	return u;
#endif
}

/* no longer use
static pid_t GetPid()
{
    static __thread pid_t pid = 0;
    static __thread pid_t tid = 0;
    if( !pid || !tid || pid != getpid() )
    {
        pid = getpid();
#if defined( __APPLE__ )
		tid = syscall( SYS_gettid );
		if( -1 == (long)tid )
		{
			tid = pid;
		}
#elif defined( __FreeBSD__ )
		syscall(SYS_thr_self, &tid);
		if( tid < 0 )
		{
			tid = pid;
		}
#else
        tid = syscall( __NR_gettid );
#endif

    }
    return tid;

}
static pid_t GetPid()
{
	char **p = (char**)pthread_self();
	return p ? *(pid_t*)(p + 18) : getpid();
}
*/
template <class T,class TLink>
void RemoveFromLink(T *ap) //把这个元素从链表中删除,这里使用了模板类,epoll cond timeout均可以使用。
{
	TLink *lst = ap->pLink;
	if(!lst) return ;
	assert( lst->head && lst->tail );

	if( ap == lst->head )
	{
		lst->head = ap->pNext;
		if(lst->head)
		{
			lst->head->pPrev = NULL;
		}
	}
	else
	{
		if(ap->pPrev)
		{
			ap->pPrev->pNext = ap->pNext;
		}
	}

	if( ap == lst->tail )
	{
		lst->tail = ap->pPrev;
		if(lst->tail)
		{
			lst->tail->pNext = NULL;
		}
	}
	else
	{
		ap->pNext->pPrev = ap->pPrev;
	}

	ap->pPrev = ap->pNext = NULL;
	ap->pLink = NULL;
}

template <class TNode,class TLink>
void inline AddTail(TLink*apLink,TNode *ap) //添加元素到链表中
{
	if( ap->pLink )
	{
		return ;
	}
	if(apLink->tail)
	{
		apLink->tail->pNext = (TNode*)ap;
		ap->pNext = NULL;
		ap->pPrev = apLink->tail;
		apLink->tail = ap;
	}
	else
	{
		apLink->head = apLink->tail = ap;
		ap->pNext = ap->pPrev = NULL;
	}
	ap->pLink = apLink;
}
template <class TNode,class TLink>
void inline PopHead( TLink*apLink )//删除链表头
{
	if( !apLink->head )
	{
		return ;
	}
	TNode *lp = apLink->head;
	if( apLink->head == apLink->tail )
	{
		apLink->head = apLink->tail = NULL;
	}
	else
	{
		apLink->head = apLink->head->pNext;
	}

	lp->pPrev = lp->pNext = NULL;
	lp->pLink = NULL;

	if( apLink->head )
	{
		apLink->head->pPrev = NULL;
	}
}

template <class TNode,class TLink>
void inline Join( TLink*apLink,TLink *apOther )//添加ap0ther到aplink中
{
	//printf("apOther %p\n",apOther);
	if( !apOther->head )
	{
		return ;
	}
	TNode *lp = apOther->head;
	while( lp )
	{
		lp->pLink = apLink;
		lp = lp->pNext;
	}
	lp = apOther->head;
	if(apLink->tail)
	{
		apLink->tail->pNext = (TNode*)lp;
		lp->pPrev = apLink->tail;
		apLink->tail = apOther->tail;
	}
	else
	{
		apLink->head = apOther->head;
		apLink->tail = apOther->tail;
	}

	apOther->head = apOther->tail = NULL;
}

/////////////////for copy stack //////////////////////////
stStackMem_t* co_alloc_stackmem(unsigned int stack_size) //为每个共享栈分配内存
{
	stStackMem_t* stack_mem = (stStackMem_t*)malloc(sizeof(stStackMem_t));
	stack_mem->occupy_co= NULL;
	stack_mem->stack_size = stack_size;
	stack_mem->stack_buffer = (char*)malloc(stack_size);
	stack_mem->stack_bp = stack_mem->stack_buffer + stack_size;
	return stack_mem;
}

stShareStack_t* co_alloc_sharestack(int count, int stack_size) //分配count个共享栈 大小为stack_size。
{
	stShareStack_t* share_stack = (stShareStack_t*)malloc(sizeof(stShareStack_t)); //申请一个stShareStack_t管理共享栈
	share_stack->alloc_idx = 0;//初始idx为0,分配方式为循环遍历,根据idx的值。
	share_stack->stack_size = stack_size;//设置栈大小

	//alloc stack array
	share_stack->count = count;//设置栈数量
	stStackMem_t** stack_array = (stStackMem_t**)calloc(count, sizeof(stStackMem_t*));//申请count个结构体
	for (int i = 0; i < count; i++)
	{
		stack_array[i] = co_alloc_stackmem(stack_size);//为每个结构体初始化和分配内存
	}
	share_stack->stack_array = stack_array;//设置栈的地址
	return share_stack;
}

static stStackMem_t* co_get_stackmem(stShareStack_t* share_stack) //获取一个共享栈
{
	if (!share_stack)
	{
		return NULL;
	}
	int idx = share_stack->alloc_idx % share_stack->count; //一共count个栈,所以对count取摸,栈重复使用。
	share_stack->alloc_idx++;//idx++下一次申请使用下一个共享栈

	return share_stack->stack_array[idx];//返回申请的共享栈的地址
}


// ----------------------------------------------------------------------------
struct stTimeoutItemLink_t;
struct stTimeoutItem_t;
struct stCoEpoll_t //管理定时时间的结构体
{
	int iEpollFd;//epoll的描述符fd
	static const int _EPOLL_SIZE = 1024 * 10;//epoll可添加的时间数量

	struct stTimeout_t *pTimeout;//超时事件

	struct stTimeoutItemLink_t *pstTimeoutList;//从命名来看是超时时间的链表,可是并没有发现有实际使用。

	struct stTimeoutItemLink_t *pstActiveList;//已触发时间列表 

	co_epoll_res *result;//使用时epoll返回的结果存储的变量

};
typedef void (*OnPreparePfn_t)( stTimeoutItem_t *,struct epoll_event &ev, stTimeoutItemLink_t *active );//函数指针,源码中指向OnPollPreparePfn函数
typedef void (*OnProcessPfn_t)( stTimeoutItem_t *);//函数指针,源码中指向OnSignalProcessEvent函数
struct stTimeoutItem_t//stTimeoutItem_t结构体存储回调函数以及其他属性。
{

	enum
	{
		eMaxTimeout = 40 * 1000 //40s 命名像最大定时时间,实际为使用
	};
	stTimeoutItem_t *pPrev;//上一个stTimeoutItem_t
	stTimeoutItem_t *pNext;//下一个stTimeoutItem_t
	stTimeoutItemLink_t *pLink;//控制这个链表的结构体

	unsigned long long ullExpireTime;//时间超时时间=now+timeout

	OnPreparePfn_t pfnPrepare;//函数指针,源码中指向OnPollPreparePfn函数
	OnProcessPfn_t pfnProcess;//函数指针,源码中指向OnSignalProcessEvent函数

	void *pArg; // routine 指向当前协程
	bool bTimeout;//是否超时
};
struct stTimeoutItemLink_t //管理stTimeoutItem_t的链表
{
	stTimeoutItem_t *head;//表头
	stTimeoutItem_t *tail;//表尾

};
struct stTimeout_t//管理 stTimeoutItemLink_t的结构体
{
	stTimeoutItemLink_t *pItems;//指向一个 stTimeoutItemLink_t
	int iItemSize;//链表大小,限制莲

	unsigned long long ullStart;//开始时间
	long long llStartIdx;//当前的idx idx和ullstart会随着运行进行变化。
						//时间轮概念客参考该博客(https://blog.youkuaiyun.com/GreyBtfly/article/details/83688996)
};
stTimeout_t *AllocTimeout( int iSize )//初始化一个stTimeout_t
{
	stTimeout_t *lp = (stTimeout_t*)calloc( 1,sizeof(stTimeout_t) );//分配内存

	lp->iItemSize = iSize;//设置最大加入的大小。
	lp->pItems = (stTimeoutItemLink_t*)calloc( 1,sizeof(stTimeoutItemLink_t) * lp->iItemSize );//分配内存

	lp->ullStart = GetTickMS();//设置时间
	lp->llStartIdx = 0;

	return lp;
}
void FreeTimeout( stTimeout_t *apTimeout )//释放一个stTimeout_t
{
	free( apTimeout->pItems );
	free ( apTimeout );
}
int AddTimeout( stTimeout_t *apTimeout,stTimeoutItem_t *apItem ,unsigned long long allNow )//添加超时时间
{
	if( apTimeout->ullStart == 0 )//如果ullStart为0,则设置其为allNow
	{
		apTimeout->ullStart = allNow;
		apTimeout->llStartIdx = 0;
	}
	if( allNow < apTimeout->ullStart )//如果allNow小于stTimeout_t的ullStart时间,则产生错误日志。
	{
		co_log_err("CO_ERR: AddTimeout line %d allNow %llu apTimeout->ullStart %llu",
					__LINE__,allNow,apTimeout->ullStart);

		return __LINE__;
	}
	if( apItem->ullExpireTime < allNow )//如果添加的事件的超时时间小于allNow时间,则产生错误日志。
	{
		co_log_err("CO_ERR: AddTimeout line %d apItem->ullExpireTime %llu allNow %llu apTimeout->ullStart %llu",
					__LINE__,apItem->ullExpireTime,allNow,apTimeout->ullStart);

		return __LINE__;
	}
	unsigned long long diff = apItem->ullExpireTime - apTimeout->ullStart;//diff为应该在时间轮中加入的位置

	if( diff >= (unsigned long long)apTimeout->iItemSize )//如果diff大于最大的时间,重置并产生错误日志
	{
		diff = apTimeout->iItemSize - 1;
		co_log_err("CO_ERR: AddTimeout line %d diff %d",
					__LINE__,diff);

		//return __LINE__;
	}
	AddTail( apTimeout->pItems + ( apTimeout->llStartIdx + diff ) % apTimeout->iItemSize , apItem );//添加到stTimeout_t中。

	return 0;
}
inline void TakeAllTimeout( stTimeout_t *apTimeout,unsigned long long allNow,stTimeoutItemLink_t *apResult )
{
	if( apTimeout->ullStart == 0 )
	{
		apTimeout->ullStart = allNow;
		apTimeout->llStartIdx = 0;
	}

	if( allNow < apTimeout->ullStart )
	{
		return ;
	}
	int cnt = allNow - apTimeout->ullStart + 1;
	if( cnt > apTimeout->iItemSize )
	{
		cnt = apTimeout->iItemSize;
	}
	if( cnt < 0 )
	{
		return;
	}
	for( int i = 0;i<cnt;i++)
	{
		int idx = ( apTimeout->llStartIdx + i) % apTimeout->iItemSize;
		Join<stTimeoutItem_t,stTimeoutItemLink_t>( apResult,apTimeout->pItems + idx  );//join之后会置NULL,所以不会重复添加上一轮的事件。
	}
	apTimeout->ullStart = allNow;
	apTimeout->llStartIdx += cnt - 1;


}//取出时间片中的所有超时事件。简述一下就是添加从ullstart到allnow的事件,并更新ullStart和idx的值
static int CoRoutineFunc( stCoRoutine_t *co,void * )
{
	if( co->pfn )
	{
		co->pfn( co->arg );
	}
	co->cEnd = 1;

	stCoRoutineEnv_t *env = co->env;

	co_yield_env( env );

	return 0;
}//协程调用的函数,运行之后让出协程。用于协程恢复。



struct stCoRoutine_t *co_create_env( stCoRoutineEnv_t * env, const stCoRoutineAttr_t* attr,
		pfn_co_routine_t pfn,void *arg )//初始化协程
{

	stCoRoutineAttr_t at;
	if( attr )//如果attr参数不为NULL,即有共享栈,把共享栈信息复制到at中,
	{
		memcpy( &at,attr,sizeof(at) );
	}
	if( at.stack_size <= 0 )//如果小于等于0,复制128×1024即128k
	{
		at.stack_size = 128 * 1024;
	}
	else if( at.stack_size > 1024 * 1024 * 8 ) //如果大于8M,重置为8M
	{
		at.stack_size = 1024 * 1024 * 8;
	}

	if( at.stack_size & 0xFFF )//如果低位的值,则进位。即0x1001会编程0x2000 4k对齐 最小单位是扇区4k
	{
		at.stack_size &= ~0xFFF;//低12位清零
		at.stack_size += 0x1000;//加0x1000
	}

	stCoRoutine_t *lp = (stCoRoutine_t*)malloc( sizeof(stCoRoutine_t) );//分配内存

	memset( lp,0,(long)(sizeof(stCoRoutine_t)));//清空置0


	lp->env = env;
	lp->pfn = pfn;
	lp->arg = arg;//初始化env,pfn,arg属性

	stStackMem_t* stack_mem = NULL;//初始化
	if( at.share_stack )
	{
		stack_mem = co_get_stackmem( at.share_stack);
		at.stack_size = at.share_stack->stack_size;//如果有共享栈。分配共享栈,并设置大小。
	}
	else
	{
		stack_mem = co_alloc_stackmem(at.stack_size);//没有共享栈,分配内存
	}
	lp->stack_mem = stack_mem;//设置指向的栈

	lp->ctx.ss_sp = stack_mem->stack_buffer;//初始地址
	lp->ctx.ss_size = at.stack_size;//大小

	lp->cStart = 0;//是否开始,用来判断一个协成是否初始化过ctx
	lp->cEnd = 0;//是否结束
	lp->cIsMain = 0;//是不是主协程
	lp->cEnableSysHook = 0;//是否hook过
	lp->cIsShareStack = at.share_stack != NULL;//是不是共享栈

	lp->save_size = 0;//使用共享栈时会使用的属性,如何使用后面会介绍,协程如何使用共享栈的内存。
	lp->save_buffer = NULL;

	return lp;
}

int co_create( stCoRoutine_t **ppco,const stCoRoutineAttr_t *attr,pfn_co_routine_t pfn,void *arg )//创建协程
{
	if( !co_get_curr_thread_env() )//如果env没有初始化过,则初始化env,会创建主协程。
	{
		co_init_curr_thread_env();
	}
	stCoRoutine_t *co = co_create_env( co_get_curr_thread_env(), attr, pfn,arg );//初始化协程
	*ppco = co;
	return 0;
}
void co_free( stCoRoutine_t *co )//释放协程内存,有共享栈则释放共享栈内存。
{
    if (!co->cIsShareStack)
    {
        free(co->stack_mem->stack_buffer);
        free(co->stack_mem);
    }
    free( co );
}
void co_release( stCoRoutine_t *co )//释放协程内存
{
    co_free( co );
}

void co_swap(stCoRoutine_t* curr, stCoRoutine_t* pending_co);//交换协程,pending_co协程取代curr协程

void co_resume( stCoRoutine_t *co )//启动协程
{
	stCoRoutineEnv_t *env = co->env;//设置env
	stCoRoutine_t *lpCurrRoutine = env->pCallStack[ env->iCallStackSize - 1 ];//lp为当前运行的协程
	if( !co->cStart )//如果没有初始化过,初始化ctx
	{
		coctx_make( &co->ctx,(coctx_pfn_t)CoRoutineFunc,co,0 );
		co->cStart = 1;
	}
	env->pCallStack[ env->iCallStackSize++ ] = co;//协程入栈
	co_swap( lpCurrRoutine, co );//交换协程


}
void co_yield_env( stCoRoutineEnv_t *env )//yield协程,协程会出调用栈,因此如果没有使用poll或者epoll,当前协程将不会使用
{

	stCoRoutine_t *last = env->pCallStack[ env->iCallStackSize - 2 ];
	stCoRoutine_t *curr = env->pCallStack[ env->iCallStackSize - 1 ];//取出最顶端的两个协程

	env->iCallStackSize--;//栈大小减一

	co_swap( curr, last);//交换协程
}

void co_yield_ct()//让出协程
{

	co_yield_env( co_get_curr_thread_env() );
}
void co_yield( stCoRoutine_t *co )//让出协程 libco的协程是按照调用栈的顺序运行的,所以co其实没有意义,
								//当然也有没有使用调用栈的,不过我没学习过x
{
	co_yield_env( co->env );
}

void save_stack_buffer(stCoRoutine_t* occupy_co)//保存栈数据
{
	///copy out
	stStackMem_t* stack_mem = occupy_co->stack_mem;//定义一个指针
	int len = stack_mem->stack_bp - occupy_co->stack_sp;//栈大小

	if (occupy_co->save_buffer)//如果保存过初始化。
	{
		free(occupy_co->save_buffer), occupy_co->save_buffer = NULL;
	}

	occupy_co->save_buffer = (char*)malloc(len); //malloc buf;
	occupy_co->save_size = len;//初始化

	memcpy(occupy_co->save_buffer, occupy_co->stack_sp, len);//保存数据
}

void co_swap(stCoRoutine_t* curr, stCoRoutine_t* pending_co)//交换协程
{
 	stCoRoutineEnv_t* env = co_get_curr_thread_env();//获取env

	//get curr stack sp
	char c;
	curr->stack_sp= &c;//获取栈地址 第一个定义的变量的地址就是栈地址。

	if (!pending_co->cIsShareStack)//如果不是共享栈
	{
		env->pending_co = NULL;
		env->occupy_co = NULL;
	}
	else
	{
		env->pending_co = pending_co;
		//get last occupy co on the same stack mem
		stCoRoutine_t* occupy_co = pending_co->stack_mem->occupy_co;
		//set pending co to occupy thest stack mem;
		pending_co->stack_mem->occupy_co = pending_co;//从定义来看是获取上一个occupy协程,并设置pending协成为occupy协程,但是我翻了一下源码,只发现了这个stack_mem->occupy_co= NULL;0-0

		env->occupy_co = occupy_co;
		if (occupy_co && occupy_co != pending_co)//如果两个协程不是同一个,则保存数据。
		{
			save_stack_buffer(occupy_co);
		}
	}

	//swap context
	coctx_swap(&(curr->ctx),&(pending_co->ctx) );//交换协程

	//stack buffer may be overwrite, so get again;
	stCoRoutineEnv_t* curr_env = co_get_curr_thread_env();
	stCoRoutine_t* update_occupy_co =  curr_env->occupy_co;
	stCoRoutine_t* update_pending_co = curr_env->pending_co;

	if (update_occupy_co && update_pending_co && update_occupy_co != update_pending_co)//如果不是同一个协程
	{
		//resume stack buffer
		if (update_pending_co->save_buffer && update_pending_co->save_size > 0)//如果使用了栈
		{
			memcpy(update_pending_co->stack_sp, update_pending_co->save_buffer, update_pending_co->save_size);//恢复数据
		}
	}
}



//int poll(struct pollfd fds[], nfds_t nfds, int timeout);
// { fd,events,revents }
struct stPollItem_t ;
struct stPoll_t : public stTimeoutItem_t
{
	struct pollfd *fds;//指向管理的fd
	nfds_t nfds; // typedef unsigned long int nfds_t; fd数量

	stPollItem_t *pPollItems;//指向管理他的item

	int iAllEventDetach;//是否分离的属性

	int iEpollFd;//指向管理事件的iepollfd

	int iRaiseCnt;//计数功能,在OnPollPreparePfn中自增,具体作用不清楚


};
struct stPollItem_t : public stTimeoutItem_t
{
	struct pollfd *pSelf;//自己的fd
	stPoll_t *pPoll;指向管理的stpoll_t

	struct epoll_event stEvent;//需要监听的事件
};
/*
 *   EPOLLPRI 		POLLPRI    // There is urgent data to read.
 *   EPOLLMSG 		POLLMSG
 *
 *   				POLLREMOVE
 *   				POLLRDHUP
 *   				POLLNVAL
 *
 * */
static uint32_t PollEvent2Epoll( short events )//poll监听事件改为epoll格式
{
	uint32_t e = 0;
	if( events & POLLIN ) 	e |= EPOLLIN;
	if( events & POLLOUT )  e |= EPOLLOUT;
	if( events & POLLHUP ) 	e |= EPOLLHUP;
	if( events & POLLERR )	e |= EPOLLERR;
	if( events & POLLRDNORM ) e |= EPOLLRDNORM;
	if( events & POLLWRNORM ) e |= EPOLLWRNORM;
	return e;
}
static short EpollEvent2Poll( uint32_t events )//epoll监听事件改为poll格式
{
	short e = 0;
	if( events & EPOLLIN ) 	e |= POLLIN;
	if( events & EPOLLOUT ) e |= POLLOUT;
	if( events & EPOLLHUP ) e |= POLLHUP;
	if( events & EPOLLERR ) e |= POLLERR;
	if( events & EPOLLRDNORM ) e |= POLLRDNORM;
	if( events & EPOLLWRNORM ) e |= POLLWRNORM;
	return e;
}

static __thread stCoRoutineEnv_t* gCoEnvPerThread = NULL;	
					/*
						在GUN标准中,提供了__thread关键字,配合static后,可以实现让一个线程拥有自己的全局变量
						所以无论什么时候,协程都是作用于线程上的。线程之间会不同。
						https://blog.youkuaiyun.com/wsj18808050/article/details/51603006
					*/

void co_init_curr_thread_env()//初始化线程中的env
{
	gCoEnvPerThread = (stCoRoutineEnv_t*)calloc( 1, sizeof(stCoRoutineEnv_t) );//分配内存
	stCoRoutineEnv_t *env = gCoEnvPerThread;//设置env

	env->iCallStackSize = 0;//调用栈大小
	struct stCoRoutine_t *self = co_create_env( env, NULL, NULL,NULL );//初始化主协程
	self->cIsMain = 1;//是主协程

	env->pending_co = NULL;
	env->occupy_co = NULL;

	coctx_init( &self->ctx );//初始化ctx,全为0 和coctx_make作用不同

	env->pCallStack[ env->iCallStackSize++ ] = self;//入栈

	stCoEpoll_t *ev = AllocEpoll();//分配内存
	SetEpoll( env,ev );//绑定env和管理时间的ev
}
stCoRoutineEnv_t *co_get_curr_thread_env()//获取env
{
	return gCoEnvPerThread;
}

void OnPollProcessEvent( stTimeoutItem_t * ap )//回调函数,用来启用协程。 poll相关的还有timeout相关的。
{
	stCoRoutine_t *co = (stCoRoutine_t*)ap->pArg;
	co_resume( co );//重新启动协程
}

void OnPollPreparePfn( stTimeoutItem_t * ap,struct epoll_event &e,stTimeoutItemLink_t *active )
{
	stPollItem_t *lp = (stPollItem_t *)ap;
	lp->pSelf->revents = EpollEvent2Poll( e.events );//更改格式并赋值


	stPoll_t *pPoll = lp->pPoll;
	pPoll->iRaiseCnt++;//计数,大概可以统计epoll的事件

	if( !pPoll->iAllEventDetach )//如果时间没从timeout分离,分离
	{
		pPoll->iAllEventDetach = 1;

		RemoveFromLink<stTimeoutItem_t,stTimeoutItemLink_t>( pPoll );//移除事件

		AddTail( active,pPoll );//加到active事件中,即事件已经触发

	}
}//将epoll_wait返回的事件格式改为poll,从timeout中删除。并加入active中。


void co_eventloop( stCoEpoll_t *ctx,pfn_co_eventloop_t pfn,void *arg )//核心 循环处理事件
{
	if( !ctx->result )
	{
		ctx->result =  co_epoll_res_alloc( stCoEpoll_t::_EPOLL_SIZE );
	}//如果result没有分配内存,分配内存。
	co_epoll_res *result = ctx->result;


	for(;;)
	{
		int ret = co_epoll_wait( ctx->iEpollFd,result,stCoEpoll_t::_EPOLL_SIZE, 1 );//不断epoll_wait等待事件触发,用epoll_wait返回的事件,可能触发了,但是时间未到,下面会做处理。

		stTimeoutItemLink_t *active = (ctx->pstActiveList);//已经触发的事件
		stTimeoutItemLink_t *timeout = (ctx->pstTimeoutList);//超时事件

		memset( timeout,0,sizeof(stTimeoutItemLink_t) );//初始化为0

		for(int i=0;i<ret;i++)//把所有的触发了的事件 添加到 已经触发的事件中
		{
			stTimeoutItem_t *item = (stTimeoutItem_t*)result->events[i].data.ptr;
			if( item->pfnPrepare )
			{
				item->pfnPrepare( item,result->events[i],active );//将epoll_wait返回的事件格式改为poll,从timeoutlink中删除。并加入active中。
			}
			else
			{
				AddTail( active,item );
			}//使用co_poll的事件才会初始化pfnPrepare,使用co_cond_timedwait加入的事件不会初始化,co_cond_signal的时候移除事件。
		}


		unsigned long long now = GetTickMS();//获取时间
		TakeAllTimeout( ctx->pTimeout,now,timeout );//获取所有已经超时的事件,从ptimeout中加入到timeout中

		stTimeoutItem_t *lp = timeout->head;
		while( lp )
		{
			//printf("raise timeout %p\n",lp);
			lp->bTimeout = true;
			lp = lp->pNext;
		}//设置超时事件的btimeout属性

		Join<stTimeoutItem_t,stTimeoutItemLink_t>( active,timeout );//把所有timeout事件加入到active中

		lp = active->head;
		while( lp )//遍历所有已经超时的事件
		{

			PopHead<stTimeoutItem_t,stTimeoutItemLink_t>( active );//active出栈
            if (lp->bTimeout && now < lp->ullExpireTime)//如果触发了但是时间没到,加入到ptimeout中
			{
				int ret = AddTimeout(ctx->pTimeout, lp, now);
				if (!ret)
				{
					lp->bTimeout = false;
					lp = active->head;
					continue;
				}
			}
			if( lp->pfnProcess )//时间到了,恢复协程。
			{
				lp->pfnProcess( lp );
			}

			lp = active->head;//下一个触发的事件
		}
		if( pfn )//传入的参数,可以用来退出主协程。
		{
			if( -1 == pfn( arg ) )
			{
				break;
			}
		}

	}
}
void OnCoroutineEvent( stTimeoutItem_t * ap )//恢复协程的,但是好像未使用,上面恢复协程的使用另一个函数。
{
	stCoRoutine_t *co = (stCoRoutine_t*)ap->pArg;
	co_resume( co );
}


stCoEpoll_t *AllocEpoll()//分配内存
{
	stCoEpoll_t *ctx = (stCoEpoll_t*)calloc( 1,sizeof(stCoEpoll_t) );

	ctx->iEpollFd = co_epoll_create( stCoEpoll_t::_EPOLL_SIZE );//10240个
	ctx->pTimeout = AllocTimeout( 60 * 1000 );//60×1000个link。

	ctx->pstActiveList = (stTimeoutItemLink_t*)calloc( 1,sizeof(stTimeoutItemLink_t) );
	ctx->pstTimeoutList = (stTimeoutItemLink_t*)calloc( 1,sizeof(stTimeoutItemLink_t) );


	return ctx;
}

void FreeEpoll( stCoEpoll_t *ctx )//释放内存
{
	if( ctx )
	{
		free( ctx->pstActiveList );
		free( ctx->pstTimeoutList );
		FreeTimeout( ctx->pTimeout );
		co_epoll_res_free( ctx->result );
	}
	free( ctx );
}

stCoRoutine_t *GetCurrCo( stCoRoutineEnv_t *env )//获取当前协程
{
	return env->pCallStack[ env->iCallStackSize - 1 ];
}
stCoRoutine_t *GetCurrThreadCo( )//获取当前协程
{
	stCoRoutineEnv_t *env = co_get_curr_thread_env();
	if( !env ) return 0;
	return GetCurrCo(env);
}



typedef int (*poll_pfn_t)(struct pollfd fds[], nfds_t nfds, int timeout);
int co_poll_inner( stCoEpoll_t *ctx,struct pollfd fds[], nfds_t nfds, int timeout, poll_pfn_t pollfunc)
{//pollfunc在hook的poll中调用co_poll_inner时传入co_poll(真的poll
    if (timeout == 0)//timeout为0 直接调用函数
	{
		return pollfunc(fds, nfds, timeout);
	}
	if (timeout < 0)//timeout小于0,时间设为无穷大 调用AddTimeout时会设为size-1。
	{
		timeout = INT_MAX;
	}
	int epfd = ctx->iEpollFd;
	stCoRoutine_t* self = co_self();//当前协程

	//1.struct change
	stPoll_t& arg = *((stPoll_t*)malloc(sizeof(stPoll_t)));
	memset( &arg,0,sizeof(arg) );//新建一个stpoll_t

	arg.iEpollFd = epfd;
	arg.fds = (pollfd*)calloc(nfds, sizeof(pollfd));
	arg.nfds = nfds;//初始化

	stPollItem_t arr[2];
	if( nfds < sizeof(arr) / sizeof(arr[0]) && !self->cIsShareStack)
	{
		arg.pPollItems = arr;
	}//如果不是共享栈且nfds小于2,使用分配的空间
	else
	{
		arg.pPollItems = (stPollItem_t*)malloc( nfds * sizeof( stPollItem_t ) );
	}//用malloc分配空间
	memset( arg.pPollItems,0,nfds * sizeof(stPollItem_t) );//初始化为0

	arg.pfnProcess = OnPollProcessEvent;//重新启动协程的函数
	arg.pArg = GetCurrCo( co_get_curr_thread_env() );//设置env


	//2. add epoll
	for(nfds_t i=0;i<nfds;i++)//添加所有事件到epoll中
	{
		arg.pPollItems[i].pSelf = arg.fds + i;//设置fd
		arg.pPollItems[i].pPoll = &arg;//设置管理的stPoll_t

		arg.pPollItems[i].pfnPrepare = OnPollPreparePfn;//设置函数,分离timeout,添加到active
		struct epoll_event &ev = arg.pPollItems[i].stEvent;//设置的参数

		if( fds[i].fd > -1 )
		{
			ev.data.ptr = arg.pPollItems + i;
			ev.events = PollEvent2Epoll( fds[i].events );//初始化env

			int ret = co_epoll_ctl( epfd,EPOLL_CTL_ADD, fds[i].fd, &ev );//事件加入epoll
			if (ret < 0 && errno == EPERM && nfds == 1 && pollfunc != NULL)//如果epoll加入失败
			{
				if( arg.pPollItems != arr )
				{
					free( arg.pPollItems );
					arg.pPollItems = NULL;
				}
				free(arg.fds);
				free(&arg);
				return pollfunc(fds, nfds, timeout);//用poll加入 用系统的poll 这里没有完善后面的处理 
			}
		}
		//if fail,the timeout would work
	}

	//3.add timeout

	unsigned long long now = GetTickMS();//获取时间
	arg.ullExpireTime = now + timeout;//设置超时时间
	int ret = AddTimeout( ctx->pTimeout,&arg,now );//事件添加到ptimeout中,用来回调
	int iRaiseCnt = 0;
	if( ret != 0 )
	{
		co_log_err("CO_ERR: AddTimeout ret %d now %lld timeout %d arg.ullExpireTime %lld",
				ret,now,timeout,arg.ullExpireTime);
		errno = EINVAL;
		iRaiseCnt = -1;

	}
    else
	{
		co_yield_env( co_get_curr_thread_env() );//让出协程,事件触发之后会回调
		iRaiseCnt = arg.iRaiseCnt;//可以统计有多少事件触发,猜测的0-0
	}

    {
		//clear epoll status and memory
		RemoveFromLink<stTimeoutItem_t,stTimeoutItemLink_t>( &arg );//移除之前加入到ptimeout的事件
		for(nfds_t i = 0;i < nfds;i++)
		{
			int fd = fds[i].fd;
			if( fd > -1 )
			{
				co_epoll_ctl( epfd,EPOLL_CTL_DEL,fd,&arg.pPollItems[i].stEvent );//从epoll中删除
			}
			fds[i].revents = arg.fds[i].revents;
		}


		if( arg.pPollItems != arr )//如果大于两个,释放临时空间
		{
			free( arg.pPollItems );
			arg.pPollItems = NULL;
		}

		free(arg.fds);
		free(&arg);
	}

	return iRaiseCnt;
}

int	co_poll( stCoEpoll_t *ctx,struct pollfd fds[], nfds_t nfds, int timeout_ms )//功能看co_poll
{
	return co_poll_inner(ctx, fds, nfds, timeout_ms, NULL);
}

void SetEpoll( stCoRoutineEnv_t *env,stCoEpoll_t *ev )//绑定env和ev
{
	env->pEpoll = ev;
}
stCoEpoll_t *co_get_epoll_ct()//获取pepoll
{
	if( !co_get_curr_thread_env() )
	{
		co_init_curr_thread_env();
	}
	return co_get_curr_thread_env()->pEpoll;
}
struct stHookPThreadSpec_t//线程共享数据相关?并无实际使用
{
	stCoRoutine_t *co;
	void *value;

	enum
	{
		size = 1024
	};
};
void *co_getspecific(pthread_key_t key)//获取数据
{
	stCoRoutine_t *co = GetCurrThreadCo();
	if( !co || co->cIsMain )//如果是主协程,调用系统的函数
	{
		return pthread_getspecific( key );
	}
	return co->aSpec[ key ].value;//返回对应的value
}
int co_setspecific(pthread_key_t key, const void *value)//设置数据
{
	stCoRoutine_t *co = GetCurrThreadCo();
	if( !co || co->cIsMain )
	{
		return pthread_setspecific( key,value );
	}
	co->aSpec[ key ].value = (void*)value;
	return 0;
}



void co_disable_hook_sys()//取消hook
{
	stCoRoutine_t *co = GetCurrThreadCo();
	if( co )
	{
		co->cEnableSysHook = 0;
	}
}
bool co_is_enable_sys_hook()//是否hook
{
	stCoRoutine_t *co = GetCurrThreadCo();
	return ( co && co->cEnableSysHook );
}

stCoRoutine_t *co_self()//返回当前协程
{
	return GetCurrThreadCo();
}

//co cond
struct stCoCond_t;
struct stCoCondItem_t//信号链表
{
	stCoCondItem_t *pPrev;
	stCoCondItem_t *pNext;
	stCoCond_t *pLink;

	stTimeoutItem_t timeout;
};
struct stCoCond_t//存储信号链表的和尾
{
	stCoCondItem_t *head;
	stCoCondItem_t *tail;
};
static void OnSignalProcessEvent( stTimeoutItem_t * ap )//信号事件的回调函数
{
	stCoRoutine_t *co = (stCoRoutine_t*)ap->pArg;
	co_resume( co );
}

stCoCondItem_t *co_cond_pop( stCoCond_t *link );//出栈
int co_cond_signal( stCoCond_t *si )//激活一个信号事件
{
	stCoCondItem_t * sp = co_cond_pop( si );//获得栈顶并出栈
	if( !sp )
	{
		return 0;
	}
	RemoveFromLink<stTimeoutItem_t,stTimeoutItemLink_t>( &sp->timeout );//从ptimeout中删除

	AddTail( co_get_curr_thread_env()->pEpoll->pstActiveList,&sp->timeout );//添加到活跃事件中

	return 0;
}
int co_cond_broadcast( stCoCond_t *si )//广播 激活所有信号事件
{
	for(;;)
	{
		stCoCondItem_t * sp = co_cond_pop( si );//获得栈顶并出栈
		if( !sp ) return 0;

		RemoveFromLink<stTimeoutItem_t,stTimeoutItemLink_t>( &sp->timeout );//从ptimeout中删除

		AddTail( co_get_curr_thread_env()->pEpoll->pstActiveList,&sp->timeout );//添加到活跃事件中
	}

	return 0;
}


int co_cond_timedwait( stCoCond_t *link,int ms )//添加一个信号事件
{
	stCoCondItem_t* psi = (stCoCondItem_t*)calloc(1, sizeof(stCoCondItem_t));
	psi->timeout.pArg = GetCurrThreadCo();
	psi->timeout.pfnProcess = OnSignalProcessEvent;//初始化

	if( ms > 0 )
	{
		unsigned long long now = GetTickMS();
		psi->timeout.ullExpireTime = now + ms;

		int ret = AddTimeout( co_get_curr_thread_env()->pEpoll->pTimeout,&psi->timeout,now );//添加到ptimeout
		if( ret != 0 )
		{
			free(psi);
			return ret;
		}
	}
	AddTail( link, psi);//添加到link栈顶

	co_yield_ct();//让出协程


	RemoveFromLink<stCoCondItem_t,stCoCond_t>( psi );//事件触发后删除,继续之前timewait的事件
	free(psi);

	return 0;
}
stCoCond_t *co_cond_alloc()//分配内存
{
	return (stCoCond_t*)calloc( 1,sizeof(stCoCond_t) );
}
int co_cond_free( stCoCond_t * cc )//释放内存
{
	free( cc );
	return 0;
}


stCoCondItem_t *co_cond_pop( stCoCond_t *link )//出栈顶,因为是链表所以只需要head=head->next,以及其他的处理
{
	stCoCondItem_t *p = link->head;
	if( p )
	{
		PopHead<stCoCondItem_t,stCoCond_t>( link );
	}
	return p;
}

个人总结 对于每个结构体所对相关的功能进行一个总结。
struct stCoEpoll_t:时间轮相关的,主要报告ptimeout,timeout,active。ptimeout是时间轮,60×1000个link,时间轮大小为60s,所以使用时定时事件不能大于60s。理论上也是可以自己拓展的。插入事件时,在对应的link上插入事件。需要注意的是,add函数参数的now与真正插入的位置无关,插入的位置是超时时间。timeout超时事件,用来获取epoll_wait函数返回的事件。
获取之后加入到active中。active活跃事件,即已经触发的事件,如果未超时,重新插入到ptimeout中,需要注意的是,每个事件在触发后在其link中都会删除。例如active中事件,会删除,然后加入到ptimeout。
struct stCoRoutineEnv_t :调用栈相关的函数,yield和resume等相关函数都是根据调用栈来切换协程。协程正是通过调用栈和时间轮两个一起管理。
struct stTimeoutItem_t:时间轮里面的使用的结构体,用来管理事件,以及回调函数。还有超时事件,协程等属性。add、takeall,free,alloc。等都是在链表上进行的操作。因为每一个都是存储了管理他的link(存储head,tail)和他的pprev,pnext。所以删除,增删都是极快的。
stPollItem_t :poll都是继承了stTimeoutItem_t,用于epoll事件触发后,用对应的stTimeoutItem_t来启用协程,或者其他操作。
本身是poll属性的,再使用epoll时进行了格式转换。
stCoCond_t:信号相关的事件。结构体的数据类型和上述几个类似,不过这次存储head和tail的没有命名为link。
主要函数有co_cond_timedwait,添加等待事件,初始化之后添加到ptimeout中,并添加初始化的时间到cond的link中,然后让出协程,协程触发后从link中删除。co_cond_signal,移除栈顶的等待事件,从cond的link和ptimeout中删除,并添加到active中。
active会去触发timewait的事件,如果时间未到重新加入ptimeout。co_cond_broadcast 广播就是触发所有的等待事件。过程和
co_cond_signal类似。 cond事件在一种情况下会产生错误日志,timewait设置的事件,已经超时触发了,才有singal来触发事件。这时超时时间小于now,并不会加入到时间轮中。
if( apItem->ullExpireTime < allNow )
{
co_log_err();
return LINE;
}
总体来说libco是一个比较优秀的库,学习过程中会发现很多设计上细节的东西,对于各种情况的处理,文件4K大小,根据大小用不同方式分配内存,等等。虽然有很多东西没放出来,管中窥豹,还是很厉害的。初学者可以学到很多东西。

2024年最新腾讯协程库libco的原理分析_st协程库原理(1),C C++工程师面试题 当readwrite_routine和accept_routine都会调用co_poll,但是accept会将执行权交给主进程,而task协程挂起后,执行权则会交给accept协程。那么前面由于执行碰到EAGAIN而挂起的task协程,则通过co_eventloop来驱动继续执行。%rdi,%rsi,%rdx,%rcx,%r8,%r9 用作函数参数,依次对应第1参数,第2参数。16个64位寄存器,分别是:%rax,%rbx,%rcx,%rdx,%esi,%edi,%rbp,%rsp, 阅读详情

相关推荐

Polyspace静态代码分析实战:嵌入式高可信软件开发指南

静态代码分析是一种在不运行程序的情况下,通过分析源代码来发现潜在缺陷的技术。其核心原理包括数据流分析、控制流分析和抽象解释,能够在编译阶段识别运行时错误、编码规范违规等问题。这项技术的核心价值在于提升软件质量、降低测试成本,并满足功能安全标准(如ISO 26262、DO-178C)的严格要求,尤其适用于嵌入式系统、汽车电子和航空航天等安全关键领域。通过形式化方法,工具可以数学证明代码的某些属性,为高可信软件开发提供客观证据。本文以MathWorks Polyspace为例,深入解析其在实际嵌入式项目中的配置

weixin_34197488的博客 661

细说时间测量RDTSC和RDTSCP

细说RDTSC的坑

yizhiniu_xuyw的专栏 7954

GD32F470移植FreeRTOS实战:从环境搭建到任务调度与调试

实时操作系统(RTOS)是嵌入式开发中实现多任务并发与资源管理的核心技术,其核心原理在于通过调度器对任务进行优先级管理和上下文切换,确保关键任务能够及时响应。FreeRTOS作为一款开源、轻量级的RTOS,凭借其可裁剪性和广泛的硬件支持,在资源受限的微控制器(MCU)领域具有极高的技术价值,尤其适用于需要复杂任务调度和实时响应的应用场景,如工业控制、物联网设备和消费电子。本文以兆易创新的Cortex-M4内核MCU——GD32F470为例,详细解析了FreeRTOS的移植流程,涵盖了从工程框架搭建、内核配置

weixin_30239339的博客 393

RDTSC指令实现纳秒级计时器

原文:http://blog.chinaunix.net/uid-20647570-id-217750.html X86 platform 从pentium开始,很多80x86微处理器都引入TSC,一个用于时间戳计数器的64位的寄存器,它在每个时钟信号(CLK, CLK是微处理器中一条用于接收外部振荡器的时钟信号输入引线)到来时加一。 通过它可以计算CPU的主频,比如:如果微处...

liuzq的专栏 1926

HC32F460移植FreeRTOS实战:从零搭建实时多任务系统

实时操作系统(RTOS)是嵌入式开发中管理多任务、实现确定响应的核心软件架构。其基本原理是通过任务调度器,基于优先级或时间片轮转算法,在单个处理器上高效模拟并发执行。FreeRTOS作为一款开源、轻量级的RTOS内核,凭借其可裁剪性和高可靠性,在工业控制、物联网设备等场景中被广泛应用,能显著提升代码结构清晰度和系统可维护性。本文以华大半导体HC32F460(ARM Cortex-M4内核)为硬件平台,深入解析FreeRTOS移植的完整流程。内容涵盖工程环境搭建、源码结构解析、关键配置文件FreeRTOSCo

weixin_30919571的博客 332

TSC指令中文手册:深入解析与应用

本文还有配套的精品资源,点击获取 简介:《TSC指令中文手册》是对计算机硬件中的Time Stamp Counter(TSC)指令的深入解析文档。TSC是CPU内部寄存器,提供精确时间测量能力,对性能分析、基准测试、实时系统时间同步有重要作用。本手册详细介绍TSC寄存器的访问、解释和使用,包括汇编指令RDTSC和RDTSCP,讨论TSC在多核系统中的同步问题,以及在操作系统...

weixin_42514540的博客 2576

汇编基础-----常见命令基本使用

(Load Effective Address):加载有效地址,将一个内存地址加载到一个寄存器中,而不是加载内存中的值。:用于与操作系统进行交互的指令,如Linux中的int 0x80、syscall等。:字节顺序交换指令,用于交换32位或64位寄存器中的字节顺序。:用于辅助调试和优化的指令,如NOP、HLT、PAUSE等。:用于浮点数运算的指令集,如加载浮点数、加减乘除等。:用于程序流程控制的指令,如条件跳转、循环等。:交换指令,用于交换寄存器或内存中的值。:用于访问内存的指令,如加载、存储等。

a1309525802的博客 1726

使用.NET 免费开源包ViewFaceCore做一个人脸识别并注册的功能,没有比我更全的

本文介绍了一个基于WinForm的免费人脸识别系统实现方案。系统使用AForge和ViewFaceCore库,支持从USB或电脑自带摄像头采集图像,具备人脸注册和快速识别功能。硬件方面仅需普通摄像头,软件依赖多个NuGet包(如AForge.Video、ViewFaceCore等)。界面包含视频显示、图像预览和功能按钮控件,后台采用SQLServer存储用户面部特征数据。核心代码展示了人脸检测、特征提取和多线程处理逻辑,通过ViewFaceCore实现高效识别。系统具有识别速度快、准确率高的特点,适合Win

一个靠“吃代码”长大的程序猿,专治各种“Bug 饥饿症”。 817

libco简要分析(待更)

文章目录源码分析co_routine.h 本篇分析分为两个部分。libco源码分析。libco例子分析。首先进行源码的分析。 分析之前需要了解一下前置知识 1.什么是协程? 2.什么是共享栈? 源码分析思路:对每个.h.cpp进行分析,先分析.h再分析对应的.cpp。 建议阅读顺序:结构体->与结构体关系紧密的函数->其他函数 建议例子和源码分析一起阅读,阅读体验更佳。另外参考了大量其...

practer的博客 333

MATLAB下pdf文件操作读取示例

当遇到一些需要大量数据的项目,数据的读取与储存显得尤为重要。怎样把数据从其他格式的文件中读取,并运用到程序中是很多时候我们要解决的问题。对于平常的.txt,和.xlsx文件,MATLAB有对应的函数帮助读取,但是遇到pdf文件时MATLAB很难读取内容。本文以一个操作实例提供了一个从pdf文件中读取数据的方法。

UESTC 五高考3模拟的博客 6558

DDS(Direct Digital Synthesizer)数字频率合成器

随着数字技术在仪器仪表和通信系统中的广泛使用,可从参考频率源产生多个频率的数 字控制方法诞生了,即直接数字频率合成(DDS)。 直接数字合成(DDS)是一种产生模拟波形(通常是正弦波)的方法,它通过产生数字形式的时变信号,然后进行数模转换。由于DDS设备内的操作主要是数字的,它可以提供输出频率之间的快速切换,良好的频率分辨率,并在广泛的频率频谱上操作。随着设计和工艺技术的进步,今天的DDS设备非常紧凑,功耗很小。 上面是比较书面的语言,其实简单地说就是一个信号发生器模块,主要是产生频率可调的三角波正弦波。

denghuajing的博客 4864

现在不学C++27协程标准化,6个月后你的代码将无法通过ISO C++27合规性审计(附自动化检查工具链)

掌握C++27协程标准化实战,助你提前规避ISO合规风险。覆盖异步I/O、任务调度等真实场景,提供语法迁移指南与自动化审计工具链,确保代码零违规。6个月后C++27正式发布前必学,值得收藏。

PoliVein的博客 242

Android 性能之 Game Mode

本文的目的是介绍 AOSP Game Mode 功能。Game Mode 功能的核心是系统服务 GameManagerService,在本文中用 GMS 指代 GameManagerService。在 API 26 之后,应用开发者可以通过将应用的 category 属性设置为 "game",使系统(即 GMS)能够将该应用识别为 "游戏"。GMS 定义了 4 种 game mode,即 1|2|3|4|standard|performance|battery|custom;

专注于系统快稳省领域 3064

协程(三) 腾讯libco源码分析

腾讯的libco使用了hook技术,做到了在遇到阻塞IO时自动切换协程,(由事件循环co_eventloop检测的)阻塞IO完成时恢复协程,简化异步回调为相对同步方式的功能。其没有使用显示的调度器来管理所有协程(保存协程的相关数据),在协程切换及恢复之间主要依靠epoll_event.data.ptr来传递恢复协程所需的数据

Hyman's Blog 8362

代码实现 —— 基于 STM32 的可见光通信系统课程设计

基于STM32F103ZET6,源码如需移植,需要修改定时器捕获通道引脚,及定时器,如果你也有这个课设任务,希望对你有所帮助。

不善挽留 1万+

基于STFT和卷积神经网络的时序数据分类

我们提供了相应的代码和数据,读者可以直接运行并尝试在自己的数据集上进行分类实验。在本文中,我们将介绍一种基于短时傅里叶变换(STFT)和卷积神经网络(CNN)的时序数据分类方法。我们将提供可直接运行的代码和数据,以帮助读者理解和实现这一方法。STFT可以提取信号的频谱信息,有助于捕捉时序数据中的关键特征。在这个例子中,我们将使用一个简化的手势识别数据集作为示例。在这段代码中,我们使用Adam优化器和稀疏分类交叉熵损失函数进行模型的编译。然后,我们使用训练集进行模型的训练,同时使用测试集进行模型性能的验证。

CodeWOW的博客 694

深入解析libco协程库:原理、实现与高性能C++并发编程实践

协程作为现代高性能服务端开发的核心技术,通过用户态的协作式任务调度,实现了同步编程风格下的异步非阻塞执行,有效解决了传统多线程模型资源消耗大和回调地狱的问题。其原理在于保存和恢复执行上下文,在遇到I/O操作时自动挂起并切换任务,从而提升并发处理能力。这一技术在高并发、低延迟的海量连接场景中具有重要价值,例如即时通讯、网络服务器等。libco作为经典的C++有栈协程库,通过Hook系统调用和共享栈设计,实现了对同步代码的低侵入性改造和极致的内存优化,是深入理解高性能并发编程的绝佳范例。

weixin_30755393的博客 407

【100分- 求最多可以派出多少支团队】(题目+思路+Java&C++&Python解析+在线测试)

用数组代表每个人的能力,一个比赛活动要求参赛团队的最低能力值为N,每个团队可以由1人或者2人组成,且1个人只能参加1个团队,计算出最多可以派出多少只符合要求的团队。

m0_73659489的博客 313

二级目录 —— libco源码分析、学习笔记(腾讯协程开源库)

博客一级目录 libco源码分析/学习笔记 由于时间紧迫和我技术有限,一些前驱知识点我就不过多解释了。大概有基础的操作系统概念,一点汇编知识(64位汇编栈帧之类的东西),c/c++,linux网络编程。不用很精通(好吧博主我也比较菜),学习嘛。 对于linux而言,进程拥有系统资源(包括内存、cpu、显示器等)、是系统资源分配的基本单位;但是进程的分配和销毁代价比较大,为了提...

GreyBtfly的博客 1748

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

Moon中的协程调度原理:理解Lua协程在游戏服务器中的应用

在游戏服务器开发领域,**Moon框架**凭借其独特的**Lua协程调度机制**和**Actor模型架构**,为开发者提供了一套高性能的异步编程解决方案。作为一款基于C++17和Lua的轻量级游戏服务器框架,Moon通过精巧的协程调度设计,实现了高效的并发处理能力,特别适合需要处理大量并发连接的游戏服务器场景。 ## 🌟 Moon框架与Actor模型简介 Moon框架采用经典的**Actor

gitblog_00792的博客 821

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

libco配置、编译 初体验 - 腾讯协程开源库

在某度上搜索libco全都是源码分析,像我这样的菜鸡使用Makefile时还是有点小挫折的,下面我们一起体验一下书写编译第一个ibco程序。 博客一级目录 二级目录——libco源码分析/学习笔记 下载源代码 github:https://github.com/WbtLm/libco 打开上述网址,点击如下图所示的绿色按钮,选择Download Zip: 将zip解压,(我将里面所...

GreyBtfly的博客 3619

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

编译器 | 从工业实现到极简设计 —— 编译器的多元形态与实践

……

u013669912的博客 888

PlatformIO集成libopencm3与FreeRTOS构建嵌入式实时系统框架

嵌入式实时操作系统(RTOS)是处理多任务、传感器数据采集和网络通信等复杂场景的核心技术,其通过任务调度、内存管理和中断处理等机制,为资源受限的微控制器提供确定性的实时响应能力。FreeRTOS作为一款开源、可裁剪的RTOS内核,凭借其轻量级和可移植性,广泛应用于物联网节点、工业控制等嵌入式领域。在开发实践中,结合硬件抽象库(如libopencm3)和现代化工具链(如PlatformIO),能显著提升代码复用率和开发效率。本文以STM32F103平台为例,详细解析如何配置PlatformIO项目以集成lib

weixin_34198453的博客 320

27_写文章的工具

本文整理了C语言学习与应用的系列文章目录,按不同阶段分类:面试准备篇包含指针、关键字、结构体等核心知识;入职指南篇提供Git使用手册;技术进阶篇详解单向/双向链表的各项操作;扩展阅读篇探讨代码到硬件的构建流程。适合C语言学习者分阶段系统学习,从基础到企业级开发全面覆盖。

qq_73850061的博客 357

C++协程(一):Coroutine Theory

本文翻译自c++协程库cppcoro库作者Lewis Baker的github post,本篇为第一篇,原文内容在https://lewissbaker.github.io/2017/09/25/coroutine-theory Coroutine Theory This is the first of a series of posts on the C++ Coroutines TS, a new language feature that is currently on track for inclu

sinat_17700695的博客 1318
上一篇: co_routine.h分析
下一篇: 腾讯一面凉经
practer
博客等级 码龄10年 11粉丝 38原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值