OS X 上的 AIO 与 Linux - 为什么它在 Mac OS X 10.6 上不起作用
我的问题很简单。为什么下面的代码可以在 Linux 上运行,但不能在 Mac OS X 10.6.2 Snow Leopard 上运行。
要编译,请将文件保存到 aio.cc,并在 Linux 上使用 g++ aio.cc -o aio -lrt
进行编译,在 Mac OS 上使用 g++ aio.cc -o aio
进行编译X. 我使用 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。真糟糕!
My question is really simple. Why the code below does work on Linux, and doesn't on Mac OS X 10.6.2 Snow Leopard.
To compile save the file to aio.cc, and compile with g++ aio.cc -o aio -lrt
on Linux, and g++ aio.cc -o aio
on Mac OS X. I'm using Mac OS X 10.6.2 for testing on a Mac, and Linux kernel 2.6 for testing on Linux.
The failure I see on OS X is aio_write fails with -1 and sets errno to EAGAIN, which simply means "Resource temporarily unavailable". Why is that?
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);
}
UPDATE (Feb 2010): OSX does not support AIO on sockets at all. Bummer!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
所提供的代码在 Mountain Lion 10.8.2 上进行了测试。它只需稍加修正即可工作。
线路
“aio->aio_fildes = sd;”
例如应更改为:
aio->aio_fildes = open( "/dev/null", O_RDWR);
以获得预期的结果。
参见手册。 “aio_write() 函数允许调用进程对先前打开的文件执行异步写入。”
The presented code was tested on Mountain Lion 10.8.2. It works with a small correction.
The line
"aio->aio_fildes = sd;"
should be changed for example to:
aio->aio_fildes = open( "/dev/null", O_RDWR);
to get the expected result.
see manual. "The aio_write() function allows the calling process to perform an asynchronous write to a previously opened file."
我在 10.6.2 上的代码与您的代码非常相似(但写入文件),工作没有任何问题 - 所以可以做您正在尝试的事情。
只是出于好奇,您对 SIGIO 常量使用什么值?
我发现 OS X 中的无效值会导致 aio_write 失败 - 所以
我总是通过 SIGUSR1。
也许检查 sigaction() 的返回值来验证信号详细信息?
I have code very similar to yours on 10.6.2 (but writing to a file) working without any problems - so it is possible to do what you're trying.
Just out of curiosity, what value are you using for the SIGIO constant ?
I found that an invalid value here in OS X would casue aio_write to fail - so
I always pass SIGUSR1.
Maybe check the return value of sigaction() to verify the signal details?
您的链接中提出的要点都指向引发 io 完成通知的不同方法(例如 kqueue,它是 BSD 特定机制),但并没有真正回答您有关异步 io 的 POSIX 方法的问题。以及他们是否在研究达尔文。
UNIX 世界确实是一个解决方案的大杂烩,如果有一个经过尝试和测试的解决方案可以跨所有平台工作,那就太好了,可惜目前还没有 - POSIX 是旨在实现最大一致性的解决方案。
这有点在黑暗中刺伤,但在套接字句柄上设置非阻塞(即设置套接字选项 O_NONBLOCK )以及使用 SIGUSR1 也可能很有用。
如果我有时间,我将使用您的套接字示例和看看我是否也能从中得到什么。
祝你好运。
The points raised in your links all point to a different method for raising io completion notifications (e.g. kqueue which is a BSD specific mechanism), but doesn't really answer your question re POSIX methods for async io. and whether they work on Darwin.
The UNIX world really is a mish mash of solutions for this, and it would be really good if there was one tried and tested solutiom that worked across all platforms, alas currently there's not - POSIX being the one that aims for the most consistency.
It's a bit of a stab in the dark, but it might be useful as well to set nonblocking on your socket handle ( i.e. set socket option O_NONBLOCK ) as well as using SIGUSR1
If I get some time I'll work with your socket sample and see if I can get anything out of that too.
Best of luck.
OSX 允许您通过 (CF)RunLoop 使用套接字。或者从运行循环获取回调。
这是我发现在 Mac 上使用异步 IO 的最优雅的方式。
您可以使用现有的套接字并执行 CFSocketCreateWithNative。并在您的运行循环上注册回调。
这是一小段代码,显示了如何设置它,由于我已经减少了源文件,因此不完整......
OSX Allows you to use sockets via the (CF)RunLoop. Or getting callbacks from the runloop.
That is the most elegant way I have found to use async IO on mac.
You can use your existing socket and do a CFSocketCreateWithNative. And register callbacks on your runloop.
Here is a small snippet of code that shows how it can be setup, incomplete since I have cut down on a source file...