zoukankan      html  css  js  c++  java
  • kqueue epoll 边界触发模式的网络编程模型

    本文并不打算介绍边界触发模式,需要了解的朋友自己到网上搜索.

    本文只是打算介绍近期总结的三种边界触发模式的实现方式,后面会实现每一种然后做一个性能比较.

    1)模仿windows完成端口的模式.

    这是最早的时候想到的一种方法,并且已经用C++实现过.大概结构这样的,

    定义了一个IO请求结构,类似于IOCP的OVERLAP结构:

    struct OVERLAP
    {
    void *buf;
    int bytetransfer;
    int errcode;
    };

    然后是一个对应用不透明的socket结构:

    struct socket_t
    {
    volatile int readable;
    volatile int writeable;
    list * pending_read;
    list * pending_write;
    };

    向上层提供了两个发送/接受接口

    int WSASend(socket_t*,OVERLAP*);
    int WSARecv(socket_t*,OVERLAP*);

    如果网络操作无法立即完成(readable/writeable == 0 或 read/write的错误码是EWOULDBLOCK)

    则将请求保存在pending_read/pending_write中.

    在epoll线程中,如果发现一个套接口被激活,则将其readable/writeable设置为1,并查看pending

    list 中是否有未完成的请求,如果有,则弹一个出来执行,然后往完成队列中添加一个完成事件.

    下面是WSARecv的伪代码:

    int WSARecv(s,overlap)
    {
    if !s.readable then
    s.pending_read.push_back(overlap)
    return IO_PENDING
    end

    int bytetransfer = read(overlap.buf,overlap.bytetransfer)
    if bytetransfer < 0 then
    if errno == EWOULDBLOCK then
           s.readable = 0  
    s.pending_read.push_back(overlap)
    return IO_PENDING
    end
    end

    return bytetransfer

    }

    epoll中套接口被激活的伪代码:

    void OnReadActive(s)
    {
    s.readable = 1
    ioreq = s.pending_read.pop_front()
    if ioreq ~= nil then
    //弹出一个请求,执行,然后投递一个完成通告
    int bytetransfer = read(ioreq.buf,ioreq.bytetransfer)
    ioreq.bytetransfer = bytetransfer
    IOCompleteEventQueue.push_back(ioreq)
    end
    }

    完成例程的伪码如下:

    void complete_routine()
    {
    complete_status = GetCompleteStatus()
    s = GetSocket(complete_status)
    do
    ret = WSARecv(s,complete_status)
    while( ret!= IO_PENDING)
    }

    注:这个模式在最开始的时候实际的IO操作是交由另外的IO工作线程完成的,IO完成后投递完成通告

    这样,即使IO立即可以完成也会投递完成通告,在IO繁忙时对完成队列的操作消耗也是不小的。改进

    之后,只有当套接字从未激活态变为激活态,且有IO请求时才会投递一次完成通告(IOCP已经增加了类似

    的选项FILE_SKIP_COMPLETION_PORT_ON_SUCCESS)

    2)跟模式1类似,区别如下:

    void OnReadActive(s)
    {
    s.readable = 1
    ioreq = s.pending_read.pop_front()
    if ioreq ~= nil then
    IOCompleteEventQueue.push_back(ioreq)
    end
    }

    也就是epoll线程完全不执行实际的IO请求,所有的请求都由用户提供的完成线程执行.此时完成队列

    实际上并不存放完成事件,存放的只是pending的IO操作.

    此时Read操作也分成两个

    int WSARead(s,overlap)
    {
    s.pending_read.push_back(overlap)
    req = pending_read.pop_front()
    IOCompleteEventQueue.push_back(req)//仅仅投递一个请求,不尝试完成操作
    }

    //此函数在完成例程中调用
    int raw_read(s,overlap)
    {
    if !s.readable then
    s.pending_read.push_back(overlap)
    return IO_PENDING
    end

    int bytetransfer = read(overlap.buf,overlap.bytetransfer)
    if bytetransfer < 0 then
    if errno == EWOULDBLOCK then
    s.readable = 0
    s.pending_read.push_back(overlap)
    return IO_PENDING
    end
    end

    return bytetransfer
    }

    3)IO操作全部都在epoll线程中执行, 要充分利用多核CPU多启动几个epoll线程即可.

    每个epoll对上层提供一个队列IO_queue,用以保存IO请求.上层请求IO时仅仅是往这

    个队列中放入一个元素即可.

    epoll线程主循环伪代码如下:

    void main_loop()
    {
    while(true)
    {
    local_queue = IO_queue //将IO_queue中的所有请求同步到local_queue中
    while(req = local_queue.pop_front)
    {
    //保证请求按提交的顺序被执行
    req.s.pending_read.push_back(req)
    req = s.pending_read.pop_front()
    //执行请求,如果请求无法完成,重新插入到pending队列的头部
    }
    //epoll_wait............
    for all active s do
    OnReadActive(s)
    end
    }
    }

    void OnReadActive(s)
    {
    s.readable = 1
    while(ioreq = s.pending_read.pop_front())
    {
    read(ioreq.buf,ioreq.bytetransfer)
    //操作完成,回调用一个用户提供的函数
    }
    }



  • 相关阅读:
    如何入门深度学习?
    java opencv使用相关
    python操作Excel读写--使用xlrd
    从声学模型算法总结 2016 年语音识别的重大进步丨硬创公开课
    sift 与 surf 算法
    BP神经网络原理详解
    Nature重磅:Hinton、LeCun、Bengio三巨头权威科普深度学习
    浅谈流形学习(转)
    远离神经网络这个黑盒,人工智能不止这一条路可走
    对比深度学习十大框架:TensorFlow 并非最好?
  • 原文地址:https://www.cnblogs.com/sniperHW/p/2436385.html
Copyright © 2011-2022 走看看