由于routine代码分成了好几篇博客分析,然后现在在宏观上对函数和结构上进行小小的总结和整理。
一、常用的函数接口
以下函数是最上层的函数(供平时编程调用的函数)
int co_create( stCoRoutine_t **ppco,const stCoRoutineAttr_t *attr,pfn_co_routine_t pfn,void *arg );
创建一个协程,申请、初始化协程控制块;并负责协程环境的初始化。
参数1:协程控制块(结构体)指针的指针。
参数2:share stack(我认为是内存池,允许多个协程栈共享一块内存详见二级目录附带的知乎链接),可以写NULL表示不使用share stack。
参数3:函数指针:typedef void *(*pfn_co_routine_t)( void * );
参数4:参数3表示的函数的参数。void*类型。
返回值:始终返回0。
注意:主协程不需要create(由操作系统创建),也不需要resume(由操作系统赋予执行权),更不需要yield(return或exit)。一旦create了其它协程,它就会被create自动初始化。
void co_resume( stCoRoutine_t *co );
执行一个协程(交换执行权限,执行co,阻塞当前协程)。如果co是第一次执行会自动提前初始化上下文。
参数1:协程控制块指针。表示要执行的协程。
void co_yield_ct();
用于子协程主动交出执行权。
void co_yield( stCoRoutine_t *co);
剥夺co所在线程正在执行的协程的执行权。这里与void co_yield_ct();不一样的是:剥夺其它协程的执行权。这就允许协程时间片管理了(虽然可能用不到)。
void co_enable_hook_sys();
函数是用来打开 libco 的钩子标示,进行系统 io 函数的时候才会调用到 libco 的函数而不是原系统函数
int co_poll( stCoEpoll_t *ctx,struct pollfd fds[], nfds_t nfds, int timeout_ms );
下面这个函数功能相同(只是参数不同),co_poll直接调用并返回co_poll_inner的返回值,调用时将inner的参数5填为NULL。
int co_poll_inner( stCoEpoll_t *ctx,struct pollfd fds[], nfds_t nfds, int timeout, poll_pfn_t pollfunc);
我们对co_poll_inner进行解释:
功能:相当于epoll。要使co_poll工作必须在主协程中调用eventloop,否则无法检查事件超时。
参数1:eventloop控制块指针。因为poll要跟eventloop搭配。
参数2:监听的句柄数组,struct pollfd是Linux poll用到的数据结构。
参数3:用于标记数组fds中的结构体元素的总数量。
参数4:超时时间ms,若此参数不为空,则此时间不仅仅被加入到epoll中监听,还会被加入到事件轮中等待超时同样会转为active。
参数5:函数指针,超时(active)事件。定义如下:
typedef int (*poll_pfn_t)(struct pollfd fds[], nfds_t nfds, int timeout);
简单的例子见下:
#include "co_routine.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/time.h>
#include <stack>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/epoll.h>
#ifdef __FreeBSD__
#include <cstring>
#include <sys/types.h>
#include <sys/wait.h>
#endif
#include<iostream>
using namespace std;
struct task_t
{
stCoRoutine_t *co;
int epFd;
};
typedef struct task_t task_t;
int myErrorOperate(char const * const error_str,int error_line,int error_exit=1)
{
perror(error_str);
printf("%d\n",error_line);
if(error_exit==1)
exit(1);
return 0;
}
int co_accept(int fd, struct sockaddr *addr, socklen_t *len );
static void* mcoListen(void *arg_co)
{
// task_t &co=*(task_t*)arg_co;
co_enable_hook_sys();
int lsEpFd;
lsEpFd=epoll_create(100);
if(lsEpFd<0)
{
myErrorOperate("create listen_epfd err",__LINE__);//exit
}
int lsSocketFd;
if((lsSocketFd=socket(AF_INET,SOCK_STREAM,0))<0)
{
// free(lsEpFd);
myErrorOperate("create listen_socket fd err.",__LINE__);//exit
}
//set socket opt
int ret,val=1;
ret=setsockopt(lsSocketFd,SOL_SOCKET,SO_REUSEADDR,(void*)&val,sizeof(val));
//reuse addr
if(ret<0)
{
myErrorOperate("set SO_REUSEADDR err.",__LINE__,0);
}
struct sockaddr_in saddr;
memset(&saddr,0,sizeof(sockaddr_in));
saddr.sin_family=AF_INET;
saddr.sin_addr.s_addr=INADDR_ANY;
saddr.sin_port=htons(8001);
ret=bind(lsSocketFd,(struct sockaddr*)&saddr,sizeof(struct sockaddr_in));
if(ret<0)
{
myErrorOperate("lsten socket bind err.",__LINE__,1);//exit
}
ret=listen(lsSocketFd,1024);//backlog
if(ret<0)
{
myErrorOperate("listen err.",__LINE__,1);//exit
}
cout<<"Accepting connections..."<<endl;
socklen_t saddrLen;
for(;;)
{
saddrLen=sizeof(saddr);
ret=co_accept(lsSocketFd,(struct sockaddr*)&saddr,&saddrLen);
if(ret<0)//每次poll超时后都需要重新加入。
{
struct pollfd pf={0};
pf.fd=ret; //关心epoll事件
pf.events=(POLLIN|POLLERR|POLLHUP);
co_poll(co_get_epoll_ct(),&pf,1,1000);//yield 同时关心epoll事件,和1000ms的超时事件
}
if(ret>0)
{
cout<<ret<<endl;
}
}
}
void * print(void *args)
{
co_enable_hook_sys();
for(;;)
{
cout<<"time 0.5"<<endl;
struct pollfd pf={0};//不关心epoll时间,只关心时间轮超时事件。
co_poll(co_get_epoll_ct(),&pf,1,500);//500ms打印一次
}
}
int main() {
task_t coLs;
stCoRoutine_t *time;
co_create(&time,NULL,print,NULL);
co_resume(time);//启动0.5s计时打印函数
co_create(&(coLs.co),NULL,mcoListen,&coLs);
co_resume(coLs.co);//启动接受连接函数,接受任何连接请求,打印sokecfd,然后不做任何事情
cout<<"listen co init complete."<<endl;
co_eventloop(co_get_epoll_ct(),0,0);
}
co_epoll系列
co_epoll_create();
co_epoll_ctl();
co_epoll_wait();