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)
    //操作完成,回调用一个用户提供的函数
    }
    }



  • 相关阅读:
    Android 解决小米手机Android Studio安装app 报错的问题It is possible that this issue is resolved by uninstalling an existi
    Android Unresolved Dependencies
    Android studio 自定义打包apk名
    Android Fragment与Activity交互的几种方式
    魅族和三星Galaxy 5.0webView 问题Android Crash Report
    Android几种常见的多渠道(批量)打包方式介绍
    Android批量打包 如何一秒内打完几百个apk渠道包
    上周热点回顾(9.30-10.6)团队
    上周热点回顾(9.23-9.29)团队
    上周热点回顾(9.16-9.22)团队
  • 原文地址:https://www.cnblogs.com/sniperHW/p/2436385.html
Copyright © 2011-2022 走看看