muduo源码学习笔记(2)

本文详细介绍了muduo库中LogStream、FixedBuffer和Fmt的实现,以及Logger类的工作机制,包括如何初始化、写入数据并在析构时输出。文章还探讨了日志级别、日志输出到文件的策略,如文件命名、日志滚动和崩溃处理。通过对muduo源码的学习,揭示了其高效和灵活的日志管理方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:

​ 对于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)
    
### 关于Chun哥手写的muduo课程笔记 #### muduo简介 muduo是在Linux环境下使用C++实现的一个多Reactor多线程的高性能网络服务器[^1]。该由陈硕开发,并且他撰写了一本书《Linux多线程服务端编程:使用muduo C++网络》,深入介绍了此的设计理念与应用实例。 #### Buffer类解析 Buffer类主要用于封装用户缓冲区并提供一系列用于操作这些数据的方法。重点在于理解`Buffer`类中的读写机制、内部调整逻辑以及动态扩容特性[^2]。以下是简化版`Buffer`类的部分代码: ```cpp class Buffer { public: void append(const char* data, size_t len); ssize_t readFd(int fd, int* savedErrno); private: std::vector<char> buffer_; }; ``` #### Reactor模式下的关键组件分析 在muduo框架内,Reactor模式通过几个重要组成部分得以体现,即`EventLoop`, `Poller` 和 `Channel` 类之间的协作关系。具体来说,`EventLoop` 作为整个系统的中心调度者;而`Poller`则负责监听文件描述符上的事件变化情况;最后,`Channel`对象表示单个I/O通道的状态和行为[^3]。 对于接受新连接的功能模块而言,会创建一个名为`acceptChannel_`的对象来专门处理来自客户端的新请求。它不仅包含了实际套接字资源(`acceptSocket_`)的信息,还定义了当有新的TCP连接到来时应该执行的动作[^4]。 #### Epoll接口调用说明 为了高效管理大量并发连接,在Linux系统上通常采用epoll机制来进行I/O多路复用。这里给出了一个简单的例子展示如何向epoll实例注册感兴趣的文件描述符及其对应的操作类型[^5]: ```c++ // 向epoll实例添加/修改/删除监视项 int ctlResult = ::epoll_ctl(epfd_, EPOLL_CTL_ADD | EPOLL_CTL_MOD | EPOLL_CTL_DEL, sockfd_, &event_); if (ctlResult < 0) { // 错误处理... } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值