ceph Timer源码分析

本文深入剖析Ceph定时器的实现原理,包括定时器线程、事件处理及任务调度机制。通过源码分析,详细解读了定时器如何在Ceph中协调osd和monitor的心跳任务。

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

ceph Timer源码分析

ceph定时器主要用来实现某些定时任务,比如osd之间的心跳,monitor之间的心跳等.
源文件:

  1. src/common/timer.h
  2. src/common/timer.cc
  3. src/include/Context.h

定时器事件或者任务

ceph中的事件都继承自Context类,并且实现自己的事件处理方法finish().例如基于定时器的心跳机制就是在finish中每隔一段时间发送一个ping请求.

class Context {
  Context(const Context& other);
  const Context& operator=(const Context& other);
 protected:
 // finishe: 事件的处理方法
  virtual void finish(int r) = 0;
 public:
  Context() {}
  virtual ~Context() {}       // we want a virtual destructor!!!
  virtual void complete(int r) {
    finish(r);
    delete this;
  }
};

定时器线程

定时器线程用于执行定时器任务,具体的任务处理方法由SafeTimer类的timer_thread()定义.

class SafeTimerThread : public Thread {
  SafeTimer *parent;
public:
  explicit SafeTimerThread(SafeTimer *s) : parent(s) {}
  void *entry() {
    parent->timer_thread();
    return NULL;
  }
};

定时器

定时器由定时器线程和定时器任务组成,由定时器线程周期的处理定时器任务.

class SafeTimer:
主要数据成员:
	CephContext *cct
	Mutex &lock
	Cond cond
	bool safe_callbacks
	SafeTimerThread *thread 定时器线程
	// schedule和events都表示定时器任务集,
	// 前者采取时间到事件的映射方式,主要用于定时器线程按时间执行定时器任务.
	// 后都采用事件到迭代器的映射方法,主要用于主线程按事件名取消事件.
	multimap<utime_t, Context*> schedule
	map<Context*, multimap<utime_t, Context *>::iterator> events
	bool stopping 定时器线程的终止状态标志
主要成员函数:
	init():
		thread = new SafeTimerThread(this) 新建定时器线程
		thread->create("safe_timer") 启动定时器线程
	timer_thread(): 定时器线程的执行函数
		确定定时器线程处于执行状态,即stopping为false.
		当事件集schedule不为空时,循环检查事件是否期执行.
		事件在schedule中是按照时间升序排列的.
		如果第一个事件没有到时间,后面的事件就不用检查了,直接终止循环,然后等待.
		如果第一个事件到定时时间,就调用它的finish()进行处理
		处理完当前事件后,等待直到下一个事件的时间.
	shutdown():
		取消全部定时器事件
		销毁定时器线程thread
	add_event_after(double seconds, Context *callback):
		基于相对时间when增加定时器事件
	add_event_at(utime_t when, Context *callback):
		基于绝对时间when增加定时器事件
	cancel_event(Context *callback):
		根据事件名取消定时器事件
	cancel_all_event():
		取消全部定时器事件

question:
定时器线程一个时刻只能处理一个事件,那如果有两个事件时间相同,怎么处理?
理论上一个线程不可能同时执行两个事件,问题的关键在于timer_thread()函数的在执行:定时器事件是按执行时间排序的,因此定时器线程执行事件的过程是,执行一个事件,等待一段时间,执行下一个事件…因此如果两个事件的执行时间相同,那么定时器线程不等待直接执行该事件,由于每个事件的执行时间很短,因此可以近似认为两个事件是同时执行的.这里其实用到了一个小技巧:pthread_cond_timedwait(cond, mutex, time),time是绝对时间,如果time值大于当前时间now,那么线程等待now-time,但如果time小于等于now,那么线程不等待直接执行.

Ceph中,stripe是一种将数据分片存储的概念。当进行文件读取操作时,需要通过一系列的计算来确定数据所在的具体位置。本文以CephFS的文件读取流程为例进行分析。 首先,在文件读取过程中,Ceph会将文件划分为若干个条带(stripe),每个条带由多个对象分片(stripe unit)组成。条带可以看作是逻辑上连续的一维地址空间。 接下来,通过file_to_extent函数将一维坐标转化为三维坐标(objectset,stripeno,stripepos),来确定具体的位置。其中,objectset表示所在的对象集,stripeno表示条带号,stripepos表示条带内的偏移位置。 具体的计算过程如下:假设需要读取的数据的偏移量为offset,每个对象分片的大小为su(stripe unit),每个条带中包含的对象分片数为stripe_count。 首先,计算块号blockno = offset / su,表示数据所在的分片号。 然后,计算条带号stripeno = blockno / stripe_count,表示数据所在的条带号。 接着,计算条带内偏移stripepos = blockno % stripe_count,表示数据在条带内的偏移位置。 接下来,计算对象集号objectsetno = stripeno / stripes_per_object,表示数据所在的对象集号。 最后,计算对象号objectno = objectsetno * stripe_count + stripepos,表示数据所在的对象号。 通过以上计算,可以确定数据在Ceph中的具体位置,从而完成文件读取操作。 需要注意的是,以上分析是基于Ceph版本10.2.2(jewel)进行的,尽管版本跨度较大,但是该部分代码在12.2.10(luminous)版本中仍然比较稳定,基本的框架没有发生变化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值