• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            桃源谷

            心靈的旅行

            人生就是一場(chǎng)旅行,不在乎旅行的目的地,在乎的是沿途的風(fēng)景和看風(fēng)景的心情 !
            posts - 32, comments - 42, trackbacks - 0, articles - 0
              C++博客 :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理
            From 2008精選

            UNIXC++程序設(shè)計(jì)守則 (2)
            原文地址:http://d.hatena.ne.jp/yupo5656/20040712/p2

            準(zhǔn)則2: 要知道信號(hào)處理函數(shù)中可以做那些處理
            · 在用sigaction函數(shù)登記的信號(hào)處理函數(shù)中可以做的處理是被嚴(yán)格限定的
            · 僅僅允許做下面的三種處理
               1. 局部變量的相關(guān)處理
               2. “volatile sig_atomic_t”類型的全局變量的相關(guān)操作
               3. 調(diào)用異步信號(hào)安全的相關(guān)函數(shù)
            · 以外的其他處理不要做

             
            說明
            因?yàn)樵谑盏叫盘?hào)時(shí)要做一些處理,那通常是準(zhǔn)備一個(gè)信號(hào)處理函數(shù)并用sigaction函數(shù)把它和信號(hào)名進(jìn)行關(guān)聯(lián)的話就OK了。但是,在這個(gè)信號(hào)處理函數(shù)里可以做的處理是像上面那樣被嚴(yán)格限定的。沒有很好掌握這些知識(shí)就隨便寫一些代碼的話就會(huì)引起下面那樣的問題
            · 問題1: 有程序死鎖的危險(xiǎn)
               o  這是那些依賴于某一時(shí)刻,而且錯(cuò)誤再現(xiàn)比較困難的BUG產(chǎn)生的真正原因
               o  死鎖是一個(gè)比較典型的例子,除此之外還能引起函數(shù)返回值不正確,以及在某一函數(shù)內(nèi)執(zhí)行時(shí)突然收到SEGV信號(hào)等的誤操作。
               譯者注1SEGV通常發(fā)生在進(jìn)試圖訪問無效內(nèi)存區(qū)域時(shí)(可能是個(gè)NULL,或超出進(jìn)程空之外的內(nèi)存地址)。當(dāng)bug原因和SEGV影響在不同時(shí)間現(xiàn)時(shí),它別難于捕到。


            · 問題2: 由于編譯器無意識(shí)的優(yōu)化操作,有導(dǎo)致程序紊亂的危險(xiǎn)
               o  這是跟編譯器以及編譯器優(yōu)化級(jí)別有關(guān)系的bug。它也是“編譯器做了優(yōu)化處理而不能正常動(dòng)作”,“因?yàn)?/span>inline化了程序不能動(dòng)作了”,“變換了OS了程序也不能動(dòng)作”等這些解析困難bug產(chǎn)生的原因。

             
            還是一邊看具體的代碼一邊解說吧。在下面的代碼里至少有三個(gè)問題,根據(jù)環(huán)境的不同很可能引起不正確的動(dòng)作*1按照次序來說明里面的錯(cuò)誤

             

             1int gSignaled;
             2void sig_handler(int signo) {
             3    std::printf("signal %d received!\n", signo);
             4    gSignaled = 1;
             5}

             6int main(void{
             7    struct sigaction sa;
             8  // (省略)
             9  sigaction(SIGINT, &sa, 0);
            10    gSignaled = 0;
            11    while(!gSignaled) {
            12  //std::printf("waiting\n");
            13        struct timespec t = 10 }; nanosleep(&t, 0);
            14    }

            15}

            16

             

            錯(cuò)誤1: 競(jìng)爭(zhēng)條件
                在上面的代碼里有競(jìng)爭(zhēng)條件sigaction函數(shù)被調(diào)用后gSignaled還未被賦值成0值之前,如果接受到SIGINT信號(hào)了那會(huì)變得怎么樣呢? 在信號(hào)處理函數(shù)中被覆寫成1后的gSignaled會(huì)在信號(hào)處理函數(shù)返回后被初始化成0在后面的while循環(huán)里可能會(huì)變成死循環(huán)


            錯(cuò)誤2: 全局變量gSignaled 聲明的類型不正確
                 在信號(hào)處理函數(shù)里使用的全局變數(shù)gSignaled的類型沒有聲明成volatile sig_atomic_t 這樣的話在執(zhí)行while循環(huán)里的代碼的時(shí)候接收到了了SIGINT信號(hào)時(shí)有可能引起while的死循環(huán)那為什么能引起這樣的情況呢
                · 信號(hào)處理函數(shù)里,把內(nèi)存上gSignaled的值變更成1 ,它的匯編代碼如下:

             

                      movl    $1, gSignaled

                · 但是,就像下面的代碼描述的那樣,main函數(shù)是把gSignaled的值存放到了寄存器里while循環(huán)之前,僅僅是做了一次拷貝變量gSignaled內(nèi)存上的值到寄存器里而在while循環(huán)里只是參照這個(gè)寄存器里的值
                      movl     gSignaled, %ebx
                   .L8:
                                   testl    %ebx, %ebx
                                   jne      .L8

                 在不執(zhí)行優(yōu)化的情況下編譯后編譯器有可能不會(huì)生成上面那樣的偽代碼Gcc當(dāng)使用-O2選項(xiàng)做優(yōu)化編譯時(shí),生成的實(shí)際那樣的匯編代碼產(chǎn)生的危害并不僅僅是像上面說的威脅那樣簡(jiǎn)單。這方面的問題,是設(shè)備驅(qū)動(dòng)的開發(fā)者所要知道的常識(shí),但現(xiàn)實(shí)情況是對(duì)于應(yīng)用程序的設(shè)計(jì)者.開發(fā)者幾乎都不知道這些知識(shí)。
            為了解決上面的問題,全局變量gSignaled的類型要像下面那樣聲明

                 volatile sig_atomic_t gSignaled;

                 volatile則是提示編譯器不要像上面那樣做優(yōu)化處理,變成每次循環(huán)都要參照該變量?jī)?nèi)存里的值那樣進(jìn)行編譯。所以在信號(hào)處理函數(shù)里把該變量的值修改后也能真實(shí)反映到main函數(shù)的while循環(huán)里
            sig_atomic_t 是根據(jù)CPU類型使用typedef來適當(dāng)定義的整數(shù)值,例如x86平臺(tái)是int就是指用一條機(jī)器指令來更新內(nèi)存里的最大數(shù)據(jù)*2在信號(hào)處理函數(shù)里要被引用的變量必須要定義成sig_atomic_t類型那么不是sig_atomic_t類型的變量(比如x86平臺(tái)上的64位整數(shù))就得使用兩條機(jī)器指令來完成更新動(dòng)作。如果在執(zhí)行一條機(jī)器指令的時(shí)候突然收到一個(gè)信號(hào)而程序執(zhí)行被中斷,而且在信號(hào)處理函數(shù)中一引用這個(gè)變量的話,就只能看到這個(gè)變量的部分的值。另外,由于字節(jié)對(duì)齊的問題不能由一條機(jī)器指令來完成的情況也會(huì)存在。把該變量的類型變成sig_atomic_t的話,這個(gè)變量被更新時(shí)就只需要一條機(jī)器指令就可以完成了。所以在信號(hào)處理函數(shù)里即使使用了該變量也不會(huì)出現(xiàn)任何問題

                 2006/1/16 補(bǔ)充: 有一點(diǎn)東西忘記寫了關(guān)于sig_atomic_t詳細(xì)的東西,請(qǐng)參考C99規(guī)范的§7.14.1.1/5小節(jié)在信號(hào)處理函數(shù)里對(duì)volatile sig_atomic_t以外的變量進(jìn)行修改,其結(jié)果都是"unspecified"(參照譯者注2)另外, sig_atomic_t類型的變量的取值范圍是在SIG_ATOMIC_MIN/MAX之間 (參見§7.18.3/2)有無符號(hào)是跟具體的實(shí)現(xiàn)有關(guān)。考慮到移植性取值在0127之間是比較合適的C99也支持這個(gè)取值范圍C++規(guī)范(14882:2003)里也有同樣的描述確切的位置是§1.9/9這里SUSv3的相關(guān)描述請(qǐng)參考sigaction這里*3此外雖然在GCC的參考手冊(cè)里也說了把指針類型更新成原子操作,但在標(biāo)準(zhǔn)C/C++卻沒有記載*4
            譯者注2
                       When the processing of the abstract machine is interrupted by receipt of a signal, the value of objects with type other than volatile sig_atomic_t are unspecified, and the value of any object not of volatile sig_atomic_t that is modified by the handler becomes undefined.
                                   ------
            ISO/IEC FDIS 14882:1998(E) 1.9小節(jié)


            錯(cuò)誤3: 在信號(hào)處理函數(shù)里調(diào)用了不可重入的函數(shù)
            上述的樣例代碼中調(diào)用了printf函數(shù),但是這個(gè)函數(shù)是一個(gè)不可重入函數(shù),所以在信號(hào)處理函數(shù)里調(diào)用的話可能會(huì)引起問題。具體的是,在信號(hào)處理函數(shù)里調(diào)用printf函數(shù)的瞬間,引起程序死鎖的可能性還是有的。但是,這個(gè)問題跟具體的時(shí)機(jī)有關(guān)系,所以再現(xiàn)起來很困難,也就成了一個(gè)很難解決的bug了。
            下面講一下bug發(fā)生的過程。首先講解一下printf函數(shù)的內(nèi)部實(shí)現(xiàn)。
                · printf函數(shù)內(nèi)部調(diào)用malloc函數(shù)
                · malloc函數(shù)會(huì)在內(nèi)部維護(hù)一個(gè)靜態(tài)區(qū)域來保存mutex是為了在多線程調(diào)用malloc函數(shù)的時(shí)候起到互斥的作用
                · 總之malloc函數(shù)里有“mutex鎖定,分配內(nèi)存,mutex解鎖”這樣“連續(xù)的不能被中斷”的處理

             

            main関數(shù):
              call printf  // while循環(huán)中的printf函數(shù)
                call malloc
                  call pthread_mutex_lock(鎖定malloc函數(shù)內(nèi)的靜態(tài)
            mutex)
                  // malloc處理時(shí)
            ..
            收到SIGINT信號(hào)

                    call sig_handler
                      call printf // 信號(hào)處理函數(shù)中的printf函數(shù)

                        call malloc
                          call pthread_mutex_lock(鎖定malloc函數(shù)內(nèi)的靜態(tài)
            mutex)
                          // 相同的mutex一被再度鎖定,就死鎖啦!!

             
                 知道上面的流程的話像這樣的由于信號(hào)中斷引起的死鎖就能被理解了吧。為了修正這個(gè)bug,在信號(hào)處理函數(shù)里就必須調(diào)用可重入函數(shù)。可重入函數(shù)的一覽表在UNIX規(guī)范 (SUSv3)有詳細(xì)記載*5你一定會(huì)驚訝于這個(gè)表里的函數(shù)少吧。
            另外,一定不要忘記以下的幾點(diǎn):
                · 雖然在SUSv3里有異步信號(hào)安全(async-signal-safe)函數(shù)的一覽,但根據(jù)不同的操作系統(tǒng),某些函數(shù)是沒有被實(shí)現(xiàn)的。所以一定要參考操作系統(tǒng)的手冊(cè)
                · 第三者做成的函數(shù),如果沒有特別說明的場(chǎng)合,首先要假定這個(gè)函數(shù)是不可重入函數(shù),不能隨便在信  號(hào)處理函數(shù)中使用。
                · 調(diào)用不可重入函數(shù)的那些函數(shù)就會(huì)變成不可重入函數(shù)了


                最后,為了明確起見,想說明一下什么是異步信號(hào)安全(async-signal-safe)”函數(shù)異步信號(hào)安全函數(shù)是指在該函數(shù)內(nèi)部即使因?yàn)樾盘?hào)而正在被中斷,在其他的地方該函數(shù)再被調(diào)用了也沒有任何問題。如果函數(shù)中存在更新靜態(tài)區(qū)域里的數(shù)據(jù)的情況(例如,malloc),一般情況下都是不全的異步信號(hào)函數(shù)。但是,即使使用靜態(tài)數(shù)據(jù),如果在這里這個(gè)數(shù)據(jù)時(shí)候把信號(hào)屏蔽了的話,它就會(huì)變成異步信號(hào)安全函數(shù)了。
            譯者注3:不可重入函數(shù)就不是異步信號(hào)安全函數(shù)

             


            *1sigaction函數(shù)被調(diào)用前,一接收到SIGINT信號(hào)就終止程序,暫且除外吧
            *2“最大”是不完全正確的例如,Alpha平臺(tái)上32/64bit的變量用一條命令也能被更新,但是好像把8/16bit的數(shù)據(jù)更新編程了多條命令了http://lists.sourceforge.jp/mailman/archives/anthy-dev/2005-September/002336.html 請(qǐng)參考這個(gè)URL地址
            *3If the signal occurs other than as the result of calling abort(), kill(), or raise(), the behavior is undefined if the signal handler calls any function in the standard library other than one of the functions listed in the table above or refers to any object with static storage duration other than by assigning a value to a static storage duration variable of type volatile sig_atomic_t. Furthermore, if such a call fails, the value of errno is unspecified.
            *4在這個(gè)手冊(cè)里“ In practice, you can assume that int and other integer types no longer than int are atomic. ”這部分是不正確的請(qǐng)參照Alpha的例子
            *5The following table defines a set of functions that shall be either reentrant or non-interruptible by signals and shall be async-signal-safe. 后面有異步信號(hào)安全函數(shù)一覽

            我的個(gè)人簡(jiǎn)歷第一頁 我的個(gè)人簡(jiǎn)歷第二頁
            国产精品美女久久福利网站| 久久无码人妻精品一区二区三区| 精品国产乱码久久久久久人妻| AV无码久久久久不卡蜜桃| 久久精品国产精品亚洲精品| 国产成人精品久久二区二区| 国产精品免费久久久久电影网| 亚洲国产香蕉人人爽成AV片久久| 久久久www免费人成精品| 青青草原综合久久大伊人精品| 久久久久亚洲爆乳少妇无| 国产成人精品久久| 一级做a爰片久久毛片人呢| 国内精品久久久久影院薰衣草| A级毛片无码久久精品免费| 久久精品国产亚洲AV影院| 一本久久a久久精品综合夜夜| 久久人人爽人人爽人人片AV高清| 亚洲国产精品婷婷久久| 狼狼综合久久久久综合网| 日韩欧美亚洲综合久久影院Ds | 91亚洲国产成人久久精品| 久久夜色精品国产亚洲av| 久久综合狠狠综合久久| 亚洲国产成人精品女人久久久| 国产午夜精品久久久久免费视| 亚洲国产精品无码久久久久久曰| 91久久精品电影| 大伊人青草狠狠久久| 久久精品99久久香蕉国产色戒| 欧美日韩精品久久免费| 久久久久久久久久久免费精品| 97久久久精品综合88久久| 99久久人妻无码精品系列| 浪潮AV色综合久久天堂| 久久无码AV中文出轨人妻| 亚洲国产成人久久综合碰| 亚洲乱码日产精品a级毛片久久 | 久久天天躁狠狠躁夜夜不卡| 久久综合九色欧美综合狠狠 | 久久人人爽人人爽人人片AV不|