ceph源码分析之线程介绍

Ceph是一款优秀的分布式存储软件,通过学习它的源码,我们可以学到很多编程技巧,ceph中关于多线程的编程,是一个很好的学习对象。

Ceph中线程的在src/common/Thread.h中定义

class Thread {
 private:
  pthread_t thread_id;
 public:
  Thread(const Thread& other);
  const Thread& operator=(const Thread& other);
  Thread();
  virtual ~Thread();
 protected:
  virtual void *entry() = 0;//子类需要实现此函数,作为线程入口
 private:
  static void *_entry_func(void *arg);
 public:
  const pthread_t &get_thread_id();
  bool is_started();
  bool am_self();
  int kill(int signal);//杀掉自己
  int try_create(size_t stacksize);
  void create(size_t stacksize = 0);//创建线程,里面传入了entry函数
  int join(void **prval = 0);//阻塞自己等待线程结束,回收资源
  int detach();//父子线程分离
};

C开发者都知道线程编程接口,一个线程在创建时调用pthread_create函数来传入entry函数,杀死线程调用pthread_kill函数,当线程被杀死之后,必须调用pthread_join函数来进行线程资源的回收,如果不调用此函数,就会出现类似zombie process。如果要想让系统自己回收线程资源,就要将线程与父线程分离即调用pthread_detach.通过接口对比,我们发现src/common/Thread.h中定义的class thread,实际上是对线程代码ceph自己实现的一层封装。


Ceph中所有要用的线程必须继承Thread类,通过查找发现如下一些线程:

Accepter.h (src\msg):class Accepter : public Thread     //用来socket bind的线程,      accepter线程入口函数里定义了poll的网络通讯结构,用来放入管道
Admin_socket.h (src\common):class AdminSocket : public Thread
Ceph_context.cc (src\common):class CephContextServiceThread : public Thread
DispatchQueue.h (src\msg):  class DispatchThread : public Thread   //用来进行消息分发的线程,  在simpleMessenger中有dispatch_queue成员变量,
FileJournal.h (src\os):  class Writer : public Thread     //用来进行写数据到journal中的线程
FileJournal.h (src\os):  class WriteFinisher : public Thread   //当用aio异步模式写数据到journal完成后,此线程用来接管其他剩余操作
FileStore.h (src\os):  struct SyncThread : public Thread    //用来同步数据执行同步的线程,主要是将已经完成的journal的序列号写入到文件中
Finisher.h (src\common):  struct FinisherThread : public Thread   //公用的finisher线程,用来查看某些特定的操作是否结束,结束后进行后续处理工作
MDLog.h (src\mds):  class ReplayThread : public Thread 
OSD.h (src\osd):  struct T_Heartbeat : public Thread   //维系osd进程之间互相心跳连接的线程
OutputDataSocket.h (src\common):class OutputDataSocket : public Thread
Pipe.h (src\msg):    class Reader : public Thread   //用来处理所有对socket的读操作,由acepter线程将socket accept以后打入到SimpleMessenger::dispatch_queue中交由此线程处理
Pipe.h (src\msg):    class Writer : public Thread   //用来处理所有对socket的写操作,由acepter线程将socket accept以后打入到SimpleMessenger::dispatch_queue中交由此线程处理
Pipe.h (src\msg):    class DelayedDelivery: public Thread    //用来处理所有对socket的延时操作
Signal_handler.cc (src\global):struct SignalHandler : public Thread 
SimpleMessenger.h (src\msg):  class ReaperThread : public Thread //用来进行消息通信的主要线程 reaper是用来在通讯完成时拆除管道,其中成员有accepter线程(用来bind,accept socket文件放入管道),还有dispatch_queue线程
Throttle.cc (src\test\common):  class Thread_get : public Thread 
Timer.cc (src\common):class SafeTimerThread : public Thread 
WorkQueue.h (src\common):  struct WorkThread : public Thread 

可以将这些线程分为四类线程

第一类是普通类线程:

使用此类线程类直接申明继承自Thread,重写一个entry函数,在进程启动最初时,调用了create函数创建了线程,同时使用它的人必须自己定义消息队列。上面大部分线程都是此类,比如FileJournal::write_thread就是一个FileJournal::Writer类对象,它自己定义了消息队列FileJournal::writeq


第二类是SafeTimerThread类线程:

此类线程使用者可以直接申明一个SafeTimer成员变量,因为SafeTimer中已经封装了SafeTimerThread类和一个消息队列(成员是Context回调类),并完成了entry函数的逻辑流程。使用者使用方法,就是设置回调函数,通过SafeTimer::add_event_after函数将钩子埋入,等待规定时间到达后执行。


第三类是FinisherThread类线程:

此类线程使用者可以直接申明一个Finisher成员变量,因为Finsher中已经封装了FinisherThread类和一个消息队列(成员是Context回调类),并完成entry函数的逻辑流程。使用者使用方法,就是设置回调函数,通过Finisher::queue函数将钩子埋入,等待某类操作完成后执行。


第四类是ThreadPool内部线程:

这类线程由于是具体工作类线程,所以他们一般都是以线程池形式一下创建多个。ThreadPool类内部有多个线程set和多个消息队列vector组成。工作流程就是线程不断的轮询从队列中拿去数据进行操作(如下图)。

ceph源码分析之线程介绍_第1张图片


先说ThreadPool中的线程是WorkThread类型,它的入口函数是ThreadPool::worker函数,此函数内定义了WorkThread类线程的操作逻辑。基本流程就是轮询所有WorkQueue_,当发现某种类型WorkQueue_中有数据时拿出,然后依次调用该WorkQueue_自己定义的函数_void_process_void_process_finish等函数来顺序执行操作。

void ThreadPool::worker(WorkThread *wt)
{
    ……
    while (!_stop) {
      ……
      if (!_pause && !work_queues.empty()) {
          WorkQueue_* wq;
          int tries = work_queues.size();
          bool did = false;
          while(tries--) {
              last_work_queue++;
              last_work_queue %= work_queues.size();
              wq = work_queues[last_work_queue];
              void *item = wq->_void_dequeue();
              if (item) {
                  ……
                  wq->_void_process(item,tp_handle);
                  ……
                  wq->_void_process_finish(item);
                  ……
             }
            ……
        }
        ……
      }
    ……
}

再说一说ThreadPool中的WorkQueue_,这是一种抽象的类,只定义了一个队列应该有的一些特定的函数,这些函数几乎都是虚函数,目的是为了调用到自己三个子类BatchWorkQueue,WorkQueueVal,WorkQueue自己定义的函数。而在三个子类中对应函数_void_process,_void_process_finish中又分别调用了使用者自己继承它们而自己实现的具体操作函数如_process,_process_finish

struct WorkQueue_ {
    string name;
    time_t timeout_interval, suicide_interval;
    WorkQueue_(string n, time_t ti, time_t sti)
      : name(n), timeout_interval(ti), suicide_interval(sti)
    { }
    virtual ~WorkQueue_() {}
    virtual void _clear() = 0;
    virtual bool _empty() = 0;
    virtual void *_void_dequeue() = 0;
    virtual void _void_process(void *item, TPHandle &handle) = 0;
    virtual void _void_process_finish(void *) = 0;
  };


最后说一下使用者如何使用: 

1.声明线程池成员ThreadPool *_tp

2.声明队列类型ThreadPool::WorkQueue_*_wq

3.重写WorkQueue中对应函数_void_process,_void_process_finish

4.调用*_tp.add_work_queue(*_wq)将队列传入


下图是WorkQueue_拥有三大继承:

ceph源码分析之线程介绍_第2张图片





从上图中我们可以看到ceph中目前本人知道的线程池有五种,OSD中四个,FileStore中一个

op_tp      处理client来的请求

disk_tp    处理scrub操作

recovery_tp处理recovery_tp操作

command_tp 处理命令行来的操作

FileStore::op_tp   处理底层数据操作


可以将上一篇介绍写流程底层操作那篇文章中的线程进行分类,可以发现拥有:

一个普通线程:FileJournal::write_thread

两个finisher线程:Journal::Finisher.finisher_thread,

FileStore::ondisk_finisher.finisher_thread,

FileStore::op_finisher.finisher_thread

两个线程池线程: OSD::op_tp,

FileStore::op_tp

ceph源码分析之线程介绍_第3张图片


你可能感兴趣的:(ceph)