分散聚集I/O

分散/聚集I/O

分散/聚集I/O 是一种可以在单词系统调用中对多个缓冲区输入输出的方法,可以把的一个缓冲区的数据写到单个数据流,也可以把单个数据流读到多个缓冲区中。其命名的原因在于数据会被分散到指定缓冲区向量,或者从指定缓冲区向量中聚集数据。这种输入输出的方法也成为向量I/O。前面所提到的标准读写系统调用可以成为线性I/O

与线性I/O相比,分散聚集I/O有如下优势:

  1. 编码模式更自然
  2. 如果数据本身是分段的,向量IO提供了直观的数据处理方式
  3. 效率更高
  4. 单个向量I/O操作可以取代多个线性I/O操作
  5. 性能更好
  6. 除了减少了发起的系统调用次数,通过内部优化,向量I/O可以比线性I/O提供更好的性能
  7. 支持原子性
  8. 和多个线性I/O操作不同,一个进程可以执行单个向量I/O操作,比秒了和其它进程交叉操作的风险

readv()和writev()

readv()函数从文件描述符fd中读取count个段(一个段即一个iovec结构体)到参数参数iov所指定的缓冲区中:

1
2
3
#include <sys/uio.h>

ssize_t readv (int fd, const struct iovec *iov, int count);

writev()函数冲参数iov指定的缓冲区中读取count个段的数据,并写入fd中:

1
2
#include <sys/uio.h>
ssize_t writev (int fd, const struct iovec *iov, int count);

除了同时操作多个缓冲区外,readv()函数和writev()函数的功能分别和read()和write()的功能一致

每个iovec结构体描述一个独立的/物理不连续的缓冲区,我们称其为段:

1
2
3
4
5
6
#include <sys/uio.h>
struct iovec
{
void *iov_base;
size_t iov_len;
}

一组段的集合称为向量。每个段描述了内存中所要读写的缓冲区的地址和长度。

readv()函数在处理下个缓冲区之前,会填满当前缓冲区的iov_len个字节。

writev()函数在处理下个缓冲区之前,会把当前缓冲区所有iov_len个字节的数据输出

这两个函数都会顺序处理向量中的段,从iov[0]开始,一直到iov[count-1]

返回值

操作成功是,readv()函数和writev()函数分别返回读写的字节数,该字节数应该等于所有count个iov_len的和

出错时,返回-1并设置errno,设置的errno和read()、write()相同。此外还有另外两种错误场景:

  1. 如果所有count个iov_len的和超出ssize_max,则不会处理任何数据,返回-1,并把errno设置为EINVAL
  2. count必须大于0,且小于等于IOV_MAX(1024),如果count为0,则返回0(errno可能设置为EINVAL)。如果超过IOV_MAX,不会处理数据,返回-1,并把errno设置为EINVAL

writev()示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/uio.h>

int main()
{
struct iovec iov[3];
ssize_t nr;
int fd, i;

char *buf[] = {
"The term buccaneer comes from the word boucan.\n",
"A boucan is a wooden frame used for cokking meat.\n",
"Buccaneer is the West Indies name for a pirate.\n"
};

fd = open ("buccaneer.txt", O_WRONLY | O_CREAT | O_TRUNC);
if (fd == -1)
{
perror ("open");
return 1;
}

for (i = 0; i < 3; i++)
{
iov[i].iov_base = buf[i];
iov[i].iov_len = strlen(buf[i]) +1;
}

nr = writev (fd, iov, 3);
if (nr == -1)
{
perror ("writev");
return 1;
}
printf ("wrote %d bytes\n", nr);

if (close (fd))
{
perror ("close");
return 1;
}
}

readv()示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/uio.h>

int main()
{
char foo[48], bar[51], baz[49];
struct iovec iov[3];
ssize_t nr;
int fd, i;

fd = open("buccaneer.txt", O_RDONLY);
if (fd == -1)
{
perror("open");
return 1;
}

iov[0].iov_base = foo;
iov[0].iov_len = sizeof(foo);
iov[1].iov_base = bar;
iov[1].iov_len = sizeof(bar);
iov[2].iov_base = baz;
iov[2].iov_len = sizeof(baz);

nr = readv (fd,iov,3);
if (nr == -1)
{
perror ("readv");
return 1;
}

for (i = 0; i < 3; i++)
{
printf ("%d:%s", i, (char*)iov[i].iov_base);
}

if (close (fd))
{
perror ("close");
return 1;
}
}

分散聚集I/O
https://carl-5535.github.io/2021/02/21/Linux系统编程/分散聚集I-O/
作者
Carl Chen
发布于
2021年2月21日
许可协议