为什么会有协程,解决什么问题?
注意:在理解协程时,从操作系统上看,线程相比于进程需要搬进搬出内存的数据很少,但仍有需要共享的资源,需要加锁(因为不同线程可能出现并行执行);而协程是在线程之下用来实现的异步高并发的工具,内部数据是公用的,协程的切换不需要太多不同资源的分配,主要分配的是cpu;
用epoll组件制作的协程,在不加入具体业务的情况下,速度是要略慢于epoll组件本身制作的reactor的,在加入具体业务之后,协程所带来的异步特性才会体现出来(注意,同步io人为改变成异步io实现的异步性能上只能接近于异步)
同步的编程方式,异步的性能(通过协程框架,能通过同步编程,实现异步编程相同的效果)
本质是将非异步的io进行的io异步操作;
异步操作:
优点:性能比较高
缺点:多个线程共用一个fd
同步操作:
优点:便于理解
缺点:性能不高
如何判读同步:检测IO和读写IO(同步异步是两个或多个对象的关系)在一个流程(上下文)里面
在我们现在CS(客户端、服务端),BS(浏览器、服务器)开发模式下,服务器的吞吐量是一个很重要的参数。其实吞吐量是IO处理时间加上业务处理。为了简单起见,比如,客户端与服务器之间是长连接的,客户端定期给服务器发送心跳包数据。客户端发送一次心跳包到服务器,服务器更新该新客户端状态的。心跳包发送的过程,业务处理时长等于IO读取(RECV系统调用)加上业务处理(更新客户状态)。吞吐量等于1s业务处理次数。
创建的协程对象结构
#define queue(name,type) struct name{
struct type *next;
struct type *prev;
}
#define rbtree_node(name,type) struct name{
char color;
struct type *right;
struct type *left;
struct type *parent;
}
struct coroutine{
struct cpu_register_set *set;//协程切换时调入调出cpu的那部分;
void *func;//coroutinr_entry
void *arg;
void *retval;//return value
void *stack;//可以制作成共享栈、独立栈
size_t stack_size;
//struct coroutine *next;
queue(ready_queue,coroutine) *ready;//所有的协程都在就绪队列当中
rbtree(wait_rbtree,coroutine) *wait;//
rbtree(sleep_rbtree,coroutine) *sleep;//wait和sleep的优先级高于ready
};
协程调度器的编写
struct scheduler{
struct scheduler_ops *ops;//调度器策略
int epfd;
queue_node *ready_set;
rbtree() *wait_set;
rbtree() *sleep_tree;
}
struct scheduler_ops{
struct scheduler_ops *next;
enset();
deset();
.....
};
理解了写成大致的制作思路,再初步的了解一下协程常用api
协程的api主要针对io操作:
系统中常用的网络io操作有:
1、socket
2、bind
3、listen
4、accept
5、send
6、recv
7、close
8、connect
对于这些接口,均为同步接口(即不返回也能继续往下执行)
为了使协程的思想更好的实现,我们需要通过同步的编程使得呈现异步的效果;
nty_accept(){
int ret = epoll/select/poll(fd.....,0);//timeout=0时立刻返回不等待
if(ret>0){
accept();
}else{
epoll_ctl(epfd,...);
yield();//让出资源(这里的资源是协程占用的线程)
}
}
这里还需要理解hook的作用,它的原理是将程序中的系统调用动态链接到其他地址(可以理解为他的名字),原本地址的调用更改为用户层函数的调用。
————该技术可以用来制作插件监控