多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國(guó)內(nèi)最全I(xiàn)T社區(qū)平臺(tái) 聯(lián)系我們 | 收藏本站
阿里云優(yōu)惠2
您當(dāng)前位置:首頁(yè) > php框架 > 框架設(shè)計(jì) > pjlib線程實(shí)現(xiàn)簡(jiǎn)析

pjlib線程實(shí)現(xiàn)簡(jiǎn)析

來(lái)源:程序員人生   發(fā)布時(shí)間:2015-08-28 08:53:13 閱讀次數(shù):5716次
本篇主要講授pjlib關(guān)于線程的實(shí)現(xiàn)方式

轉(zhuǎn)載請(qǐng)注明出處:http://blog.csdn.net/lhl_blog/article/details/44063229

系統(tǒng)環(huán)境:
1. Ubuntu14.04 TLS 內(nèi)核3.13.0⑷5-generi
2. gcc 4.8.2

3. glibc 2.19


開(kāi)始之前需要講授兩個(gè)概念:

1.線程棧

    參考nono的csdn博文http://blog.csdn.net/dog250/article/details/7704898,其對(duì)linux的進(jìn)程和線程棧進(jìn)行了較為詳細(xì)的介紹.個(gè)人認(rèn)為比較重要的就是
    linux glibc所實(shí)現(xiàn)的線程棧為不能動(dòng)態(tài)增長(zhǎng)的,這就很值得注意了,如果線程中定義使用比較大的數(shù)據(jù)結(jié)構(gòu)就會(huì)致使線程棧溢出,而線程棧是在進(jìn)程的堆中分
    配的內(nèi)存,從而城門失火,殃及池魚,這樣1旦線程棧溢出就會(huì)間接破壞進(jìn)程地址空間,從而致使很難調(diào)試的bug(可能致使內(nèi)存段毛病).因此,有效的管理線程
    棧在多線程編程中就成了比較重要的問(wèn)題.或許,有的人會(huì)說(shuō),我在進(jìn)行多線程編程時(shí),對(duì)線程棧也沒(méi)有進(jìn)行過(guò)量干預(yù)啊,也沒(méi)有出現(xiàn)過(guò)甚么問(wèn)題啊? 這類認(rèn)識(shí)
    實(shí)際上是存在問(wèn)題的, 正所謂欠下的債早晚是要還的,是時(shí)候未到.

    至于表面上沒(méi)有出現(xiàn)問(wèn)題,主要緣由以下:

    1.*NIX系統(tǒng)在創(chuàng)建線程時(shí),如果沒(méi)有指定線程棧的大小,系統(tǒng)會(huì)創(chuàng)建1個(gè)默許大小的棧(8M),我們1般不會(huì)使用如此'大'的自動(dòng)變量,但是如果線程定義了大量
    的自動(dòng)變量,例如,定義了char array[8*1024*1024]的數(shù)組,不出意外程序會(huì)出現(xiàn)段毛病,示例代碼以下:


#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
void* threadfunc(void* args)
{
    //char c = '1';
    //printf("c = %c ", c);
    char *str1 = (char *)malloc(8*1024*1024);
    char *str2 = (char *)malloc(8*1024*1024);
    char array[8*1024*1024 - 1024*10] = {0};
}

int main(void)
{
    pthread_t pid;
    size_t stack_size;
    pthread_attr_t attr;

    int rc = pthread_create(&pid, NULL, threadfunc, NULL);
    if(rc != 0) {
        printf("create thread failed ");
    }

    pthread_attr_init(&attr);
    pthread_attr_getstacksize(&attr, &stack_size); 
    stack_size /= 1024 * 1024;
    printf("default thread stacksize = %dMB ", stack_size);
    pthread_join(pid, NULL);
    return 0;
}

2.線程TLS

  線程TLS(thread locak store),線程私有數(shù)據(jù)是存儲(chǔ)和查詢與某個(gè)線程相干的數(shù)據(jù)的1種機(jī)制.把這類數(shù)據(jù)稱為線程私有數(shù)據(jù)或線程特定數(shù)據(jù)的緣由為, 希望每
  個(gè)線程可以獨(dú)立的訪問(wèn)數(shù)據(jù)副本,而不用擔(dān)心與其他線程的同步訪問(wèn)問(wèn)題. 典型的利用為每一個(gè)線程具有自己的errno值, 各個(gè)線程之間的errno值互不影響.這就像
  身份證定義空間好比系統(tǒng)的進(jìn)程空間,每一個(gè)獨(dú)立的人好比1個(gè)單獨(dú)的線程,每一個(gè)線程都有1個(gè)身份證號(hào),這個(gè)身份證號(hào)就是我們的TLS,而且我們之間的身份證號(hào)
  不會(huì)相互影響,固然這個(gè)比喻有些不恰當(dāng),理論上身份證號(hào)是1成不變的.

3.pjlib線程實(shí)現(xiàn)

3.1 安全的線程棧保護(hù)機(jī)制

struct pj_thread_t
{
    char            obj_name[PJ_MAX_OBJ_NAME];
    pthread_t       thread;//linux 線程id
    pj_thread_proc  *proc;//線程處理函數(shù)
    void            *arg;//線程處理函數(shù)參數(shù)
    pj_uint32_t     signature1;//標(biāo)簽1,具體作用未知
    pj_uint32_t     signature2;//標(biāo)簽2
    pj_mutex_t      *suspended_mutex;//摹擬線程掛起動(dòng)作
    //如果啟用了線程棧相干的檢查處理,則定義下面的成員變量:
#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
    pj_uint32_t     stk_size;//線程棧大小j
    pj_uint32_t     stk_max_usage;//已使用的線程棧大小
    char            *stk_start;//線程棧的起始地址
    const char      *caller_file;//用于記錄調(diào)用線程棧檢測(cè)函數(shù)確當(dāng)前位置
    int             caller_line;
#endif
};

pjlib線程描寫符明肯定義了線程棧相干的元素, 通過(guò)這些元素, pjlib線程可以基本實(shí)現(xiàn)安全的線程棧使用.下面介紹pjlib線程棧的使用機(jī)制的實(shí)現(xiàn)方式.

3.1.1 線程棧初始化    

     pjlib的pj_thread_create實(shí)現(xiàn)pjlib線程的創(chuàng)建,其中函數(shù)的stack_size為線程棧大小參數(shù),下面的代碼只保存pj_thread_create函數(shù)的參數(shù)和線程棧相干的內(nèi)容:
/*
 * pj_thread_create(...)
 */


PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool,
        const char *thread_name,
        pj_thread_proc *proc,
        void *arg,
        pj_size_t stack_size,
        unsigned flags,
        pj_thread_t **ptr_thread)
{
    ...
#if PJ_HAS_THREADS
    PJ_CHECK_STACK();

    /* Set default stack size */
    if (stack_size == 0)
        stack_size = PJ_THREAD_DEFAULT_STACK_SIZE;
#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
    rec->stk_size = stack_size;
    rec->stk_max_usage = 0;
#endif

//第1種方式:
#if defined(PJ_THREAD_SET_STACK_SIZE) && PJ_THREAD_SET_STACK_SIZE!=0
    /* Set thread's stack size */
    rc = pthread_attr_setstacksize(&thread_attr, stack_size);
    if (rc != 0)
        return PJ_RETURN_OS_ERROR(rc);
#endif /* PJ_THREAD_SET_STACK_SIZE */

//第2種方式:
#if defined(PJ_THREAD_ALLOCATE_STACK) && PJ_THREAD_ALLOCATE_STACK!=0
    /* Allocate memory for the stack */
    stack_addr = pj_pool_alloc(pool, stack_size);
    PJ_ASSERT_RETURN(stack_addr, PJ_ENOMEM);

    rc = pthread_attr_setstackaddr(&thread_attr, stack_addr);
    if (rc != 0)
        return PJ_RETURN_OS_ERROR(rc);
#endif /* PJ_THREAD_ALLOCATE_STACK */
    ...
    /* Create the thread. */
    rec->proc = proc;
    rec->arg = arg;
    rc = pthread_create( &rec->thread, &thread_attr, &thread_main, rec);
    if (rc != 0) {
        return PJ_RETURN_OS_ERROR(rc);
    }
    ...
}

static void *thread_main(void *param)
{
    pj_thread_t *rec = (pj_thread_t*)param;
    void *result;
    pj_status_t rc;

#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
    rec->stk_start = (char*)&rec;
#endif
....

}
     pjlib支持兩種線程棧創(chuàng)建方式,1.使用系統(tǒng)默許線程棧大小直接創(chuàng)建. 2.動(dòng)態(tài)申請(qǐng)堆內(nèi)存,然后將堆內(nèi)存地址作為線程棧的首地址.創(chuàng)建完線程棧以后,pjlib在
     thread_main中對(duì)線程棧的起始地址進(jìn)行了初始化.

3.1.2 線程棧保護(hù)機(jī)制 

    pjlib線程通過(guò)在每一個(gè)線程函數(shù)中調(diào)用PJ_CHECK_STACK()宏實(shí)現(xiàn)線程棧的保護(hù)機(jī)制,線程函數(shù)履行履行之前首先通過(guò)PJ_CHECK_STACK()監(jiān)測(cè)當(dāng)前線程棧狀態(tài),如果
    線程棧空間的使用率到達(dá)規(guī)定的警界值就會(huì)立即觸發(fā)assert斷言提示線程STACK OVERFLOW!,并退出.反之,PJ_CHECK_STACK()會(huì)重新更新當(dāng)前線程棧的使用率.下
    面為PJ_CHECK_STACK()(pj_thread_check_stack)的實(shí)現(xiàn):
/*
 * pj_thread_check_stack()
 * Implementation for PJ_CHECK_STACK()
 */
PJ_DEF(void) pj_thread_check_stack(const char *file, int line)
{
    char stk_ptr;
    pj_uint32_t usage;
    pj_thread_t *thread = pj_thread_this();

    /*計(jì)算當(dāng)前線程棧的使用率*/
    usage = (&stk_ptr > thread->stk_start) ? &stk_ptr - thread->stk_start :
        thread->stk_start - &stk_ptr;

    /*如果線程棧的使用率超過(guò)警界值,立即觸發(fā)斷言*/
    pj_assert("STACK OVERFLOW!! " && (usage <= thread->stk_size - 128));

    /*重新統(tǒng)計(jì)線程棧使用率*/
    if (usage > thread->stk_max_usage) {
        thread->stk_max_usage = usage;
        thread->caller_file = file;
        thread->caller_line = line;
    }
}
    
    PJ_CHECK_STACK()1般在線程函數(shù)定義完局部變量時(shí)調(diào)用,這樣就能夠?qū)tack overflow提早預(yù)警,從而減少大量的調(diào)試時(shí)間.

3.2 pjlib TLS

    pjlib線程基于TLS機(jī)制實(shí)現(xiàn)本地線程描寫符的存儲(chǔ),所有pjlib線程或外部線程(linux原生線程)都必須使用pj_thread_register將自己的描寫符存儲(chǔ)到TLS中,
    當(dāng)調(diào)用pjlib庫(kù)函數(shù)時(shí),庫(kù)函數(shù)1般都會(huì)檢測(cè)本地線程是不是注冊(cè)過(guò),即是不是調(diào)用過(guò)pthread_setspecific()注冊(cè)線程TLS,否則pjlib庫(kù)函數(shù)不能被正常使用,只有
    這樣才能光明正大的使用pjlib函數(shù).    

    pjlib這樣做的理由可能為:
    1.每一個(gè)pjlib線程都有1個(gè)線程描寫符pj_thread_t,每一個(gè)描寫符中保存的內(nèi)容各不相同,我們可以通過(guò)線程TLS將不同線程的描寫符實(shí)現(xiàn)統(tǒng)1管理,就像errno.
    2.方便線程調(diào)試,通過(guò)TLS可以方便的獲得本地線程相干的所有信息,我們通過(guò)打印或在線調(diào)試都可以方便的跟蹤當(dāng)前調(diào)用函數(shù)的線程狀態(tài).    
    3. ... ... 

3.3 pjlib線程調(diào)度策略

    pjlib線程封裝了linux系統(tǒng)提供的幾種線程調(diào)度策略,SCHED_FIFO,SCHED_RR,SCHED_OTHER,沒(méi)有特別的設(shè)置.
生活不易,碼農(nóng)辛苦
如果您覺(jué)得本網(wǎng)站對(duì)您的學(xué)習(xí)有所幫助,可以手機(jī)掃描二維碼進(jìn)行捐贈(zèng)
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關(guān)閉
程序員人生
主站蜘蛛池模板: 老司机免费福利视频 | 久久精品三级视频 | 日韩国产精品99久久久久久 | 456在线视频 | 性久久久久 | 中文字幕无线精品乱码一区 | 伊人久久中文字幕久久cm | 亚洲精品乱码久久久久久蜜桃欧美 | 成人天堂在线 | 3344成年站福利在线视频免费 | 波多久久夜色精品国产 | 岛国视频在线播放 | 忘忧草wyc.apk | 黄大色黄美女精品大毛片 | 国产欧美日韩综合精品二区 | 午夜欧美精品久久久久久久 | 国产欧美中文字幕 | 日韩在线影视 | 精品无码久久久久国产 | 国产一级淫片免费视频 | 国产在线乱码在线视频 | 亚洲经典在线中文字幕 | 亚洲春色www | free性欧美另类高清 | 亚洲精品国产一区二区图片欧美 | 日韩国产免费一区二区三区 | 成人毛片18女人毛片免费视频未 | 女人一级毛片免费观看 | 性生活免费视频网站 | 久久一精品 | 久久久久久国产精品视频 | 日韩精品一区二区三区高清 | 午夜精品久久久久久中宇 | 亚洲欧美色综合自拍 | 欧洲福利视频 | 精品亚洲欧美中文字幕在线看 | 亚洲视频一二区 | 五月天欧美激情午夜情 | 91精品成人福利在线播放 | 亚洲区精品久久一区二区三区 | 美女教师一级毛片 |