我的问题非常简单。为什么以下代码适用于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。无赖!
答案 0 :(得分:2)
所呈现的代码在Mountain Lion 10.8.2上进行了测试。它适用于小修正。
线路
“aio-&gt; aio_fildes = sd;”
应该改为例如:
aio-&gt; aio_fildes = open(“/ dev / null”,O_RDWR);
获得预期结果。
参见手册。 “aio_write()函数允许调用进程对先前打开的文件执行异步写入。”
答案 1 :(得分:1)
我的代码与您的代码非常相似10.6.2(但写入文件)工作没有任何问题 - 所以可以做你正在尝试的事情。
出于好奇,您对SIGIO常量有什么价值? 我发现OS X中的无效值会导致aio_write失败 - 所以 我总是通过SIGUSR1。
也许检查sigaction()的返回值来验证信号细节?
答案 2 :(得分:1)
链接中提出的要点都指向了一种不同的方法来提升完成通知(例如kqueue是一种特定于BSD的机制),但是并没有真正回答你的问题,而是针对异步io的POSIX方法。以及他们是否在达尔文工作。
UNIX世界真的是一个混合的解决方案,如果有一个经过验证的解决方案可以在所有平台上运行,那将是非常好的,唉目前还没有 - POSIX是最有目的的解决方案一致性。
在黑暗中有点刺痛,但在套接字句柄上设置非阻塞(即设置套接字选项O_NONBLOCK)以及使用SIGUSR1
也可能有用。如果我有时间,我会使用你的套接字样本,看看我是否也能从中得到任何东西。
祝你好运。
答案 3 :(得分:1)
OSX允许您通过(CF)RunLoop使用套接字。或者从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
}