【问题标题】:AIO on OS X vs Linux - why it doesn't work on Mac OS X 10.6OS X 与 Linux 上的 AIO - 为什么它在 Mac OS X 10.6 上不起作用
【发布时间】:2010-02-06 03:52:46
【问题描述】:

我的问题很简单。为什么下面的代码可以在 Linux 上运行,而不能在 Mac OS X 10.6.2 Snow Leopard 上运行。

要编译,将文件保存到 aio.cc,并在 Linux 上使用 g++ aio.cc -o aio -lrt 编译,在 Mac OS X 上使用 g++ aio.cc -o aio 编译。我使用 Mac OS X 10.6.2 在 Mac 和 Linux 内核上进行测试2.6 用于在 Linux 上进行测试。

我在 OS X 上看到的失败是 aio_write 失败并显示 -1 并将 errno 设置为 EAGAIN,这仅表示“资源暂时不可用”。这是为什么呢?

extern "C" {
#include <aio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <errno.h>
#include <signal.h>
}
#include <cassert>
#include <string>
#include <iostream>

using namespace std;

static void
aio_completion_handler(int signo, siginfo_t *info, void *context)
{
  using namespace std;
  cout << "BLAH" << endl;
}


int main()
{
  int err;

  struct sockaddr_in sin;
  memset(&sin, 0, sizeof(sin));

  sin.sin_port = htons(1234);
  sin.sin_addr.s_addr = inet_addr("127.0.0.1");
  sin.sin_family = PF_INET;

  int sd = ::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
  if (sd == -1) {
    assert(!"socket() failed");
  }

  const struct sockaddr *saddr = reinterpret_cast<const struct sockaddr *>(&sin);
  err = ::connect(sd, saddr, sizeof(struct sockaddr));
  if (err == -1) {
    perror(NULL);
    assert(!"connect() failed");
  }

  struct aiocb *aio = new aiocb();
  memset(aio, 0, sizeof(struct aiocb));

  char *buf = new char[3];
  buf[0] = 'a';
  buf[1] = 'b';
  buf[2] = 'c';
  aio->aio_fildes = sd;
  aio->aio_buf = buf;
  aio->aio_nbytes = 3;

  aio->aio_sigevent.sigev_notify = SIGEV_SIGNAL;
  aio->aio_sigevent.sigev_signo = SIGIO;
  aio->aio_sigevent.sigev_value.sival_ptr = &aio;

  struct sigaction sig_act;

  sigemptyset(&sig_act.sa_mask);
  sig_act.sa_flags = SA_SIGINFO;
  sig_act.sa_sigaction = aio_completion_handler;

  sigaction(SIGIO, &sig_act, NULL);

  errno = 0;
  int ret = aio_write(aio);
  if (ret == -1) {
    perror(NULL);
  }  
  assert(ret != -1);  
}

更新(2010 年 2 月):OSX 根本不支持套接字上的 AIO。无赖!

【问题讨论】:

  • 你能ktrace看看有什么问题吗?

标签: c++ aio aio-write


【解决方案1】:

提供的代码在 Mountain Lion 10.8.2 上进行了测试。它适用于一个小的修正。 线路
“aio->aio_fildes = sd;”

例如应该改为:
aio->aio_fildes = open("/dev/null", O_RDWR);

得到预期的结果。

参见手册。 “aio_write() 函数允许调用进程对之前打开的文件执行异步写入。”

【讨论】:

    【解决方案2】:

    我在 10.6.2 上的代码与您的代码非常相似(但写入文件)没有任何问题 - 因此可以执行您正在尝试的操作。

    出于好奇,您对 SIGIO 常量使用什么值? 我发现 OS X 中的无效值会导致 aio_write 失败 - 所以 我总是通过 SIGUSR1。

    也许检查 sigaction() 的返回值来验证信号细节?

    【讨论】:

    【解决方案3】:

    您的链接中提出的要点都指向了一种用于提高 io 完成通知的不同方法(例如 kqueue,它是一种 BSD 特定机制),但并没有真正回答您关于异步 io 的 POSIX 方法的问题。以及他们是否在达尔文工作。

    UNIX 世界确实是一个混搭的解决方案,如果有一个经过试验和测试的解决方案可以在所有平台上运行,那就太好了,可惜目前还没有 - POSIX 是最适合的解决方案一致性。

    这有点像在黑暗中刺伤,但在您的套接字句柄上设置非阻塞(即设置套接字选项 O_NONBLOCK )以及使用 SIGUSR1 可能也很有用

    如果我有时间,我会处理你的套接字示例,看看我是否也能从中得到什么。

    祝你好运。

    【讨论】:

    • 我尝试通过 fcntl 设置 O_NONBLOCK 和 O_ASYNC 但它也没有帮助。我还必须调查使用 F_SETOWN 是否有帮助,但我很确定 OS X 上的套接字不支持 aio_{read,write} api,老实说,这很难过。
    • lists.apple.com/archives/Macnetworkprog/2009/Aug/msg00016.html 说:“OS X 是否支持套接字上的异步 IO?当我调用 aio_write() 时出现 ESPIPE 错误。相同的代码在 Linux 中有效,如果我使用文件句柄代替的套接字,它也适用于 OS X (10.5.7).似乎 Mac 上的套接字 + aio 需要额外的步骤,或者我只是想做一些尚不支持的事情.任何帮助都很大赞赏。” lists.apple.com/archives/Macnetworkprog/2009/Aug/msg00017.html 回复:“OS X 是否支持套接字上的异步 IO?不。”
    • 从技术上讲,POSIX AIO 提供了一种标准方法来处理完成通知的各种方法。当然,Linux 并没有很好地实现该 API。
    【解决方案4】:

    OSX 允许您通过 (CF)RunLoop 使用套接字。或者从运行循环中获取回调。 这是我发现在 mac 上使用异步 IO 的最优雅的方式。 您可以使用现有的套接字并执行 CFSocketCreateWithNative。并在你的 runloop 上注册回调。

    这里是一小段代码,展示了如何设置它,不完整,因为我已经削减了一个源文件......

    // This will setup a readCallback 
    void SocketClass::setupCFCallback() {   
    CFSocketContext     context = { 0, this, NULL, NULL, NULL };
    
    if (CFSocketRef macMulticastSocketRef = CFSocketCreateWithNative(NULL, socketHandle_, kCFSocketReadCallBack,readCallBack, &context)) {
        if (CFRunLoopSourceRef macRunLoopSrc = CFSocketCreateRunLoopSource(NULL, macMulticastSocketRef, 0)) {
            if (!CFRunLoopContainsSource(CFRunLoopGetCurrent(), macRunLoopSrc, kCFRunLoopDefaultMode)) {
                CFRunLoopAddSource(CFRunLoopGetCurrent(), macRunLoopSrc, kCFRunLoopDefaultMode);
                macRunLoopSrc_ = macRunLoopSrc;
            }
            else
                CFRelease(macRunLoopSrc);
        }
        else
            CFSocketInvalidate(macMulticastSocketRef);
        CFRelease(macMulticastSocketRef);
    }
    }
    
    
    
    void SocketClass::readCallBack(CFSocketRef inref, CFSocketCallBackType  type,CFDataRef , const void *, void *info) {
    if (SocketClass*    socket_ptr = reinterpret_cast<SocketClass*>(info))
        socket_ptr->receive(); // do stuff with your socket
    
    }
    

    【讨论】:

      猜你喜欢
      • 2012-02-19
      • 1970-01-01
      • 2011-07-29
      • 1970-01-01
      • 1970-01-01
      • 2015-08-11
      • 1970-01-01
      • 2011-12-01
      • 1970-01-01
      相关资源
      最近更新 更多