Redis Bio

簡(jiǎn)介

Redis Bio 就是Background I/O service for Redis.
他把那些耗時(shí)的io操作放到后臺(tái)的線程來(lái)執(zhí)行何之。主線程就專注服務(wù)燃少。避免那些耗時(shí)的釋放休涤,寫入操作等造成服務(wù)的等待句葵。
實(shí)現(xiàn)也特別的簡(jiǎn)單。采用新建處理線程。使用臨界區(qū)投放任務(wù)的方式完成操作锋勺。

核心的一些定義和方法

static pthread_t bio_threads[BIO_NUM_OPS]; //保存pthread
static pthread_mutex_t bio_mutex[BIO_NUM_OPS];//保存mutex
static pthread_cond_t bio_newjob_cond[BIO_NUM_OPS];//保存新任務(wù)條件變量
static pthread_cond_t bio_step_cond[BIO_NUM_OPS];//保存step條件變量
static list *bio_jobs[BIO_NUM_OPS];//保存joblist

static unsigned long long bio_pending[BIO_NUM_OPS];//保存pening數(shù)量

struct bio_job {
    time_t time; /* Time at which the job was created. */ //創(chuàng)建時(shí)間
    /* Job specific arguments pointers. If we need to pass more than three
     * arguments we can just pass a pointer to a structure or alike. */
    void *arg1, *arg2, *arg3;//三個(gè)保存的參數(shù)
};

初始化bio

/* Initialize the background system, spawning the thread. */
void bioInit(void) {
    pthread_attr_t attr;
    pthread_t thread;
    size_t stacksize;
    int j;

    /* Initialization of state vars and objects */
    for (j = 0; j < BIO_NUM_OPS; j++) {//基本的初始化操作
        pthread_mutex_init(&bio_mutex[j],NULL);
        pthread_cond_init(&bio_newjob_cond[j],NULL);
        pthread_cond_init(&bio_step_cond[j],NULL);
        bio_jobs[j] = listCreate();//創(chuàng)建任務(wù)隊(duì)列 
        bio_pending[j] = 0;//等待數(shù)量是0
    }

    /* Set the stack size as by default it may be small in some system */
    pthread_attr_init(&attr);
    pthread_attr_getstacksize(&attr,&stacksize);
    if (!stacksize) stacksize = 1; /* The world is full of Solaris Fixes */
    while (stacksize < REDIS_THREAD_STACK_SIZE) stacksize *= 2;
    pthread_attr_setstacksize(&attr, stacksize);//stack size

    /* Ready to spawn our threads. We use the single argument the thread
     * function accepts in order to pass the job ID the thread is
     * responsible of. */
    for (j = 0; j < BIO_NUM_OPS; j++) {
        void *arg = (void*)(unsigned long) j;
        if (pthread_create(&thread,&attr,bioProcessBackgroundJobs,arg) != 0) {//創(chuàng)建線程
            serverLog(LL_WARNING,"Fatal: Can't initialize Background Jobs.");
            exit(1);
        }
        bio_threads[j] = thread;
    }
}

整個(gè)創(chuàng)建過(guò)程是一個(gè)標(biāo)準(zhǔn)的初始化過(guò)程。沒(méi)有什么特別的地方狡蝶。

線程函數(shù)

void *bioProcessBackgroundJobs(void *arg) {//線程入口
    struct bio_job *job;
    unsigned long type = (unsigned long) arg;
    sigset_t sigset;

    /* Check that the type is within the right interval. */
    if (type >= BIO_NUM_OPS) {//type不對(duì) 
        serverLog(LL_WARNING,
            "Warning: bio thread started with wrong type %lu",type);
        return NULL;
    }

    /* Make the thread killable at any time, so that bioKillThreads()
     * can work reliably. */
    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);//設(shè)置響應(yīng)cancel
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);//設(shè)置立即結(jié)束

    pthread_mutex_lock(&bio_mutex[type]);//鎖住互斥
    /* Block SIGALRM so we are sure that only the main thread will
     * receive the watchdog signal. */
    sigemptyset(&sigset);
    sigaddset(&sigset, SIGALRM);
    if (pthread_sigmask(SIG_BLOCK, &sigset, NULL))//只讓主線程處理 SIGALRM
        serverLog(LL_WARNING,
            "Warning: can't mask SIGALRM in bio.c thread: %s", strerror(errno));

    while(1) {
        listNode *ln;

        /* The loop always starts with the lock hold. */
        if (listLength(bio_jobs[type]) == 0) {
            pthread_cond_wait(&bio_newjob_cond[type],&bio_mutex[type]);//釋放mutex 并且進(jìn)入阻塞等待
            continue;//防止意外喚醒
        }
        /* Pop the job from the queue. */
        ln = listFirst(bio_jobs[type]);// 獲取新的任務(wù)
        job = ln->value;
        /* It is now possible to unlock the background system as we know have
         * a stand alone job structure to process.*/
        pthread_mutex_unlock(&bio_mutex[type]);//釋放鎖可以加入新的任務(wù)

        /* Process the job accordingly to its type. */
        if (type == BIO_CLOSE_FILE) {//判斷自己的類型
            close((long)job->arg1);
        } else if (type == BIO_AOF_FSYNC) {
            aof_fsync((long)job->arg1);
        } else if (type == BIO_LAZY_FREE) {
            /* What we free changes depending on what arguments are set:
             * arg1 -> free the object at pointer.
             * arg2 & arg3 -> free two dictionaries (a Redis DB).
             * only arg3 -> free the skiplist. */
            if (job->arg1)
                lazyfreeFreeObjectFromBioThread(job->arg1);
            else if (job->arg2 && job->arg3)
                lazyfreeFreeDatabaseFromBioThread(job->arg2,job->arg3);
            else if (job->arg3)
                lazyfreeFreeSlotsMapFromBioThread(job->arg3);
        } else {
            serverPanic("Wrong job type in bioProcessBackgroundJobs().");
        }
        zfree(job);

        /* Unblock threads blocked on bioWaitStepOfType() if any. */
        pthread_cond_broadcast(&bio_step_cond[type]);//喚醒所有的 setp_cond

        /* Lock again before reiterating the loop, if there are no longer
         * jobs to process we'll block again in pthread_cond_wait(). */
        pthread_mutex_lock(&bio_mutex[type]);//鎖住
        listDelNode(bio_jobs[type],ln);//刪除完成的任務(wù)
        bio_pending[type]--;//pending --
    }
}

整理流程:
1獲取鎖庶橱,判斷任務(wù)是否存在,不存在則進(jìn)入cond_wait,存在就提取出一個(gè)任務(wù)贪惹,然后根據(jù)任務(wù)類型苏章,進(jìn)行操作。
2.執(zhí)行結(jié)束后喚醒所有的step_cond,再次獲取鎖枫绅。刪除完成的任務(wù)泉孩。進(jìn)入循環(huán)

創(chuàng)建一個(gè)任務(wù)

void bioCreateBackgroundJob(int type, void *arg1, void *arg2, void *arg3) {//創(chuàng)建job
    struct bio_job *job = zmalloc(sizeof(*job));//分配內(nèi)存
    job->time = time(NULL);//開始時(shí)間
    job->arg1 = arg1;//保存參數(shù)
    job->arg2 = arg2;
    job->arg3 = arg3;
    pthread_mutex_lock(&bio_mutex[type]);//獲取鎖
    listAddNodeTail(bio_jobs[type],job);//插入任務(wù)
    bio_pending[type]++;//pending++
    pthread_cond_signal(&bio_newjob_cond[type]);//通知等待
    pthread_mutex_unlock(&bio_mutex[type]);
}

關(guān)閉后臺(tái)io線程

void bioKillThreads(void) {
    int err, j;

    for (j = 0; j < BIO_NUM_OPS; j++) {
        if (pthread_cancel(bio_threads[j]) == 0) {//發(fā)送cancle
            if ((err = pthread_join(bio_threads[j],NULL)) != 0) {//join等待
                serverLog(LL_WARNING,
                    "Bio thread for job type #%d can be joined: %s",
                        j, strerror(err));
            } else {
                serverLog(LL_WARNING,
                    "Bio thread for job type #%d terminated",j);
            }
        }
    }
}

一些狀態(tài)的獲取

unsigned long long bioWaitStepOfType(int type) {
    unsigned long long val;
    pthread_mutex_lock(&bio_mutex[type]);//加鎖
    val = bio_pending[type];
    if (val != 0) {//
        pthread_cond_wait(&bio_step_cond[type],&bio_mutex[type]);//d等待通知
        val = bio_pending[type];
    }
    pthread_mutex_unlock(&bio_mutex[type]);//釋放鎖
    return val;
}
unsigned long long bioPendingJobsOfType(int type) {
    unsigned long long val;
    pthread_mutex_lock(&bio_mutex[type]);
    val = bio_pending[type];
    pthread_mutex_unlock(&bio_mutex[type]);
    return val;
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市并淋,隨后出現(xiàn)的幾起案子寓搬,更是在濱河造成了極大的恐慌,老刑警劉巖县耽,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件句喷,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡酬诀,警方通過(guò)查閱死者的電腦和手機(jī)脏嚷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)瞒御,“玉大人父叙,你說(shuō)我怎么就攤上這事‰热梗” “怎么了趾唱?”我有些...
    開封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)蜻懦。 經(jīng)常有香客問(wèn)我甜癞,道長(zhǎng),這世上最難降的妖魔是什么宛乃? 我笑而不...
    開封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任悠咱,我火速辦了婚禮,結(jié)果婚禮上征炼,老公的妹妹穿的比我還像新娘析既。我一直安慰自己,他們只是感情好谆奥,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開白布眼坏。 她就那樣靜靜地躺著,像睡著了一般酸些。 火紅的嫁衣襯著肌膚如雪宰译。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天魄懂,我揣著相機(jī)與錄音沿侈,去河邊找鬼。 笑死市栗,一個(gè)胖子當(dāng)著我的面吹牛肋坚,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼智厌,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼诲泌!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起铣鹏,我...
    開封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤敷扫,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后诚卸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體葵第,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年合溺,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了卒密。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡棠赛,死狀恐怖哮奇,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情睛约,我是刑警寧澤鼎俘,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站辩涝,受9級(jí)特大地震影響贸伐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜怔揩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一捉邢、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧商膊,春花似錦伏伐、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)砰苍。三九已至潦匈,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間赚导,已是汗流浹背茬缩。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留吼旧,地道東北人凰锡。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親掂为。 傳聞我的和親對(duì)象是個(gè)殘疾皇子裕膀,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 本文將從Redis的基本特性入手昼扛,通過(guò)講述Redis的數(shù)據(jù)結(jié)構(gòu)和主要命令對(duì)Redis的基本能力進(jìn)行直觀介紹。之后概...
    kelgon閱讀 61,123評(píng)論 24 626
  • 介錯(cuò):“介錯(cuò)”,是出自日本歷史上為切腹者來(lái)?yè)?dān)當(dāng)補(bǔ)刀行為之人的稱謂欲诺。這對(duì)于被托付執(zhí)行介錯(cuò)的人來(lái)說(shuō)是一件相當(dāng)光榮的事情...
    FuFu醬蘸kk閱讀 1,056評(píng)論 0 0
  • 我需要 最狂的風(fēng) 和最靜的海
    云從松樹升起閱讀 282評(píng)論 3 1
  • 不要認(rèn)為自己有能力在哪都一樣抄谐,重要的是平臺(tái),平臺(tái)扰法,平臺(tái)蛹含。 不要認(rèn)為這世界是不會(huì)變的。不要等到一艘船從艙頂開始漏水的...
    蘇措_b773閱讀 200評(píng)論 0 1
  • 想啥呢塞颁!第一次用簡(jiǎn)書浦箱! 第一次用簡(jiǎn)書寫東西,很好奇成品會(huì)是什么樣子殴边,大概由于是第一次的緣故憎茂,有新鮮感,所以覺(jué)著很有...
    雕刻時(shí)光123閱讀 192評(píng)論 0 1