前言:
对于muduo库,我觉得,光Linux多线程上提到的一些实现,还是不够的,在base/里面,还有/net里面提供了很多不错的实现,值得去学习,暑假算是看看muduo的百分之八十的源码,并对其进行了一次实现,(剩下的在最近总结的时候,也会开始看看,并实现一遍),对于muduo库,简单谈谈自己对其实现的理解。
日志缓存流LogStream
在muduo实现的基础日志类,一共用来三个文件,LogFilem, LogStream,Loggin。本着自底向上原则,我们从底层的实现慢慢讲起。
在LogStream中,定义了三个类,两个底层的类分别是FixedBuffer和Fmt。
muduo在实现写日志的时候,并没有直接写向stderr或者IO流,而是会写入自己定义的FixedBuffer(缓冲流中),下面就让我们看看缓冲流的定义和实现吧!
FixedBuffer定义在namespace detail算是用户不可见,在创建FixedBuffer的时候可以向其输出大小来控制内部的buf的大小,也定义两个常量分别对应大小buf
const int kSmallBuffer = 4000; const int kLargeBuffer = 4000 * 1024;
一共有三个成员,data就是缓存的数据,cur指向缓存数据的末尾的下一个和cookie_指针
我们需要重点关注以下函数
//追加字符串 void append(const char* /*restrict*/ buf, size_t len) { // FIXME: append partially if (implicit_cast<size_t>(avail()) > len) { memcpy(cur_, buf, len); cur_ += len; } } //设置缓存 void setCookie(void (*cookie)()) { cookie_ = cookie; };
值得注意的是下面在实现转化的时候在detail中定义了
const char digits[] = "9876543210123456789"; const char* zero = digits + 9; BOOST_STATIC_ASSERT(sizeof(digits) == 20); const char digitsHex[] = "0123456789ABCDEF"; BOOST_STATIC_ASSERT(sizeof digitsHex == 17); // 先用求余除法求出 value转化成字符串的倒着顺序的,然后用std::reverse对其进行倒转 // 另一方面用了取数的方法,相比直接申请赋值会更有效率一些 template<typename T> size_t convert(char buf[], T value) { T i = value; char* p = buf; do { int lsd = static_cast<int>(i % 10); i /= 10; *p++ = zero[lsd]; } while (i != 0); if (value < 0) { *p++ = '-'; } *p = '\0'; std::reverse(buf, p); return p - buf; } /// 同样的实现不过是16进制的而已 size_t convertHex(char buf[], uintptr_t value);
下面我们就开始看看LogStream
它有两个数据成员,一个是缓冲流buffer,另一个是一个成员是表示数值的最大字节数(用于后面的优化)。
在里面重载的若干函数,它是通过一个模板函数,对其进行统一起来书写
template <typename T> void LogStream::formatInteger(T v) { if (buffer_.avail() >= kMaxNumericSize) { size_t len = convert(buffer_.current(), v); buffer_.add(len); } } LogStream& LogStream::operator<<(int v) { formatInteger(v); return *this; }
值得注意的是,里面有一个重载函数的形参是StringPrice。这也是一个亮点的地方,它实现了无论是字符串还是string都可以进行零拷贝数据,只拷贝指针来传递,也是一个不错的方法,具体怎么实现的,看看它的构造函数也就明白了一切。
class StringPiece { public: StringPiece() : ptr_(nullptr), length_(0) {} StringPiece(const char *str)