linux下动态加载文件

#ifndef _WATCHER_H_
#define _WATCHER_H_

#include <sys/time.h>
#include <sys/stat.h>
#include <string>

// We often want to know if a file was just now created or updated (then we
// may reload the file to get latest data). FileWatcher does boilerplate part
// for you as below.
// Example:
//   Watcher fw;
//   fw.create ("path_of_file_to_be_watched");
//   ....
//   if (fw.is_timestamp_updated() > 0) {  // >0 means that the file is created or updated 
//       ......
//       fw.update_timestamp(); // update recorded timestamp
//   }
//
// You may call is_timestamp_updated() multiple times to know latest status
// of the file before calling update_timestamp(), or you may combine calls to
// the two functions by:
//   if (fw.check_and_update_timestamp() > 0) {
//       ....
//   }

class Watcher {
public:
    static const time_t NON_EXIST_TS = (time_t)-1;
    // must be negative
    static const int SKEW = -2;//文件来自未来的异常:文件的时间戳异常或者从时间较快的服务器上拷贝来的文件
    static const int DELETED = -1;
    // must be zero
    static const int UNCHANGED = 0;
    // must be positive
    static const int UPDATED = 1; 
    static const int CREATED = 2;
    
    // Default constructor
    explicit Watcher ()
        : _chk_ts(NON_EXIST_TS) , _last_ts(NON_EXIST_TS)//这里的explicit没有实际意义,可以删除
    {}

    // Create this watcher on a file
    // Returns:
    //   -1   file_path is NULL
    //    0   success
    int create (const char* file_path)
    {
        if (NULL == file_path) {
            FATAL("file_path is NULL");
            return -1;
        }
        
        file_path_ = file_path;//后置下划线。。。好吧。。。

        struct stat tmp_st;
        int ret = stat (file_path_.c_str(), &tmp_st);//为了存储之前的状态
        if (ret < 0) {
            WARN("%s does not exist", file_path_.c_str());
            _chk_ts = NON_EXIST_TS;
            _last_ts = NON_EXIST_TS;
        } else {
            _chk_ts = tmp_st.st_mtime;
            _last_ts = tmp_st.st_mtime;
        }
        return 0;
    }

    // Check if watched file was created/modified/deleted. Recorded timestamp
    // is not changed so it's safe to call this function multiple times, you
    // won't miss an update.
    // Returns:
    //   UPDATED   the file is modified or created since last call to this
    //             function
    //   UNCHANGED the file is not modified or still not existing since last
    //             call to this function
    //   DELETED   the file was deleted since last call to this function
    //   SKEW      the file exists but the timestamp steps back
    // Note: this method only checks, to tell file_reader_t to forget the change
    //       (because you already processed the file), you must call
    //       update_timestamp(). You may call check_and_update_timestamp() to
    //       combine two calls.
    // Note: If the file is updated too frequently(< 500ms), this method may
    //       return UNCHANGED due to precision of stat(2); If the file is
    //       being updated too frequently for a period of time, this method
    //       should eventually return several UPDATEs; If the file is being 
    //       updated and deleted too frequently, the update events are
    //       probably undetectable. Fortunately, this is unseen in our app.
    int is_timestamp_updated ()
    {
        struct stat tmp_st;
        const int ret = stat (file_path_.c_str(), &tmp_st);
        if (ret < 0) {
            _chk_ts = NON_EXIST_TS;
            if (NON_EXIST_TS != _last_ts) {
                return DELETED;
            } else {
                return UNCHANGED;
            }
        } else {
            _chk_ts = tmp_st.st_mtime;
            if (NON_EXIST_TS != _last_ts) {
                if (_chk_ts > _last_ts) {
                    return UPDATED;
                } else if (_chk_ts == _last_ts) {
                    return UNCHANGED;
                } else {
                    return SKEW;
                }
            } else {
                return CREATED;
            }
        }
    }

    // Change recorded time
    void update_timestamp ()
    {
        _last_ts = _chk_ts;
    }

    // Combine is_timestamp_updated() and update_timestamp()
    int check_and_update_timestamp ()
    {
        const int ret = is_timestamp_updated ();
        update_timestamp ();
        return ret;
    }
    
    // Get path of the watched file
    const char* filepath () const {
        return file_path_.c_str(); 
    }

private:
    std::string file_path_;
    time_t _chk_ts;
    time_t _last_ts;    
};

typedef Watcher watcher_t;

#endif

·代码整体比较简单,其实就是根据时间戳的各种情况做判断。


那么有了监控的程序,如果当文件更新的时候我需要动态加载该文件,怎么处理?因为加载了文件需要更新已经存储的数据,并且这些数据在动态加载的时候还可能被使用。

解决方案自然是开线程监控,使用两个buffer,再任一时刻,客户只能访问其中的一个buffer,而访问哪一个,由加载程序决定。当文件被更新时,将数据更新到没有被使用的buffer中,这样能够保证旧数据的正常访问;当更新完成,并且旧数据不再被调用的时候,通知给客户传递数据的接口使用新数据,清空之前的buffer,并将该buffer设置为未使用,等待下次更新时使用。

#ifndef _RELOADER_H
#define _RELOADER_H
#include <stdio.h>
#include <time.h>
#include <stdint.h>
#include <sys/stat.h>
#include <string.h>
#include <new>

template <bool> class static_assert;
template <> class static_assert<true> {};

// @brief   双 buffer 管理类
// @note    T 一定要实现 int load(const char* cp_dir, const char* cp_fname) 函数, 否则编译失败
// @note    如果有 init 需求, 可以在构造时, 提供一个 int T::init() 函数
// @note    以上两个函数在返回 < 0 时,表示失败
// @note    推荐设计:文件不存在或者内容为空,也认为是成功
template <class T>
class reloader_t
{
public:
    typedef T content_type;

    // @param   init_func,如果传入的话,reload会对new出来的对象调用 init_func
	// @note 	init_func必须是静态函数
    reloader_t(const char* cp_path,
               const char* cp_fname,
               int (content_type::*init_func)() = NULL);
    ~reloader_t();

    // @return  -1, 文件不存在; 0, 不需要reload; 1, 可以reload
    int need_reload() const;

    // @brief   双 buffer 切换
    // @return  -1, reload失败; 1, reload成功
    int reload();

    // 取得当前可以使用的 buffer 的指针
    content_type* get_content();

    // 取得当前可以使用的 buffer 的指针
    const content_type* get_content() const;

    time_t get_file_modtime() const
    {
        return _tm;
    }

    time_t get_file_loadtime() const
    {
        return _last_update_tm;
    }

protected:
    time_t _tm;
    time_t _last_update_tm; 
    uint8_t _using_no;

    // 两个buffer的指针
    content_type* _p_content[2];

    // 实际的buffer,避免reload时频繁的申请内存
    char _mem[2 * sizeof(content_type)];

    char _path[512];
    char _fname[512];

    // 个性 init 函数
    int (content_type::*_init_func)();

    //以下函数保证 T::load 的返回值必须为int
    typedef char small_t;
    struct big_t {
        char c[2];
    };

    static small_t _is_int(int);
    template <class V>
    static big_t _is_int(V);

    template <class RT, class CLA>
    static RT _get_load_ret_type(RT (CLA::*)(const char*, const char*));    
};

template <class T>
reloader_t<T>::reloader_t(const char* cp_path,
                          const char* cp_fname,
                          int (T::*init_func)()):
    _tm(0),
    _last_update_tm(0),
    _using_no(1),
    _init_func(init_func)
{
    // 静态检查 load 的返回值必须是 int
    static_assert<sizeof(small_t) == sizeof(_is_int(_get_load_ret_type(&T::load)))>();

    if (cp_path == NULL) {
        cp_path = "./";
    }
    if (cp_fname == NULL) {
        cp_fname = "reload_file";
    }
    snprintf(_path, sizeof(_path), "%s", cp_path);
    snprintf(_fname, sizeof(_fname), "%s", cp_fname);

    _p_content[0] = NULL;
    _p_content[1] = NULL;

    memset(&_mem, 0, sizeof(_mem));
}

template <class T>
reloader_t<T>::~reloader_t()
{
    if (_p_content[0] != NULL)
    {
        _p_content[0]->~T();
        _p_content[0] = NULL;
    }

    if (_p_content[1] != NULL)
    {
        _p_content[1]->~T();
        _p_content[1] = NULL;
    }
}

template <class T>
int reloader_t<T>::need_reload() const
{
    char fpath[sizeof(_path) + sizeof(_fname) + 1] = "";
    snprintf(fpath, sizeof(fpath), "%s/%s", _path, _fname);

    struct stat curr_stat;

    // 记录当前状态文件状态
    if ((stat(fpath, &curr_stat) != 0) 
        || ((curr_stat.st_mode & S_IFREG) == 0))
    {
        return -1;
    }

    if (curr_stat.st_mtime <= _tm)
    {
        // 当前数据为新
        return 0;
    }

    return 1;
}

template <class T>
int reloader_t<T>::reload()
{
    if (_using_no != 0 && _using_no != 1) {
        return -1;
    }

    // sleep 1 秒,保证所有线程不再使用该资源
    // 外围保证
    sleep(1);

    struct stat curr_stat;
    char fpath[sizeof(_path) + sizeof(_fname) + 1] = "";            
    snprintf(fpath, sizeof(fpath), "%s/%s", _path, _fname);

    // 记录当前状态文件状态
    if ((stat(fpath, &curr_stat) != 0)
        || ((curr_stat.st_mode & S_IFREG) == 0)) {
        WARN("get file:%s status failed", fpath);
        return -1;
    }

	//保证使用的buffer编号为1或者0
    int reload_no = 1 - _using_no;
    if (_p_content[reload_no] != NULL) {
        _p_content[reload_no]->~T();
        _p_content[reload_no] = NULL;
    }
    _p_content[reload_no] = new (&_mem[reload_no * sizeof(T)]) T();

    // init
    if (_init_func != NULL) {
        int init_ret = (_p_content[reload_no]->*_init_func)();
        if (init_ret < 0) {
            FATAL("init failed.init ret:%d", init_ret);
            goto RELOAD_FAILED;
        }
    }
                
    {
        int load_ret = _p_content[reload_no]->load(_path, _fname);
        if (load_ret < 0) {
            FATAL("load file:%s/%s failed.load ret:%d",
                         _path, _fname, load_ret);
            goto RELOAD_FAILED;
        }
    }

    _tm = curr_stat.st_mtime;
    _last_update_tm = time(NULL);
    _using_no = reload_no;
    INFO("reload [%s] success and shift to data [%d]", fpath, _using_no);

    return 1;
RELOAD_FAILED:
    if (_p_content[reload_no] != NULL) {
        _p_content[reload_no]->~T();
        _p_content[reload_no] = NULL;
    }
    return -1;
}

template <class T>
T* reloader_t<T>::get_content()
{
    if (_tm == 0 || (_using_no != 1 && _using_no != 0)) {
        return NULL;
    }
    return _p_content[_using_no];
}

template <class T>
const T* reloader_t<T>::get_content() const
{
    return get_content();
}

#endif
该类中也实现了文件状态的判断,但是个人觉得,这种功能还是应该独立,采用组合的方式进行处理,让reloader拥有一个watcher对象是一个不错的选择。


最后,再加上线程,搞定:

#ifndef _HIT_RELOAD_H
#define _HIT_RELOAD_H

#include <pthread.h>
#include "watcher.h"

static const char WORD_RELOAD_FILE[] = "./conf/reloadWord";

class Thread
{/*{{{*/
	Thread(const Thread &);
	void operator=(const Thread &);
	pthread_t _thread;

public:
	Thread() : _thread(0), _status(true), _start(false) {}
	virtual ~ Thread() {}

    //启动线程函数
    int start()
    {
        int ret = 0;
        if (_start == false) {
            ret = pthread_create(&_thread, NULL, Thread::func, this);
            if (ret != 0) {
                FATAL("create thread failed.");
                _status = false;
            } else {
                _start = true;
            }
        }
        return ret;
    }
    
    //取得线程的状态
    bool valid() const{return _status;}
    
    //取得线程句柄
    pthread_t & get(){return _thread;}

    const pthread_t & get() const{return _thread;}

    //等待线程结束函数
    int join(){
        if (_status == true && _start == true) {
            return pthread_join(_thread, NULL);
        }
        return -1;
    }

	virtual void *run() = 0;

protected:
    bool _status;
    bool _start;

    static void *func(void *args){
        Thread *p_thread = static_cast <Thread *>(args);
        return p_thread->run();
    }
};/*}}}*/

class ReloadThread:public Thread
{/*{{{*/
public:

	template<class T>
	void reloadCheck(Watcher *watcher, reloader_t<T> *mgr, const char* file)
	{/*{{{*/
			bool all_reloaded = false;
			int ret = watcher->check_and_update_timestamp();
			if (ret > 0) {
				all_reloaded = true;
				if (mgr != NULL){
					if (mgr->reload() < 0) {
						printf("reload %s config failed", file);
						all_reloaded = false;
					}
				}
			} else if (ret < 0) {
                printf("watcher status not valid");
                // 检测状态失败, reload未成功
                all_reloaded = false;
            } else {
                // 未检测到更新
                all_reloaded = false;
            }
			if (all_reloaded) {
                printf("%s is reloaded!", file);
            }
	}/*}}}*/
	void *run(){

		Watcher watcher_word;
		watcher_word.create("word_filter_file");
		
		while (true)
		{
			sleep(g_conf.iSleepTime);
			
			//reload word.conf
			reloadCheck<WordFilter>(&watcher_word, 
					g_data.word_filter_mgr, 
					g_conf.word_filter_file);
		}
		
		return NULL;
	}
};/*}}}*/

#endif
/* vim: set ts=4 sw=4 noet: */




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值