0x01 缘起
在高性能服务器开发的过程中,良好的IO缓存是整个设计比较重要的环节。带着这方面的兴趣,阅读了开源ISE的IO Buffer设计,不评估其优劣,纯粹学习这方面知识,深度学习下STL vector容器的使用和内存的管理。
0x02 IO Buffer介绍
消息缓存,主要存储socket接收和发送的消息。发送消息时,将消息有序的缓存到buffer中;消息接收时,将消息存放在缓存中,进行拼包,还原出完整的消息。
0x03 ISE IO Buffer
///
// class IoBuffer - 输入输出缓存
//
// +-----------------+------------------+------------------+
// | useless bytes | readable bytes | writable bytes |
// | | (CONTENT) | |
// +-----------------+------------------+------------------+
// | | | |
// 0 <= readerIndex <= writerIndex <= size
class IoBuffer
{
public:
enum { INITIAL_SIZE = 1024 }; //Buffer初始化大小
public:
IoBuffer();
~IoBuffer();
/* const 成员函数,不能修改类成员;
* 获取可以读的有效字节;
* */
int getReadableBytes() const { return writerIndex_ - readerIndex_; }
/*
* 获取可以写的有效空间字节数;
* */
int getWritableBytes() const { return (int)buffer_.size() - writerIndex_; }
/*
* 获取可以目前已经读取完,认为无效的空间字节数;
* */
int getUselessBytes() const { return readerIndex_; }
/*
* 追加一个字符串对象到缓存;
* */
void append(const string& str);
/*
* 追加一个指针对象 bytes个字节到缓存;
* */
void append(const void *data, int bytes);
/*
* 向缓存追加 bytes 个字节并填充为'\0';
* */
void append(int bytes);
/*从缓存读取 bytes 个字节数据*/
void retrieve(int bytes);
/*从缓存读取全部可读数据并存入 str 中*/
void retrieveAll(string& str);
/*从缓存读取全部可读数据,相当于清空*/
void retrieveAll();
/*交换一个IO缓存*/
void swap(IoBuffer& rhs);
/* 获取读的起始指针 ,访问一个只读指针 */
const char* peek() const { return getBufferPtr() + readerIndex_; }
private:
/* 获取缓存的起始指针 */
char* getBufferPtr() const { return (char*)&*buffer_.begin(); }
/* 获取写位置的起始指针 */
char* getWriterPtr() const { return getBufferPtr() + writerIndex_; }
/* 扩展缓存 */
void makeSpace(int moreBytes);
private:
std::vector<char> buffer_; //buffer 缓存容器,内存的扩展
int readerIndex_; //读指针
int writerIndex_; //写指针
};
关键代码段,内存扩展:
//-----------------------------------------------------------------------------
// 描述: 扩展缓存空间以便可再写进 moreBytes 个字节
//-----------------------------------------------------------------------------
void IoBuffer::makeSpace(int moreBytes)
{
/*如果无用的字节空间和可以写的字节空间小于需要写入的空间,就调用resize调整扩大
* vector容量,满足值的写入;
* vector容量会成倍扩张,INITIAL_SIZE*2,
* 并将原来的从就的地方拷贝到新的;
*/
if (getWritableBytes() + getUselessBytes() < moreBytes)
{
buffer_.resize(writerIndex_ + moreBytes);
}
else
{
/*如果大于,调整目前的位置,将内存移动一次,将全部可读数据移至缓存开始处*/
int readableBytes = getReadableBytes();
char *buffer = getBufferPtr();
memmove(buffer, buffer + readerIndex_, readableBytes);
readerIndex_ = 0;
writerIndex_ = readerIndex_ + readableBytes;
ISE_ASSERT(readableBytes == getReadableBytes());
}
}
后面将详细讲解vector扩容等知识点。
0x04 STL vector
类关键片段:
// 默认allocator为alloc, 其具体使用版本请参照<stl_alloc.h>
template <class T, class Alloc = alloc>
class vector
{
public:
// 标记为'STL标准强制要求'的typedefs用于提供iterator_traits<I>支持
typedef T value_type; // STL标准强制要求
typedef value_type* pointer; // STL标准强制要求
typedef const value_type* const_pointer;
// 由于vector的特性, 一般我们实作的时候都分配给其连续的内存空间,
// 所以其迭代器只需要定义成原生指针即可满足需要
typedef value_type* iterator; // STL标准强制要求
typedef const value_type* const_iterator;
typedef value_type& reference; // STL标准强制要求
typedef const value_type& const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type; // STL标准强制要求
#ifdef __STL_CLASS_PARTIAL_SPECIALIZATION
typedef reverse_iterator<const_iterator> const_reverse_iterator;
typedef reverse_iterator<iterator> reverse_iterator;
#else /* __STL_CLASS_PARTIAL_SPECIALIZATION */
typedef reverse_iterator<const_iterator, value_type, const_reference,
difference_type> const_reverse_iterator;
typedef reverse_iterator<iterator, value_type, reference, difference_type>
reverse_iterator;
#endif /* __STL_CLASS_PARTIAL_SPECIALIZATION */
protected:
// 这个提供STL标准的allocator接口
typedef simple_alloc<value_type, Alloc> data_allocator;
iterator start; // 内存空间起始点
iterator finish; // 当前使用的内存空间结束点
iterator end_of_storage; // 实际分配内存空间的结束点
void insert_aux(iterator position, const T& x);
....................
}
详细讲解resize函数:
void resize(size_type __new_size, const _Tp& __x) {
if (__new_size < size())
erase(begin() + __new_size, end());
else
insert(end(), __new_size - size(), __x);
}
void resize(size_type __new_size) { resize(__new_size, _Tp()); }
resize()的作用是改变vector中元素的数目:
1、如果n比当前的vector元素数目要小,vector的容量要缩减到resize的第一个参数大小,既n。并移除那些超出n的元素同时销毁他们。
2、如果n比当前vector元素数目要大,在vector的末尾扩展需要的元素数目,如果第二个参数val指定了,扩展的新元素初始化为val的副本,否则按类型默认初始化。
注意:如果n大于当前的vector的容量(是容量,并非vector的size),将会引起自动内存分配。所以现有的pointer,references,iterators将会失效。
0x05 额外补充const
1、const成员函数不允许修改它所在对象的任何一个数据成员,const成员函数能够访问对象的const成员,而其他成员函数则不可以。
2、对于const类对象/指针/引用可以调用const成员函数,但是不可以调用非const类型的成员函数
2、对于const类对象/指针/引用可以调用const成员函数,但是不可以调用非const类型的成员函数