事件响应方式
在编程中,常常要让程序等待某些事件,如何及时响应这些事件,有若干方式。1. 程序轮询,程序不断查询状态,直到期望的状态发生。这将剧烈消耗CPU和相关资源。可以在轮询中插入sleep之类的休眠以减小CPU的消耗,但是牺牲了响应时间。
2. 事件触发,有几种:
1:1 一个线程等待一个事件,同步阻塞方式。等待期间不消耗CPU,事件可以立即响应。如read。
M:1 一个线程等待M个事件。同步阻塞方式。等待期间不消耗CPU,出现任一事件可以立即响应。如select。
M:N N个线程等待M个事件。同步阻塞方式。等待期间不消耗CPU,出现任一事件都会选出一个线程来响应这个事件。如epoll。
只讨论 rpc 的情况?
不考虑跨硬件 rpc 的话,除了socket/pipe,还有信号量,条件量,直接回调(有竞争就加锁)配着队列之类的也方便 本帖最后由 yulihua49 于 2016-08-12 21:33 编辑
cokeboL 发表于 2016-08-12 17:30 static/image/common/back.gif
只讨论 rpc 的情况?
不考虑跨硬件 rpc 的话,除了socket/pipe,还有信号量,条件量,直接回调(有竞争就加 ...
把你的想法说说,补充一下。
尤其是同步阻塞的事件处理器如何处理异步IO,没想出来怎样简练的描述。
关于eventfd,signalfd,timerfd等等。 最近论坛上的少,才看到。
eventfd这个好像没什么可说的吧,epollfd,就是epoll那几个接口
signalfd,这个具体是指信号?比如kill -9?还是指其他?
实现timer的方式也挺多的,timerfd这个也没明白具体指? 如果非要用c/c++
异步IO
发包肯定要存队列,同步阻塞的地方处理,也只是把包挂到sock fd对应的结构体的消息队列里然后加个等待可写的事件,但是无法保证同步阻塞的地方返回时包已经发出去了,
想等到结果,就得把同步的地方分两半,发包之后的部分弄个回调来做,如果同步这部分逻辑,发包和等待都是在循环里,又不行,还得改成回调之后再激发下一次循环的结构,
所以你们各位弄的协程那种,可能比这种写法稍好一些
如果同步的地方都用同步发肯定太虐心了,一是发包的sock fd如果有多个,就都应该从epoll列表里dele了先,然后再遍历阻塞发,这种肯定就乱了,不行 timer c/c++ epoll这种结构里
我见过的主流大概就两种:
一种是弄个小顶堆或者list、map也凑合用,epoll的循环里每次wait最小时间间隔,nginx好像是这种,redis类似但是好像是没用小顶堆,用的链表,我猜是redis的业务场景,简单的链表实现足够了吧
另一种是wait固定的tick time,然后和timer的结构最小间隔对比看到时间没,就相当于轮询吧
不太清楚你们具体的业务场景,各种ipc机制和锁这些,就像过家家,不同的业务场景和需求,可以有太多中摆法。。
可惜你们不用golang,否则真是简化了太多结构,chan、锁、回调加起来的各种组合,就差不多能搞所有姿势了,省心,省力。 本帖最后由 yulihua49 于 2016-08-15 16:16 编辑
cokeboL 发表于 2016-08-15 14:53 static/image/common/back.gif
不太清楚你们具体的业务场景,各种ipc机制和锁这些,就像过家家,不同的业务场景和需求,可以有太多中摆法。 ...
个人偏爱C是因为癖好,喜欢玩机器,C更像在使用一个机器。其他语言更高级,接近于任务描述,应该是好用些,但是资源控制更间接,有时不知他怎么干的,需要优化也无从下手。
那几个fd,你在网上搜搜,有详细说明。配合epoll完成非IO异步操作的。
这里有一个eventfd配合epoll进行文件异步IO的例子:
/*******************************************
* 需要linux 2.6.22 以上版本
* 和libaio 3.107以上版本
* 如果不具备这个条件,在makefile里用SIO_fd.o
* 取代本模块
*******************************************/
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/eventfd.h>
#include <libaio.h>
#include <scsrv.h>
static int AIO_oper(int fd,char *buff,size_t iosize,int flg)
{
io_context_t myctx;
int rc,num;
uint64_t finished_aio;
struct iocb _iocb,*io=&_iocb;
struct io_event event;
int efd = eventfd(0, 0);
T_YIELD yield=get_yield();
if (efd == -1) {
return flg?write(fd,buff,iosize):read(fd,buff,iosize);
}
memset(&myctx,0,sizeof(myctx));
io_set_eventfd(io,efd);
io_queue_init(1, &myctx);
if(flg) io_prep_pread(io, fd, buff, iosize, 0);
else io_prep_pwrite(io, fd, buff, iosize, 0);
rc = io_submit(myctx, 1, &io);
if(rc<0) {
close(efd);
io_destroy(myctx);
return flg?write(fd,buff,iosize):read(fd,buff,iosize);
}
if(yield) {
rc = yield(efd,0,0);//efd提交给epoll,并yield and resume.
if(rc==0) eventfd_read(efd, &finished_aio);
}
close(efd);
num = io_getevents(myctx, 1, 1, &event, NULL);
if(num>0) {
if(event.res2==0) num=event.res;
else num=-1;
}
io_destroy(myctx);
return num;
}
int AIO_read(int fd,char *buff,size_t iosize)
{
return AIO_oper(fd,buff,iosize,0);
}
int AIO_write(int fd,char *buff,size_t iosize)
{
return AIO_oper(fd,buff,iosize,1);
}
本帖最后由 yulihua49 于 2016-08-15 16:22 编辑
cokeboL 发表于 2016-08-15 14:53 static/image/common/back.gif
不太清楚你们具体的业务场景,各种ipc机制和锁这些,就像过家家,不同的业务场景和需求,可以有太多中摆法。 ...
发了个回复怎么丢了?
/*******************************************
* 需要linux 2.6.22 以上版本
* 和libaio 3.107以上版本
* 如果不具备这个条件,在makefile里用SIO_fd.o
* 取代本模块
*******************************************/
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/eventfd.h>
#include <libaio.h>
#include <scsrv.h>
static int AIO_oper(int fd,char *buff,size_t iosize,int flg)
{
io_context_t myctx;
int rc,num;
uint64_t finished_aio;
struct iocb _iocb,*io=&_iocb;
struct io_event event;
int efd = eventfd(0, 0);
T_YIELD yield=get_yield();
if (efd == -1) {
return flg?write(fd,buff,iosize):read(fd,buff,iosize);
}
memset(&myctx,0,sizeof(myctx));
io_set_eventfd(io,efd);
io_queue_init(1, &myctx);
if(flg) io_prep_pread(io, fd, buff, iosize, 0);
else io_prep_pwrite(io, fd, buff, iosize, 0);
rc = io_submit(myctx, 1, &io);
if(rc<0) {
close(efd);
io_destroy(myctx);
return flg?write(fd,buff,iosize):read(fd,buff,iosize);
}
if(yield) {
rc = yield(efd,0,0);//efd提交给epoll,并yield and resume.
if(rc==0) eventfd_read(efd, &finished_aio);
}
close(efd);
num = io_getevents(myctx, 1, 1, &event, NULL);
if(num>0) {
if(event.res2==0) num=event.res;
else num=-1;
}
io_destroy(myctx);
return num;
}
int AIO_read(int fd,char *buff,size_t iosize)
{
return AIO_oper(fd,buff,iosize,0);
}
int AIO_write(int fd,char *buff,size_t iosize)
{
return AIO_oper(fd,buff,iosize,1);
}
cokeboL 发表于 2016-08-15 14:53 static/image/common/back.gif
不太清楚你们具体的业务场景,各种ipc机制和锁这些,就像过家家,不同的业务场景和需求,可以有太多中摆法。 ...
/*******************************************
* 需要linux 2.6.22 以上版本
* 和libaio 3.107以上版本
* 如果不具备这个条件,在makefile里用SIO_fd.o
* 取代本模块
*******************************************/
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/eventfd.h>
#include <libaio.h>
#include <scsrv.h>
static int AIO_oper(int fd,char *buff,size_t iosize,int flg)
{
io_context_t myctx;
int rc,num;
uint64_t finished_aio;
struct iocb _iocb,*io=&_iocb;
struct io_event event;
int efd = eventfd(0, 0);
T_YIELD yield=get_yield();
if (efd == -1) {
return flg?write(fd,buff,iosize):read(fd,buff,iosize);
}
memset(&myctx,0,sizeof(myctx));
io_set_eventfd(io,efd);
io_queue_init(1, &myctx);
if(flg) io_prep_pread(io, fd, buff, iosize, 0);
else io_prep_pwrite(io, fd, buff, iosize, 0);
rc = io_submit(myctx, 1, &io);
if(rc<0) {
close(efd);
io_destroy(myctx);
return flg?write(fd,buff,iosize):read(fd,buff,iosize);
}
if(yield) {
rc = yield(efd,0,0);//efd提交给epoll,并yield and resume.
if(rc==0) eventfd_read(efd, &finished_aio);
}
close(efd);
num = io_getevents(myctx, 1, 1, &event, NULL);
if(num>0) {
if(event.res2==0) num=event.res;
else num=-1;
}
io_destroy(myctx);
return num;
}
int AIO_read(int fd,char *buff,size_t iosize)
{
return AIO_oper(fd,buff,iosize,0);
}
int AIO_write(int fd,char *buff,size_t iosize)
{
return AIO_oper(fd,buff,iosize,1);
}