在栈上准备了一个65536字节的extrabuf(这个空间是在readFd函数内部定义的,说以说是内部栈空间,在这个函数返回以后这个栈空间就会消失,属于临时变量),然后利用readv(0来读取数据,iovec有两块,第一块指向Muuod Buffer中的writable字节,另一快指向栈上extrabuf。这样如果读入的数据不多,那么全部都读到Buffer中去了;如果长度超过Buffer的writable字节数,就会读到栈上的extrabuf里,然后程序再把extrabuf里的数据append()到Buffer中,代码在8.7.2
这么做利用了临时栈上空间,避免每个连接的初始Buffer过大造成的内存浪费,也避免了反复调用read()的系统开销(由于缓冲区过大,通常一次readv()系统调用就能读完全部数据)。
线程安全:
对于input buffer,onMessage()回调始终发生在该TcpConnnection所属的那个IO线程,应用程序应该在onMessage()完成对input buffer的操作,并且不要把input buffer暴漏给其他线程。这样所有对Input buffer的操作都在同一个线程,
对于output buffer,应用程序不会直接调用它,而是调用TcpConnection::send()来发送数据,后者是线程安全的。
也就是说:对于应用层读取数据来说,用户层使用onMessage()来操作input buffer,但是onMessage是作为回调别handleRead()调用的,所以操作input buffer的操作始终在IO线程中,只要自己库内部不把input buffer暴漏给别的线程,就可以保证安全。对于output buffer来说,对外提供了一个借口send函数,应用层序并没有直接操作outbuffer。由于存在对外接口,所以借口可以在任意线程中使用,对外内部的内部存在对output
buffer的操作。造成两者的去呗在于,一个提供了对外借口,一个没有提供对外借口。如何解决sen的线程安全?
如果TcpConnection::send()发生在该TcpConnection所属的那个IO线程,那么它会转而调用TcpConnection::sendInLoop,sendInLoop()会在当前线程(也就是IO线程)操作output buffer;如果TcpConnection::send()调用发生在别的线程,他不会在当前线程调用sendInLoop(),而是通过EventLoop::runInLoop()把sendInLoop函数调用转移到IO线程,这样sendInLoop还是会在IO线程操作output
buffer,不会有线程安全问题
sendInLoop函数就是负责发送和outputbuffer打交道的函数,
如果调用send函数的线程与这个TcpConnexction不在同一个线程内,那么send函数直接调用runInLoop函数,在这个函数中首先将runInLoop函数注册的回调存放到EventLoop中的函数向量中,让后唤醒这个线程(唤醒的这个下城就是这个TcpConnection所在的线程)让EventLoop的poll阻塞返回,返回后就是处理函数向量的各个回调,由于刚才在runInLoop函数中注册的是sendInLoop函数回调,这么一来,sendInloop函数还是在这个TcpConneciton所在的IO线程内被调用。
所有,即使在某一个线程内调用了另一个线程中的某个TcpConnection上的send函数,也可以让发送数据的事件在此TcpConnection所在的线程中发生。