Implementations of readv and writev
This commit is contained in:
parent
aec561acc5
commit
f025e5f06b
7 changed files with 287 additions and 43 deletions
|
@ -104,7 +104,7 @@
|
||||||
* be reliably traversed in the resolution of
|
* be reliably traversed in the resolution of
|
||||||
* a pathname in the absence of a loop.
|
* a pathname in the absence of a loop.
|
||||||
*/
|
*/
|
||||||
|
#define IOV_MAX INT_MAX /* maximum number of buffers for readv/writev */
|
||||||
#endif /* _POSIX_SOURCE */
|
#endif /* _POSIX_SOURCE */
|
||||||
|
|
||||||
#endif /* _LIMITS_H */
|
#endif /* _LIMITS_H */
|
||||||
|
|
|
@ -15,11 +15,9 @@ struct iovec
|
||||||
size_t iov_len;
|
size_t iov_len;
|
||||||
};
|
};
|
||||||
|
|
||||||
#if 0
|
|
||||||
_PROTOTYPE(ssize_t readv, (int _fildes, const struct iovec *_iov,
|
_PROTOTYPE(ssize_t readv, (int _fildes, const struct iovec *_iov,
|
||||||
int _iovcnt) );
|
int _iovcnt) );
|
||||||
_PROTOTYPE(ssize_t writev, (int _fildes, const struct iovec *_iov,
|
_PROTOTYPE(ssize_t writev, (int _fildes, const struct iovec *_iov,
|
||||||
int iovcnt) );
|
int iovcnt) );
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* _SYS_UIO_H */
|
#endif /* _SYS_UIO_H */
|
||||||
|
|
|
@ -100,6 +100,6 @@ libc_FILES=" \
|
||||||
v8regerror.c \
|
v8regerror.c \
|
||||||
v8regexp.c \
|
v8regexp.c \
|
||||||
v8regsub.c \
|
v8regsub.c \
|
||||||
writev.c"
|
vectorio.c"
|
||||||
|
|
||||||
TYPE=both
|
TYPE=both
|
||||||
|
|
128
lib/other/vectorio.c
Normal file
128
lib/other/vectorio.c
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/uio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#define VECTORIO_READ 1
|
||||||
|
#define VECTORIO_WRITE 2
|
||||||
|
|
||||||
|
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
|
||||||
|
|
||||||
|
static ssize_t vectorio_buffer(int fildes, const struct iovec *iov,
|
||||||
|
int iovcnt, int readwrite, ssize_t totallen)
|
||||||
|
{
|
||||||
|
char *buffer;
|
||||||
|
int iovidx, errno_saved;
|
||||||
|
ssize_t copied, len, r;
|
||||||
|
|
||||||
|
/* allocate buffer */
|
||||||
|
buffer = (char *) malloc(totallen);
|
||||||
|
if (!buffer)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* perform the actual read/write for the entire buffer */
|
||||||
|
switch (readwrite)
|
||||||
|
{
|
||||||
|
case VECTORIO_READ:
|
||||||
|
/* first read, then copy buffers (only part read) */
|
||||||
|
r = read(fildes, buffer, totallen);
|
||||||
|
|
||||||
|
copied = 0;
|
||||||
|
iovidx = 0;
|
||||||
|
while (copied < r)
|
||||||
|
{
|
||||||
|
assert(iovidx < iovcnt);
|
||||||
|
len = MIN(r - copied, iov[iovidx].iov_len);
|
||||||
|
memcpy(iov[iovidx++].iov_base, buffer + copied, len);
|
||||||
|
copied += len;
|
||||||
|
}
|
||||||
|
assert(r < 0 || r == copied);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VECTORIO_WRITE:
|
||||||
|
/* first copy buffers, then write */
|
||||||
|
copied = 0;
|
||||||
|
for (iovidx = 0; iovidx < iovcnt; iovidx++)
|
||||||
|
{
|
||||||
|
memcpy(buffer + copied, iov[iovidx].iov_base,
|
||||||
|
iov[iovidx].iov_len);
|
||||||
|
copied += iov[iovidx].iov_len;
|
||||||
|
}
|
||||||
|
assert(copied == totallen);
|
||||||
|
|
||||||
|
r = write(fildes, buffer, totallen);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
errno = EINVAL;
|
||||||
|
r = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* free the buffer, keeping errno unchanged */
|
||||||
|
errno_saved = errno;
|
||||||
|
free(buffer);
|
||||||
|
errno = errno_saved;
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t vectorio(int fildes, const struct iovec *iov,
|
||||||
|
int iovcnt, int readwrite)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct stat statbuf;
|
||||||
|
ssize_t totallen;
|
||||||
|
|
||||||
|
/* parameter sanity checks */
|
||||||
|
if (iovcnt > IOV_MAX)
|
||||||
|
{
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
totallen = 0;
|
||||||
|
for (i = 0; i < iovcnt; i++)
|
||||||
|
{
|
||||||
|
/* don't read/write anything in case of possible overflow */
|
||||||
|
if ((ssize_t) (totallen + iov[i].iov_len) < totallen)
|
||||||
|
{
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
totallen += iov[i].iov_len;
|
||||||
|
|
||||||
|
/* report on NULL pointers */
|
||||||
|
if (iov[i].iov_len && !iov[i].iov_base)
|
||||||
|
{
|
||||||
|
errno = EFAULT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* anything to do? */
|
||||||
|
if (totallen == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* there aught to be a system call here; instead we use an intermediate
|
||||||
|
* buffer; this is preferred over multiple read/write calls because
|
||||||
|
* this function has to be atomic
|
||||||
|
*/
|
||||||
|
return vectorio_buffer(fildes, iov, iovcnt, readwrite, totallen);
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t readv(int fildes, const struct iovec *iov, int iovcnt)
|
||||||
|
{
|
||||||
|
return vectorio(fildes, iov, iovcnt, VECTORIO_READ);
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t writev(int fildes, const struct iovec *iov, int iovcnt)
|
||||||
|
{
|
||||||
|
return vectorio(fildes, iov, iovcnt, VECTORIO_WRITE);
|
||||||
|
}
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
#include <errno.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <sys/uio.h>
|
|
||||||
|
|
||||||
ssize_t writev(int fildes, const struct iovec *iov, int iovcnt)
|
|
||||||
{
|
|
||||||
#if DEBUG
|
|
||||||
fprintf(stderr, "bind: not implemented for fd %d\n", socket);
|
|
||||||
#endif
|
|
||||||
errno= ENOSYS;
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
int i, r;
|
|
||||||
char *p;
|
|
||||||
ssize_t l, sum;
|
|
||||||
|
|
||||||
/* We should buffer */
|
|
||||||
sum= 0;
|
|
||||||
for (i= 0; i<iovcnt; i++)
|
|
||||||
{
|
|
||||||
p= iov[i].iov_base;
|
|
||||||
l= iov[i].iov_len;
|
|
||||||
while (l > 0)
|
|
||||||
{
|
|
||||||
r= write(fildes, p, l);
|
|
||||||
if (r <= 0)
|
|
||||||
{
|
|
||||||
assert(sum == 0);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
p += r;
|
|
||||||
l -= r;
|
|
||||||
sum += r;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return sum;
|
|
||||||
#endif
|
|
||||||
}
|
|
28
man/man3/readv.3
Normal file
28
man/man3/readv.3
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
.TH READV 3 "January 6, 2009"
|
||||||
|
.UC 4
|
||||||
|
.SH NAME
|
||||||
|
readv, writev \- vector-based IO
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.nf
|
||||||
|
.ft B
|
||||||
|
#include <sys/uio.h>
|
||||||
|
|
||||||
|
ssize_t readv(int \fIfildes\fP, const struct iovec *\fIiov\fP, int \fIiovcnt\fP);
|
||||||
|
ssize_t writev(int \fIfildes\fP, const struct iovec *\fIiov\fP, int \fIiovcnt\fP);
|
||||||
|
.fi
|
||||||
|
.SH DESCRIPTION
|
||||||
|
The \fBreadv\fP and \fBwritev\fP functions allow one to use multiple buffers
|
||||||
|
when reading from or writing to files. The \fIfildes\fP parameter specifies the
|
||||||
|
file descriptor as with the \fBread\fP and \fBwrite\fP functions. \fIiov\fP
|
||||||
|
specifies an array of buffers to be read into or written from. For each element
|
||||||
|
of this array, the iov_base member specifies the address of the buffer and
|
||||||
|
iov_len specifies its size in bytes. The number of buffers is specified by
|
||||||
|
\fIiovcnt\fP. At most IOV_MAX buffers may be specified and their total size may
|
||||||
|
not exceed SSIZE_MAX (both constants are defined in limits.h).
|
||||||
|
.SH "RETURN VALUE"
|
||||||
|
In case of success, the total number of bytes read or written is returned.
|
||||||
|
Zero may be returned if no buffers were specified or each buffer has size zero.
|
||||||
|
In the case of writev, a return value zero may also indicate an end of file
|
||||||
|
condition. If the functions fail, -1 is returned.
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
read(2), write(2)
|
129
test/test18.c
129
test/test18.c
|
@ -14,6 +14,8 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <sys/uio.h>
|
||||||
|
|
||||||
#define NOCRASH 1 /* test11(), 2nd pipe */
|
#define NOCRASH 1 /* test11(), 2nd pipe */
|
||||||
#define PDPNOHANG 1 /* test03(), write_standards() */
|
#define PDPNOHANG 1 /* test03(), write_standards() */
|
||||||
|
@ -45,7 +47,9 @@
|
||||||
#define UMASK "umask"
|
#define UMASK "umask"
|
||||||
#define CREAT "creat"
|
#define CREAT "creat"
|
||||||
#define WRITE "write"
|
#define WRITE "write"
|
||||||
|
#define WRITEV "writev"
|
||||||
#define READ "read"
|
#define READ "read"
|
||||||
|
#define READV "readv"
|
||||||
#define OPEN "open"
|
#define OPEN "open"
|
||||||
#define CLOSE "close"
|
#define CLOSE "close"
|
||||||
#define LSEEK "lseek"
|
#define LSEEK "lseek"
|
||||||
|
@ -92,6 +96,12 @@ _PROTOTYPE(void try_open, (char *fname, int mode, int test));
|
||||||
_PROTOTYPE(void test06, (void));
|
_PROTOTYPE(void test06, (void));
|
||||||
_PROTOTYPE(void test07, (void));
|
_PROTOTYPE(void test07, (void));
|
||||||
_PROTOTYPE(void access_standards, (void));
|
_PROTOTYPE(void access_standards, (void));
|
||||||
|
_PROTOTYPE(void test08, (void));
|
||||||
|
_PROTOTYPE(static int iovec_is_equal,
|
||||||
|
(struct iovec *x, struct iovec *y, size_t size));
|
||||||
|
_PROTOTYPE(static size_t iovec_setup,
|
||||||
|
(int pattern, struct iovec *iovec, char *buffer, int count));
|
||||||
|
_PROTOTYPE(static int power, (int base, int exponent));
|
||||||
_PROTOTYPE(void try_access, (char *fname, int mode, int test));
|
_PROTOTYPE(void try_access, (char *fname, int mode, int test));
|
||||||
_PROTOTYPE(void e, (char *string));
|
_PROTOTYPE(void e, (char *string));
|
||||||
_PROTOTYPE(void nlcr, (void));
|
_PROTOTYPE(void nlcr, (void));
|
||||||
|
@ -159,6 +169,7 @@ void test()
|
||||||
test05();
|
test05();
|
||||||
test06();
|
test06();
|
||||||
test07();
|
test07();
|
||||||
|
test08();
|
||||||
umask(022);
|
umask(022);
|
||||||
} /* test */
|
} /* test */
|
||||||
|
|
||||||
|
@ -855,6 +866,124 @@ char *fname;
|
||||||
close_alot, clean_up_the_mess.
|
close_alot, clean_up_the_mess.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* test READV/WRITEV *
|
||||||
|
****************************************************************************/
|
||||||
|
#define TEST8_BUFSZCOUNT 3
|
||||||
|
#define TEST8_BUFSZMAX 65536
|
||||||
|
#define TEST8_IOVCOUNT 4
|
||||||
|
|
||||||
|
void test08()
|
||||||
|
{
|
||||||
|
char buffer_read[TEST8_IOVCOUNT * TEST8_BUFSZMAX];
|
||||||
|
char buffer_write[TEST8_IOVCOUNT * TEST8_BUFSZMAX];
|
||||||
|
struct iovec iovec_read[TEST8_IOVCOUNT];
|
||||||
|
struct iovec iovec_write[TEST8_IOVCOUNT];
|
||||||
|
int fd, i, j, k, l, m;
|
||||||
|
ssize_t sz_read, sz_write;
|
||||||
|
size_t sz_read_exp, sz_read_sum, sz_write_sum;
|
||||||
|
|
||||||
|
/* try various combinations of buffer sizes */
|
||||||
|
for (i = 0; i <= TEST8_IOVCOUNT; i++)
|
||||||
|
for (j = 0; j < power(TEST8_BUFSZCOUNT, i); j++)
|
||||||
|
for (k = 0; k <= TEST8_IOVCOUNT; k++)
|
||||||
|
for (l = 0; l < power(TEST8_BUFSZCOUNT, k); l++)
|
||||||
|
{
|
||||||
|
/* put data in the buffers */
|
||||||
|
for (m = 0; m < sizeof(buffer_write); m++)
|
||||||
|
{
|
||||||
|
buffer_write[m] = m ^ (m >> 8);
|
||||||
|
buffer_read[m] = ~buffer_write[m];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set up the vectors to point to the buffers */
|
||||||
|
sz_read_sum = iovec_setup(j, iovec_read, buffer_read, i);
|
||||||
|
sz_write_sum = iovec_setup(l, iovec_write, buffer_write, k);
|
||||||
|
sz_read_exp = (sz_read_sum < sz_write_sum) ?
|
||||||
|
sz_read_sum : sz_write_sum;
|
||||||
|
|
||||||
|
/* test reading and writing */
|
||||||
|
if ((fd = open("file08", O_RDWR | O_CREAT | O_TRUNC, 0644)) < 0)
|
||||||
|
err(13, OPEN, "'file08'");
|
||||||
|
else {
|
||||||
|
sz_write = writev(fd, iovec_write, k);
|
||||||
|
if (sz_write != sz_write_sum) err(5, WRITEV, "'file08'");
|
||||||
|
if (lseek(fd, 0, SEEK_SET) != 0) err(5, LSEEK, "'file08'");
|
||||||
|
sz_read = readv(fd, iovec_read, i);
|
||||||
|
if (sz_read != sz_read_exp)
|
||||||
|
err(5, READV, "'file08'");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!iovec_is_equal(iovec_read, iovec_write, sz_read))
|
||||||
|
err(8, READV, "'file08'");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove testfile */
|
||||||
|
Remove(fd, "file08");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} /* test08 */
|
||||||
|
|
||||||
|
static int iovec_is_equal(struct iovec *x, struct iovec *y, size_t size)
|
||||||
|
{
|
||||||
|
int xpos = 0, xvec = 0, ypos = 0, yvec = 0;
|
||||||
|
|
||||||
|
/* compare byte by byte */
|
||||||
|
while (size-- > 0)
|
||||||
|
{
|
||||||
|
/* skip over zero-byte buffers and those that have been completed */
|
||||||
|
while (xpos >= x[xvec].iov_len)
|
||||||
|
{
|
||||||
|
xpos -= x[xvec++].iov_len;
|
||||||
|
assert(xvec < TEST8_IOVCOUNT);
|
||||||
|
}
|
||||||
|
while (ypos >= y[yvec].iov_len)
|
||||||
|
{
|
||||||
|
ypos -= y[yvec++].iov_len;
|
||||||
|
assert(yvec < TEST8_IOVCOUNT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* compare */
|
||||||
|
if (((char *) x[xvec].iov_base)[xpos++] !=
|
||||||
|
((char *) y[yvec].iov_base)[ypos++])
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* no difference found */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t iovec_setup(int pattern, struct iovec *iovec, char *buffer, int count)
|
||||||
|
{
|
||||||
|
static const size_t bufsizes[TEST8_BUFSZCOUNT] = { 0, 1, TEST8_BUFSZMAX };
|
||||||
|
int i;
|
||||||
|
size_t sum = 0;
|
||||||
|
|
||||||
|
/* the pattern specifies each buffer */
|
||||||
|
for (i = 0; i < TEST8_IOVCOUNT; i++)
|
||||||
|
{
|
||||||
|
iovec->iov_base = buffer;
|
||||||
|
sum += iovec->iov_len = bufsizes[pattern % TEST8_BUFSZCOUNT];
|
||||||
|
|
||||||
|
iovec++;
|
||||||
|
buffer += TEST8_BUFSZMAX;
|
||||||
|
pattern /= TEST8_BUFSZCOUNT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int power(int base, int exponent)
|
||||||
|
{
|
||||||
|
int result = 1;
|
||||||
|
|
||||||
|
/* compute base^exponent */
|
||||||
|
while (exponent-- > 0)
|
||||||
|
result *= base;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/***********************************************************************
|
/***********************************************************************
|
||||||
* EXTENDED FIONS *
|
* EXTENDED FIONS *
|
||||||
**********************************************************************/
|
**********************************************************************/
|
||||||
|
|
Loading…
Reference in a new issue