Unix/Linux上的五种IO模型(UNP6.2)

IO多路复用一般不能和blocking IO用在一起,因为blocking IO中read() write() accept() connect()都有可能阻塞当前线程,这样线程就没办法处理其他socket上的IO事件了

non-blocking IO的核心思想是避免阻塞在read()或write()或其他IO系统调用上,让一个线程能服务于多个socket连接,IO线程只能阻塞在IO多路复用函数上,如select、poll、epoll_wait,这样一来,应用层的缓冲是必须的.

1.TcpConnection必须要有output buffer:write系统调用可能一次不能发送完程序需要发送的数据,此时剩余的数据保存在output buffer中。要让程序在write操作上不阻塞,网络库必须要给每个TCP connection配置output buffer.

2.TcpConnection必须要有input buffer:收到的数据可能不构成一条完整的消息或者一次收到两条消息的数据,收到的数据先放到input buffer里,等构成一条完整的消息再通知程序的业务逻辑(TCP分包)

综上,muduo中的IO都是带缓冲的IO,不会自己去read()或write()某个socket,只会操作TcpConnection的input buffer和output buffer.

Muduo Buffer设计要点:

  • 对外表现为一块连续的内存(char*, len),以方便客户代码的编写。
  • 其 size() 可以自动增长,以适应不同大小的消息。它不是一个 fixed size array (即 char buf[8192]),通过vector自动实现
  • 利用临时栈上空间,如果读入的数据不多,那么全部都读到 Buffer 中去了;如果长度超过 Buffer 的 writable 字节数,就会读到栈上的 stackbuf 里,然后程序再把 stackbuf 里的数据 append 到 Buffer 中。

Buffer的数据结构:

muduo buffer类的设计与使用-LMLPHP

prependable空间,让程序能以很低的代价在数据前面添加几个字节.比方说,程序以固定的4个字节表示消息的长度,我要序列化一个消息,但是不知道它有多长,那么我可以一直 append() 直到序列化完成,然后再在序列化数据的前面添加消息的长度

Buffer的操作:

基本的read-write cycle:

iovec,与readv和wirtev操作相关的结构体,readv和writev函数用于在一次函数调用中读、写多个非连续缓冲区.

struct iovec{
void *iov_base; /* Pointer to data. */
size_t iov_len; /* Length of data. */
};
//写数据到缓冲区,writeIndex后移,从缓冲区中读数据,readIndex后移
ssize_t Buffer::readFd(int fd, int* savedErrno)
{
// saved an ioctl()/FIONREAD call to tell how much to read
char extrabuf[];
struct iovec vec[];
const size_t writable = writableBytes();
vec[].iov_base = begin()+writerIndex_;
vec[].iov_len = writable;
vec[].iov_base = extrabuf;
vec[].iov_len = sizeof extrabuf;
// when there is enough space in this buffer, don't read into extrabuf.
// when extrabuf is used, we read 128k-1 bytes at most.
const int iovcnt = (writable < sizeof extrabuf) ? : ;
const ssize_t n = sockets::readv(fd, vec, iovcnt);
if (n < )
{
*savedErrno = errno;
}
else if (implicit_cast<size_t>(n) <= writable)
{
writerIndex_ += n;
}
else
{
writerIndex_ = buffer_.size();
//将栈中的数据添加到buffer,如果buffer空间不够,需要扩容
append(extrabuf, n - writable);
}
// if (n == writable + sizeof extrabuf)
// {
// goto line_30;
// }
return n;
}

readv()将文件中连续的数据块读入内存分散的缓冲区中。writev()收集内存中分散的若干缓冲区中的数据写至文件的连续区域中。
#include <sys/uio.h>
ssize_t readv(int fildes, const struct iovec *iov, int iovcnt);
ssize_t writev(int fildes, const struct iovec *iov, int iovcnt);

参数iovcnt指出数组iov的元素个数,元素个数至多不超过IOV_MAX,Linux中定义IOV_MAX的值为1024。

05-12 08:10