On Wed, Sep 07, 2005 at 09:22:59AM +0400, Evgeniy Polyakov ([EMAIL PROTECTED]) wrote: > On Tue, Sep 06, 2005 at 08:57:57PM -0700, David S. Miller ([EMAIL PROTECTED]) > wrote: > > From: Evgeniy Polyakov <[EMAIL PROTECTED]> > > Date: Fri, 2 Sep 2005 18:00:55 +0400 > > > > > sock_sendfile() and generic_file_sendpage() were implemented > > > and presented in the attached patch. > > > Such methods allows to use sendfile() for any file descriptor <-> file > > > descriptor usage, especially usefull it is in the case socket -> file, > > > when there are no copy_from_user() cases when writing the data. > > > > I do not understand the socket sendfile() implementation, you > > seem to just be looping back the data to recvmsg(). How does > > this work? > > It does recvmsg(), but main purpose was to not copy this into userspace, > when destination is file descriptor/socket. > It does memcpy() unfortunately, but eliminating such a copy will require > completely new system call, like recvfile(), which will first prepare a > page, and then doing network receiving into it.
Using recv()/write() is about 5% slower in one thread and about 8-10% slower using two threads compared to new sendfile() on 1+1 hyperthreaded machine. Server uses sendfile. recv()/write(): 3m45.424s 659905536 3m50.352s 659905536 receiving sendfile(): 3m29.432s 659905536 3m33.065s 659905536 Programs attached for intrested readers. Eliminating data copying in generic_file_sendpage() requires creating new system call recvfile(), which will grap page from file and write directly into it. This can be done as second iteration on top of presented approach. -- Evgeniy Polyakov
#include <sys/sendfile.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/stat.h> #include <fcntl.h> #include <netdb.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <unistd.h> #define ulog_err(f, a...) fprintf(stderr, f ": %s [%d].\n", ##a, strerror(errno), errno) #define ulog(f, a...) fprintf(stderr, f, ##a) int create_socket(char *addr, unsigned short port) { int s; struct hostent *h; struct sockaddr_in sa; h = gethostbyname(addr); if (!h) { ulog_err("gethostbyname"); return -1; } s = socket(AF_INET, SOCK_STREAM, 0); if (s == -1) { ulog_err("Failed to create a socket"); return -1; } sa.sin_family = AF_INET; sa.sin_port = htons(port); memcpy(&sa.sin_addr.s_addr, h->h_addr_list[0], 4); if (connect(s, (struct sockaddr *)&sa, sizeof(sa)) == -1) { ulog_err("connect"); close(s); return -1; } return s; } static ssize_t test_write(int in, int s, int count) { char *buf, *ptr; int err, err1; int sz = 4096; ssize_t bytes = 0; buf = malloc(sz); if (!buf) { ulog("Failed to allocate buffer of %d bytes.\n", count); return -ENOMEM; } while (1) { err = recv(s, buf, sz, 0); if (err <= 0) { ulog_err("recv"); break; } count -= err; bytes += err; ptr = buf; while (err) { err1 = write(in, ptr, err); if (err1 <= 0) break; err -= err1; ptr += err1; } } free(buf); return bytes; } static ssize_t test_sendfile(int in, int s, int count) { int err; ssize_t bytes = 0; while (1) { err = sendfile(in, s, NULL, count); if (err <= 0) { ulog_err("sendfile"); break; } bytes += err; } return bytes; } int main(int argc, char *argv[]) { int s, in; size_t count = 1024*1024*1000; int type; ssize_t bytes; if (argc < 5) { ulog("Usage: %s ip port file type\n", argv[0]); return -1; } type = atoi(argv[4]); s = create_socket(argv[1], atoi(argv[2])); if (s < 0) return s; in = open(argv[3], O_RDWR | O_TRUNC | O_CREAT, 0644); if (in == -1) { ulog_err("open"); return -1; } if (type == 0) bytes = test_sendfile(in, s, count); else bytes = test_write(in, s, count); ulog("%zd bytes.\n", bytes); close(s); close(in); return 0; }
#include <sys/sendfile.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/stat.h> #include <fcntl.h> #include <netdb.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <unistd.h> #define ulog_err(f, a...) fprintf(stderr, f ": %s [%d].\n", ##a, strerror(errno), errno) #define ulog(f, a...) fprintf(stderr, f, ##a) int create_socket(char *addr, unsigned short port) { int s; struct hostent *h; struct sockaddr_in sa; h = gethostbyname(addr); if (!h) { ulog_err("gethostbyname"); return -1; } s = socket(AF_INET, SOCK_STREAM, 0); if (s == -1) { ulog_err("Failed to create a socket"); return -1; } sa.sin_family = AF_INET; sa.sin_port = htons(port); memcpy(&sa.sin_addr.s_addr, h->h_addr_list[0], 4); if (bind(s, (struct sockaddr *)&sa, sizeof(sa)) == -1) { ulog_err("bind"); close(s); return -1; } if (listen(s, 10)) { ulog_err("Failed to make socket listen"); close(s); return -1; } return s; } int main(int argc, char *argv[]) { int s, in, err; size_t count = 1024*1024*1000; if (argc < 4) { ulog("Usage: %s ip port file\n", argv[0]); return -1; } s = create_socket(argv[1], atoi(argv[2])); if (s < 0) return s; while (1) { int cs; struct sockaddr_in csin; socklen_t len = sizeof(csin); in = open(argv[3], O_RDONLY); if (in == -1) { ulog_err("open"); return -1; } cs = accept(s, (struct sockaddr *)&csin, &len); if (cs == -1) { ulog_err("accept"); close(in); continue; } err = sendfile(cs, in, NULL, count); if (err <= 0) { ulog_err("sendfile"); close(in); continue; } ulog("%d bytes were written.\n", err); close(in); close(cs); } close(s); close(in); return 0; }