我的班级IOBuffer
管理内存缓冲区。它有一个方法grow()
,可以增加底层缓冲区。
template<class T>
class IOBuffer
{
public:
typedef T value_type;
typedef T * pointer_type;
typedef long size_type;
IOBuffer(size_type size = 2048, const void * data = nullptr)
: data_(), eod_(), begin_(), end_()
{
grow(size);
if (data)
{
memcpy(data_, data, size);
push(size);
}
}
IOBuffer(const IOBuffer & rhs)
{
size_type capacity = rhs.capacity();
size_type size = rhs.size();
data_ = new value_type[capacity];
eod_ = data_ + capacity;
begin_ = data_;
end_ = data_ + size;
memcpy(data_, rhs.begin_, size);
}
IOBuffer(IOBuffer && rhs)
: data_(rhs.data_), eod_(rhs.eod_),
begin_(rhs.begin_), end_(rhs.end_)
{
rhs.data_ = nullptr;
}
IOBuffer & operator =(const IOBuffer & rhs)
{
const size_type & sz = rhs.size();
begin_ = data_;
grow(sz);
memcpy(begin_, rhs.begin_, sz);
end_ = begin_ + sz;
return *this;
}
IOBuffer & operator =(IOBuffer &&rhs)
{
std::swap(data_, rhs.data_);
std::swap(eod_, rhs.eod_);
std::swap(begin_, rhs.begin_);
std::swap(end_, rhs.end_);
return *this;
}
~IOBuffer()
{
delete[] data_;
}
pointer_type data() const
{
return begin_;
}
template<typename U>
const U * data_as() const
{
return reinterpret_cast<U*>(begin_);
}
size_type size() const
{
return end_ - begin_;
}
size_type capacity() const
{
return eod_ - data_;
}
pointer_type end() const
{
return end_;
}
size_type available() const
{
return eod_ - end_;
}
bool empty() const
{
return end_ == begin_;
}
bool grow(size_type newSize)
{
if (capacity() < newSize)
{
const size_type end = size();
pointer_type temp = new value_type[newSize];
if (data_ && end)
{
memcpy(temp, begin_, end * sizeof(value_type));
}
delete[] data_;
data_ = temp;
eod_ = data_ + newSize;
begin_ = data_;
end_ = data_ + end;
return true;
}
else if(data_ != begin_)
{
const size_type end = size();
memmove(data_, begin_, end * sizeof(value_type));
begin_ = data_;
end_ = begin_ + end;
}
return false;
}
size_type push(size_type n)
{
if (n > 0)
end_ += n;
assert(end_ >= data_ && end_ <= eod_);
return n;
}
size_type consume(size_type n)
{
if (n > 0)
{
n = std::min(n, size());
begin_ += n;
assert(begin_ >= data_ && begin_ <= eod_);
}
return n;
}
void clear()
{
begin_ = end_ = data_;
}
private:
pointer_type data_;
pointer_type eod_;
pointer_type begin_;
pointer_type end_;
};
奇怪的是,每天都会发生这种情况:
*** glibc detected *** ./mdrelay: free(): corrupted unsorted chunks: 0x0000000001c48db0 ***
给我一个核心转储。调试该转储(使用调试符号,我得到此跟踪)
#0 0x0000003510832925 in raise (sig=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
#1 0x0000003510834105 in abort () at abort.c:92
#2 0x0000003510870837 in __libc_message (do_abort=2,
fmt=0x3510958ac0 "*** glibc detected *** %s: %s: 0x%s ***\n")
at ../sysdeps/unix/sysv/linux/libc_fatal.c:198
#3 0x0000003510876166 in malloc_printerr (action=3,
str=0x3510958e48 "free(): corrupted unsorted chunks", ptr=<value optimized out>)
at malloc.c:6336
#4 0x0000003510878ca3 in _int_free (av=0x3510b8fe80, p=0x1c48da0, have_lock=0) at malloc.c:4832
#5 0x000000000042c529 in IOBuffer<unsigned char>::grow (this=0x1c48d18, newSize=4096)
at IOBuffer.hpp:125
和第125行是delete[] data_
里面的方法增长。当我打印出data_ points:
(gdb) p data_
$1 = (IOBuffer<unsigned char>::pointer_type) 0x1c495c0
这是正确的
我很困惑我如何调用delete[]
指向0x1c495c0
和_int_free()
收到0x1c48da0
。这些指针之间的差异是2080,原始指针和崩溃地址之间的差异是2064.另外,(找不到CentOS的glibc 2.12-1.132.el6_5.1中使用的operator new [] / delete []的细节。和libstdc ++ 4.7.3,不是库存一个),但我发现2064出现在内存中,就在data_指向的地址之前。这将排除指针损坏。
(gdb) x/2dg data_ - 0x10
0x1c495b0: 2064 2064
(gdb) print (long)data_ - 0x1c48db0
$4 = 2064
(gdb) print (long)data_ - 0x1c48da0
$5 = 2080
可能运算符delete()在编译期间得到优化(没有堆栈帧),我无法跟踪delete []和_int_free()之间指针的发生情况。关于此事的任何想法?
编辑:添加一些上下文,这个类只是我想到的一种方法,无需复制即可管理recv()或read()等函数的缓冲区。它有四个指针:data_,eod_(缓冲区空间的开始和结束)以及begin_和end_(传入数据的开始和结束)。当函数read()返回n个字节时,end_由此n前进。因此,size()是end_ - begin_,是上次read()的大小。在处理缓冲区时,begin_ advance直到它遇到end_。如果在缓冲区中留下了某些内容,因为end_和eod_之间没有足够的空间(或者begin_遇到end_),则调用grow()为下一个read()释放空间。当达到限制时,它可以增加整体缓冲区大小或仅将剩余数据移动到缓冲区空间的开头。答案 0 :(得分:0)
从场景中,我认为你可能会在其他地方写入超出data_ buffer范围的数据,你可能需要检查这个函数的代码。
另一方面,当调用memcpy / memmove时,你确定结束变量单位是字节,你需要从头到尾更改它* sizeof(value_type)吗?
答案 1 :(得分:0)
由于深思熟虑的评论,帖子中的课程版本看起来很稳定。
该类的早期版本在available()
中报告的缓冲区大于实际分配的空间,recv()
导致缓冲区溢出。