• <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>
            posts - 200, comments - 8, trackbacks - 0, articles - 0

            2015年1月13日

                 摘要: 1    背景    12    Volatile:易變的    12.1    小結(jié)    23    Volatil...  閱讀全文

            posted @ 2015-01-13 20:35 鑫龍 閱讀(431) | 評(píng)論 (0)編輯 收藏

            2014年9月29日

                 摘要: 轉(zhuǎn)自:http://my.oschina.net/moooofly/blog/175019最近看 GLib 的代碼遇到這個(gè)東東,網(wǎng)上搜索一圈,發(fā)現(xiàn)很多人都寫過這個(gè),自己今天才研究到,汗顏一下,掃盲一個(gè)點(diǎn),留此記錄為證! 首先看一篇最官方的講解: ====== likely() and unlikely() What are they ?       In Linux ...  閱讀全文

            posted @ 2014-09-29 21:31 鑫龍 閱讀(7713) | 評(píng)論 (0)編輯 收藏

            2014年9月23日

            一、linux下安裝cmake

            首先下載源碼包
                    http://www.cmake.org/cmake/resources/software.html

                    這里下載的是cmake-2.8.9.tar.gz

            隨便找個(gè)目錄解壓縮

            1         tar -xzvf cmake-2.8.9.tar.gz
            2         cd cmake-cmake-2.8.9

                    依次執(zhí)行:

            1         ./bootstrap
            2          make
            3         make install

                    cmake 會(huì)默認(rèn)安裝在 /usr/local/bin 下面

            要改變安裝路徑,在bootstrap命令中加入'--prefix=PATH'選項(xiàng)。


            二、linux下安裝boost
            linux平臺(tái)下要編譯安裝除gcc和gcc-c++之外,還需要兩個(gè)開發(fā)庫(kù):bzip2-devel 和python-devel,因此在安裝前應(yīng)該先保證這兩個(gè)庫(kù)已經(jīng)安裝:

            #yum install gcc gcc-c++ bzip2 bzip2-devel bzip2-libs python-devel -y

            然后是去官網(wǎng)下載源碼包,地址
            下載,解壓,按照如下步驟:

            #tar xvzf boost_1_50_0.tar.gz

            進(jìn)入boost_1_50_0目錄:

            #cd boost_1_50_0

            然后是編譯安裝,boost源碼包中有配置腳本,直接用就可以:

            #sh ./bootstrap.sh

            Building Boost.Build engine with toolset gcc... tools/build/v2/engine/bin.linuxx86_64/b2
            Detecting Python version... 2.6
            Detecting Python root... /usr
            Unicode/ICU support for Boost.Regex?... not found.
            Generating Boost.Build configuration in project-config.jam...

            Bootstrapping is done. To build, run:

            ./b2

            To adjust configuration, edit 'project-config.jam'.
            Further information:

            - Command line help:
            ./b2 --help

            - Getting started guide:
            http://www.boost.org/more/getting_started/unix-variants.html

            - Boost.Build documentation:
            http://www.boost.org/boost-build2/doc/html/index.html

            接下來就是編譯,重點(diǎn)關(guān)注是否編譯成功:

            #./b2

            然后就是漫長(zhǎng)的等待,如果最后出現(xiàn):

            The Boost C++ Libraries were successfully built!

            The following directory should be added to compiler include paths:

            /home/gang/BAK/boost_1_50_0

            The following directory should be added to linker library paths:

            /home/gang/BAK/boost_1_50_0/stage/lib

            表示編譯成功,如果沒有成功,就需要回查看哪里出現(xiàn)error,再安裝相應(yīng)的庫(kù),
            最后就是安裝:

            #./b2 install --prefix=/usr/local

            安裝后的頭文件在/usr/local/include/boost里面,而相應(yīng)的庫(kù)在/usr/local/lib/libboost_*

            posted @ 2014-09-23 09:29 鑫龍 閱讀(1089) | 評(píng)論 (0)編輯 收藏

            2014年9月17日

            轉(zhuǎn)自:http://www.cnblogs.com/jiqing9006/archive/2012/09/11/2679831.html

            冒泡事件就是點(diǎn)擊子節(jié)點(diǎn),會(huì)向上觸發(fā)父節(jié)點(diǎn),祖先節(jié)點(diǎn)的點(diǎn)擊事件。

            下面是html代碼部分:

            <body>
            <div id="content">
                外層div元素
                <span>內(nèi)層span元素</span>
                外層div元素
            </div>
            
            <div id="msg"></div>
            </body>

            對(duì)應(yīng)的jQuery代碼如下:

            <script type="text/javascript">
            $(function(){
                // 為span元素綁定click事件
                $('span').bind("click",function(){
                    var txt = $('#msg').html() + "<p>內(nèi)層span元素被點(diǎn)擊.<p/>";//獲取html信息
                    $('#msg').html(txt);// 設(shè)置html信息
                });
                // 為div元素綁定click事件
                $('#content').bind("click",function(){
                    var txt = $('#msg').html() + "<p>外層div元素被點(diǎn)擊.<p/>";
                    $('#msg').html(txt);
                });
                // 為body元素綁定click事件
                $("body").bind("click",function(){
                    var txt = $('#msg').html() + "<p>body元素被點(diǎn)擊.<p/>";
                    $('#msg').html(txt);
                });
            })
            </script>

            當(dāng)點(diǎn)擊span時(shí),會(huì)觸發(fā)div與body 的點(diǎn)擊事件。點(diǎn)擊div時(shí)會(huì)觸發(fā)body的點(diǎn)擊事件。

            如何防止這種冒泡事件發(fā)生呢?

            修改如下:

            <script type="text/javascript">
            $(function(){
                   // 為span元素綁定click事件
                $('span').bind("click",function(event){
                    var txt = $('#msg').html() + "<p>內(nèi)層span元素被點(diǎn)擊.<p/>";
                    $('#msg').html(txt);
                    event.stopPropagation();    //  阻止事件冒泡
                });
                // 為div元素綁定click事件
                $('#content').bind("click",function(event){
                    var txt = $('#msg').html() + "<p>外層div元素被點(diǎn)擊.<p/>";
                    $('#msg').html(txt);
                    event.stopPropagation();    //  阻止事件冒泡
                });
                // 為body元素綁定click事件
                $("body").bind("click",function(){
                    var txt = $('#msg').html() + "<p>body元素被點(diǎn)擊.<p/>";
                    $('#msg').html(txt);
                });
            })
            </script>

            event.stopPropagation(); // 阻止事件冒泡

             

            有時(shí)候點(diǎn)擊提交按鈕會(huì)有一些默認(rèn)事件。比如跳轉(zhuǎn)到別的界面。但是如果沒有通過驗(yàn)證的話,就不應(yīng)該跳轉(zhuǎn)。這時(shí)候可以通過設(shè)置event.preventDefault(); //阻止默認(rèn)行為 ( 表單提交 )。

            下面是案例:

            <script type="text/javascript">
            $(function(){
               $("#sub").bind("click",function(event){
                     var username = $("#username").val();  //獲取元素的值,val() 方法返回或設(shè)置被選元素的值。
                     if(username==""){     //判斷值是否為空
                         $("#msg").html("<p>文本框的值不能為空.</p>");  //提示信息
                         event.preventDefault();  //阻止默認(rèn)行為 ( 表單提交 )
                     }
               })
            })
            </script>

            html部分:

            <body>
            <form action="test.html">
            用戶名:<input type="text" id="username" />
            <br/>
            <input type="submit" value="提交" id="sub"/>
            </form>
            
            <div id="msg"></div>
            </body>

            還有一種防止默認(rèn)行為的方法就是return false。效果一樣。

            代碼如下:

            <script type="text/javascript">
            $(function(){
               $("#sub").bind("click",function(event){
                     var username = $("#username").val();  //獲取元素的值
                     if(username==""){     //判斷值是否為空
                         $("#msg").html("<p>文本框的值不能為空.</p>");  //提示信息
                         return false;
                     }
               })
            })
            </script>

            同理,上面的冒泡事件也可以通過return false來處理。

            <script type="text/javascript">
            $(function(){
                   // 為span元素綁定click事件
                $('span').bind("click",function(event){
                    var txt = $('#msg').html() + "<p>內(nèi)層span元素被點(diǎn)擊.<p/>";
                    $('#msg').html(txt);
                    return false;
                });
                // 為div元素綁定click事件
                $('#content').bind("click",function(event){
                    var txt = $('#msg').html() + "<p>外層div元素被點(diǎn)擊.<p/>";
                    $('#msg').html(txt);
                    return false;
                });
                // 為body元素綁定click事件
                $("body").bind("click",function(){
                    var txt = $('#msg').html() + "<p>body元素被點(diǎn)擊.<p/>";
                    $('#msg').html(txt);
                });
            })
            </script>

            posted @ 2014-09-17 14:22 鑫龍 閱讀(284) | 評(píng)論 (0)編輯 收藏

            2014年9月12日

            轉(zhuǎn)自:http://www.cnblogs.com/jiayy/p/3246167.html

            在看多核編程相關(guān)論文時(shí),往往一個(gè)并發(fā)算法會(huì)說自己是wait-free的或者lock-free的,或者是 non-blocking 的,這些專有詞匯其實(shí)表示的是并發(fā)的程度,或者說并發(fā)的級(jí)別。并發(fā)級(jí)別的理解是閱讀各種并發(fā)算法設(shè)計(jì)論文以及并發(fā)數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)的必備基礎(chǔ)。

            1.1  Wait-freedom 無(wú)等待并發(fā)

            Wait-freedom 指的是每一個(gè)線程都一直運(yùn)行下去而無(wú)須等待外部條件,整個(gè)流程中任何操作都能在一個(gè)有限的步驟內(nèi)完成,這是最高的并發(fā)級(jí)別,沒有任何阻塞。

             結(jié)合之前原子操作部分的知識(shí),可以簡(jiǎn)單認(rèn)為能夠直接調(diào)用一個(gè)原子操作實(shí)現(xiàn)的算法或程序就屬于Wait-free,比如下面的 increment_reference_counter 函數(shù)就是wait-free的,它封裝了atomic_increment這個(gè)原子自增原語(yǔ),多個(gè)線程可以同時(shí)調(diào)用這個(gè)函數(shù)對(duì)同一個(gè)內(nèi)存變量進(jìn)行自增,而無(wú)須任何阻塞(其實(shí)也是有阻塞的,是總線鎖級(jí)別)

             與此做對(duì)比,CAS類的調(diào)用就不是wait-free的,注意wait-free的原語(yǔ)都不能包含內(nèi)部循環(huán),CAS原語(yǔ)使用時(shí)通常包含在“循環(huán)直到成功”的循環(huán)內(nèi)部。

             void increment_reference_counter(rc_base* obj)

            {

                atomic_increment(obj->rc);

            }

            1.2  Lock-freedom 無(wú)鎖并發(fā)

            Lock-freedom 指的是整個(gè)系統(tǒng)作為一個(gè)整體一直運(yùn)行下去,系統(tǒng)內(nèi)部單個(gè)線程某段時(shí)間內(nèi)可能會(huì)饑餓,這是比wait-freedom弱的并發(fā)級(jí)別,但系統(tǒng)整體上看依然是沒有阻塞的。所有wait-free的算法顯然都滿足lock-free的要求。

             Lock-free算法通常可以通過同步原語(yǔ) CAS實(shí)現(xiàn)。

             void stack_push(stack* s, node* n)

            {

                node* head;

                do

                {

                    head = s->head;

                    n->next = head;

                }

                while ( ! atomic_compare_exchange(s->head, head, n));

            }

            多個(gè)線程同時(shí)調(diào)用上述函數(shù),理論上某個(gè)線程可以一直困在循環(huán)內(nèi)部,但一旦有一個(gè)線程原子操作失敗而返回循環(huán),意味著有其他線程成功執(zhí)行了原子操作而退出循環(huán),從而保證系統(tǒng)整體是沒有阻塞的。

             其實(shí)前面的原子自增函數(shù)也可以用下面的原語(yǔ)實(shí)現(xiàn),在這種實(shí)現(xiàn)里,不再是所有線程都無(wú)阻塞了,某些線程可能會(huì)因?yàn)镃AS失敗而回繞若干次循環(huán)。

            void increment_reference_counter(rc_base* obj)

            {

                   Int rc;

                   Do {

                   rc = obj->rc;

            } while(!atomic_compare_exchange(obj->rc,rc,rc+1));

            }

            1.3  Obstruction-freedom 無(wú)阻塞并發(fā)

            Obstruction-free 是指在任何時(shí)間點(diǎn),一個(gè)孤立運(yùn)行線程的每一個(gè)操作可以在有限步之內(nèi)結(jié)束。只要沒有競(jìng)爭(zhēng),線程就可以持續(xù)運(yùn)行,一旦共享數(shù)據(jù)被修改,Obstruction-free 要求中止已經(jīng)完成的部分操作,并進(jìn)行回滾,obstruction-free 是并發(fā)級(jí)別更低的非阻塞并發(fā),該算法在不出現(xiàn)沖突性操作的情況下提供單線程式的執(zhí)行進(jìn)度保證,所有 Lock-Free 的算法都是 Obstruction-free 的。

            1.4  Blocking algoithms 阻塞并發(fā)

            阻塞類的算法是并發(fā)級(jí)別最低的同步算法,它一般需要產(chǎn)生阻塞。可以簡(jiǎn)單認(rèn)為基于鎖的實(shí)現(xiàn)是blocking的算法。詳細(xì)參考第五章

            上述幾種并發(fā)級(jí)別可以使用下圖描述:
            藍(lán)色是阻塞的算法,綠色是非阻塞算法,金字塔越上方,并發(fā)級(jí)別越高,性能越好,右邊的金字塔是實(shí)現(xiàn)工具(原子操作、鎖、互斥體等)

            posted @ 2014-09-12 13:05 鑫龍 閱讀(423) | 評(píng)論 (0)編輯 收藏

            2014年9月3日

            轉(zhuǎn)自:http://www.isnowfy.com/understand-to-lock-free/

            以前一直不明白lock free是什么,后來發(fā)現(xiàn)原來是完全理解錯(cuò)了概念,lock free看到大家有的翻譯為無(wú)鎖,有的翻譯為鎖無(wú)關(guān),其實(shí)用不用鎖和lock free是不相關(guān)的,用了鎖也可能是lock free,而不用鎖有可能不是lock free。

            一個(gè)lock free的解釋是

            一個(gè)“鎖無(wú)關(guān)”的程序能夠確保執(zhí)行它的所有線程中至少有一個(gè)能夠繼續(xù)往下執(zhí)行。

            其實(shí)看我們那副圖就是說你的各個(gè)線程不會(huì)互相阻塞,那么你的程序才能成為lock free的。像我們平常用的互斥鎖,當(dāng)有線程獲得鎖,其他線程就被阻塞掉了,這里的問題就是如果獲得鎖的線程掛掉了,而且鎖也沒有釋放,那么整個(gè)程序其實(shí)就被block在那了,而如果程序是lock free的那么即使有線程掛掉,也不影響整個(gè)程序繼續(xù)向下進(jìn)行,也就是系統(tǒng)在整體上而言是一直前進(jìn)的。

            那么,不用鎖就是lock free的嗎,一開始就提到了,不用鎖也可能不是lock free的,舉個(gè)例子

            1. while (x == 0) {
            2.     x = 1-x;
            3. }

            在這里如果兩個(gè)線程同時(shí)執(zhí)行,可能同時(shí)進(jìn)入while循環(huán),然后x兩次改變值之后,依然是0,那么兩個(gè)線程就會(huì)一直互相在這里阻塞掉了,所以這里雖然沒有鎖,依然不是lock free的。

            現(xiàn)在大家寫lock free的時(shí)候一般都會(huì)使用CAS(compare and set)操作來寫,因?yàn)楝F(xiàn)在很多的cpu都是支持CAS操作并作為原子操作來處理的,CAS操作一般是這樣的

            1. bool compare_and_swap (int *oldval, int *dest, int newval) {
            2.   if (*oldval == *dest) {
            3.       *dest = newval;
            4.       return true;
            5.   }
            6.   return false;
            7. }

            其實(shí)這樣一個(gè)操作和樂觀鎖很像,并且操作簡(jiǎn)單,相應(yīng)的比互斥鎖的代價(jià)要小。所以現(xiàn)在大家都是喜歡用lock free的技術(shù)來提高系統(tǒng)的performance。

            最后如果大家對(duì)于如何編寫lock free的數(shù)據(jù)結(jié)構(gòu)感興趣的話,可以參考我后面給出的鏈接。

            一種高效無(wú)鎖內(nèi)存隊(duì)列的實(shí)現(xiàn)
            無(wú)鎖隊(duì)列的實(shí)現(xiàn)
            鎖無(wú)關(guān)的(Lock-Free)數(shù)據(jù)結(jié)構(gòu)
            An Introduction to Lock-Free Programming

            posted @ 2014-09-03 21:11 鑫龍 閱讀(3239) | 評(píng)論 (0)編輯 收藏

            2014年8月23日

            轉(zhuǎn)自:http://zhucuicui.96986.blog.163.com/blog/static/5833370220136219016445/

            建立socket后默認(rèn)connect()函數(shù)為阻塞連接狀態(tài),在大多數(shù)實(shí)現(xiàn)中,connect的超時(shí)時(shí)間在75s至幾分鐘之間,想要縮短超時(shí)時(shí)間,可解決問題的兩種方法:方法一、將socket句柄設(shè)置為非阻塞狀態(tài),方法二、采用信號(hào)處理函數(shù)設(shè)置阻塞超時(shí)控制。

            在一個(gè)TCP套接口被設(shè)置為非阻塞之后調(diào)用connect,connect會(huì)立即返回EINPROGRESS錯(cuò)誤,表示連接操作正在進(jìn)行中,但是仍未完成;同時(shí)TCP的三路握手操作繼續(xù)進(jìn)行;在這之后,我們可以調(diào)用select來檢查這個(gè)鏈接是否建立成功;非阻塞connect有三種用途:
            1.我們可以在三路握手的同時(shí)做一些其它的處理.connect操作要花一個(gè)往返時(shí)間完成,而且可以是在任何地方,從幾個(gè)毫秒的局域網(wǎng)到幾百毫秒或幾秒的廣域網(wǎng).在這段時(shí)間內(nèi)我們可能有一些其他的處理想要執(zhí)行;
            2.可以用這種技術(shù)同時(shí)建立多個(gè)連接.在Web瀏覽器中很普遍;
            3.由于我們使用select來等待連接的完成,因此我們可以給select設(shè)置一個(gè)時(shí)間限制,從而縮短connect的超時(shí)時(shí)間.在大多數(shù)實(shí)現(xiàn)中,connect的超時(shí)時(shí)間在75秒到幾分鐘之間.有時(shí)候應(yīng)用程序想要一個(gè)更短的超時(shí)時(shí)間,使用非阻塞connect就是一種方法;
            非阻塞connect聽起來雖然簡(jiǎn)單,但是仍然有一些細(xì)節(jié)問題要處理:
            1.即使套接口是非阻塞的,如果連接的服務(wù)器在同一臺(tái)主機(jī)上,那么在調(diào)用connect建立連接時(shí),連接通常會(huì)立即建立成功.我們必須處理這種情況;
            2.源自Berkeley的實(shí)現(xiàn)(和Posix.1g)有兩條與select和非阻塞IO相關(guān)的規(guī)則:
            A:當(dāng)連接建立成功時(shí),套接口描述符變成可寫;
            B:當(dāng)連接出錯(cuò)時(shí),套接口描述符變成既可讀又可寫;
            注意:當(dāng)一個(gè)套接口出錯(cuò)時(shí),它會(huì)被select調(diào)用標(biāo)記為既可讀又可寫;

            非阻塞connect有這么多好處,但是處理非阻塞connect時(shí)會(huì)遇到很多可移植性問題;

            處理非阻塞connect的步驟:
            第一步:創(chuàng)建socket,返回套接口描述符;
            第二步:調(diào)用fcntl把套接口描述符設(shè)置成非阻塞;
            第三步:調(diào)用connect開始建立連接;
            第四步:判斷連接是否成功建立;
            A:如果connect返回0,表示連接簡(jiǎn)稱成功(服務(wù)器可客戶端在同一臺(tái)機(jī)器上時(shí)就有可能發(fā)生這種情況);
            B:調(diào)用select來等待連接建立成功完成;
            如果select返回0,則表示建立連接超時(shí);我們返回超時(shí)錯(cuò)誤給用戶,同時(shí)關(guān)閉連接,以防止三路握手操作繼續(xù)進(jìn)行下去;
            如果select返回大于0的值,則需要檢查套接口描述符是否可讀或可寫;如果套接口描述符可讀或可寫,則我們可以通過調(diào)用getsockopt來得到套接口上待處理的錯(cuò)誤(SO_ERROR),如果連接建立成功,這個(gè)錯(cuò)誤值將是0,如果建立連接時(shí)遇到錯(cuò)誤,則這個(gè)值是連接錯(cuò)誤所對(duì)應(yīng)的errno值(比如:ECONNREFUSED,ETIMEDOUT等).
            "讀取套接口上的錯(cuò)誤"是遇到的第一個(gè)可移植性問題;如果出現(xiàn)問題,getsockopt源自Berkeley的實(shí)現(xiàn)是返回0,等待處理的錯(cuò)誤在變量errno中返回;但是Solaris會(huì)讓getsockopt返回-1,errno置為待處理的錯(cuò)誤;我們對(duì)這兩種情況都要處理;

            這樣,在處理非阻塞connect時(shí),在不同的套接口實(shí)現(xiàn)的平臺(tái)中存在的移植性問題,首先,有可能在調(diào)用select之前,連接就已經(jīng)建立成功,而且對(duì)方的數(shù)據(jù)已經(jīng)到來.在這種情況下,連接成功時(shí)套接口將既可讀又可寫.這和連接失敗時(shí)是一樣的.這個(gè)時(shí)候我們還得通過getsockopt來讀取錯(cuò)誤值;這是第二個(gè)可移植性問題;
            移植性問題總結(jié):
            1.對(duì)于出錯(cuò)的套接口描述符,getsockopt的返回值源自Berkeley的實(shí)現(xiàn)是返回0,待處理的錯(cuò)誤值存儲(chǔ)在errno中;而源自Solaris的實(shí)現(xiàn)是返回0,待處理的錯(cuò)誤存儲(chǔ)在errno中;(套接口描述符出錯(cuò)時(shí)調(diào)用getsockopt的返回值不可移植)
            2.有可能在調(diào)用select之前,連接就已經(jīng)建立成功,而且對(duì)方的數(shù)據(jù)已經(jīng)到來,在這種情況下,套接口描述符是既可讀又可寫;這與套接口描述符出錯(cuò)時(shí)是一樣的;(怎樣判斷連接是否建立成功的條件不可移植)

            這樣的話,在我們判斷連接是否建立成功的條件不唯一時(shí),我們可以有以下的方法來解決這個(gè)問題:
            1.調(diào)用getpeername代替getsockopt.如果調(diào)用getpeername失敗,getpeername返回ENOTCONN,表示連接建立失敗,我們必須以SO_ERROR調(diào)用getsockopt得到套接口描述符上的待處理錯(cuò)誤;
            2.調(diào)用read,讀取長(zhǎng)度為0字節(jié)的數(shù)據(jù).如果read調(diào)用失敗,則表示連接建立失敗,而且read返回的errno指明了連接失敗的原因.如果連接建立成功,read應(yīng)該返回0;
            3.再調(diào)用一次connect.它應(yīng)該失敗,如果錯(cuò)誤errno是EISCONN,就表示套接口已經(jīng)建立,而且第一次連接是成功的;否則,連接就是失敗的;

            被中斷的connect:
            如果在一個(gè)阻塞式套接口上調(diào)用connect,在TCP的三路握手操作完成之前被中斷了,比如說,被捕獲的信號(hào)中斷,將會(huì)發(fā)生什么呢?假定connect不會(huì)自動(dòng)重啟,它將返回EINTR.那么,這個(gè)時(shí)候,我們就不能再調(diào)用connect等待連接建立完成了,如果再次調(diào)用connect來等待連接建立完成的話,connect將會(huì)返回錯(cuò)誤值EADDRINUSE.在這種情況下,應(yīng)該做的是調(diào)用select,就像在非阻塞式connect中所做的一樣.然后,select在連接建立成功(使套接口描述符可寫)或連接建立失敗(使套接口描述符既可讀又可寫)時(shí)返回;

            方法二、定義信號(hào)處理函數(shù):

             

            1. sigset(SIGALRM, u_alarm_handler);
            2. alarm(2);
            3. code = connect(socket_fd, (struct sockaddr*)&socket_st, sizeof(struct sockaddr_in));
            4. alarm(0);
            5. sigrelse(SIGALRM);

            首先定義一個(gè)中斷信號(hào)處理函數(shù)u_alarm_handler,用于超時(shí)后的報(bào)警處理,然后定義一個(gè)2秒的定時(shí)器,執(zhí)行connect,當(dāng)系統(tǒng)connect成功,則系統(tǒng)正常執(zhí)行下去;如果connect不成功阻塞在這里,則超過定義的2秒后,系統(tǒng)會(huì)產(chǎn)生一個(gè)信號(hào),觸發(fā)執(zhí)行u_alarm_handler函數(shù), 當(dāng)執(zhí)行完u_alarm_handler后,程序?qū)⒗^續(xù)從connect的下面一行執(zhí)行下去。
            其中,處理函數(shù)可以如下定義,也可以加入更多的錯(cuò)誤處理。

            1. void u_alarm_handler()
            2. {
            3. }

            posted @ 2014-08-23 17:01 鑫龍 閱讀(1657) | 評(píng)論 (0)編輯 收藏

            2014年8月7日

            引用:http://m.shnenglu.com/isware/archive/2011/07/20/151470.aspx
            -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
            “據(jù)說”驚群?jiǎn)栴}已經(jīng)是一個(gè)很古老的問題了,并且在大多數(shù)系統(tǒng)中已經(jīng)得到有效解決,但對(duì)我來說,仍舊是一個(gè)比較新的概念,因此有必要記錄一下。

            什么是驚群

                    舉一個(gè)很簡(jiǎn)單的例子,當(dāng)你往一群鴿子中間扔一塊食物,雖然最終只有一個(gè)鴿子搶到食物,但所有鴿子都會(huì)被驚動(dòng)來爭(zhēng)奪,沒有搶到食物的鴿子只好回去繼續(xù)睡覺, 等待下一塊食物到來。這樣,每扔一塊食物,都會(huì)驚動(dòng)所有的鴿子,即為驚群。對(duì)于操作系統(tǒng)來說,多個(gè)進(jìn)程/線程在等待同一資源是,也會(huì)產(chǎn)生類似的效果,其結(jié) 果就是每當(dāng)資源可用,所有的進(jìn)程/線程都來競(jìng)爭(zhēng)資源,造成的后果:
            1)系統(tǒng)對(duì)用戶進(jìn)程/線程頻繁的做無(wú)效的調(diào)度、上下文切換,系統(tǒng)系能大打折扣。
            2)為了確保只有一個(gè)線程得到資源,用戶必須對(duì)資源操作進(jìn)行加鎖保護(hù),進(jìn)一步加大了系統(tǒng)開銷。

                    最常見的例子就是對(duì)于socket描述符的accept操作,當(dāng)多個(gè)用戶進(jìn)程/線程監(jiān)聽在同一個(gè)端口上時(shí),由于實(shí)際只可能accept一次,因此就會(huì)產(chǎn)生驚群現(xiàn)象,當(dāng)然前面已經(jīng)說過了,這個(gè)問題是一個(gè)古老的問題,新的操作系統(tǒng)內(nèi)核已經(jīng)解決了這一問題。

            linux內(nèi)核解決驚群?jiǎn)栴}的方法

                    對(duì)于一些已知的驚群?jiǎn)栴},內(nèi)核開發(fā)者增加了一個(gè)“互斥等待”選項(xiàng)。一個(gè)互斥等待的行為與睡眠基本類似,主要的不同點(diǎn)在于:
                    1)當(dāng)一個(gè)等待隊(duì)列入口有 WQ_FLAG_EXCLUSEVE 標(biāo)志置位, 它被添加到等待隊(duì)列的尾部. 沒有這個(gè)標(biāo)志的入口項(xiàng), 相反, 添加到開始.
                    2)當(dāng) wake_up 被在一個(gè)等待隊(duì)列上調(diào)用時(shí), 它在喚醒第一個(gè)有 WQ_FLAG_EXCLUSIVE 標(biāo)志的進(jìn)程后停止。
                    也就是說,對(duì)于互斥等待的行為,比如如對(duì)一個(gè)listen后的socket描述符,多線程阻塞accept時(shí),系統(tǒng)內(nèi)核只會(huì)喚醒所有正在等待此時(shí)間的隊(duì)列 的第一個(gè),隊(duì)列中的其他人則繼續(xù)等待下一次事件的發(fā)生,這樣就避免的多個(gè)線程同時(shí)監(jiān)聽同一個(gè)socket描述符時(shí)的驚群?jiǎn)栴}。

            根據(jù)以上背景信息,我們來比較一下常見的Server端設(shè)計(jì)方案。
            方案1:listen后,啟動(dòng)多個(gè)線程(進(jìn)程),對(duì)此socket進(jìn)行監(jiān)聽(僅阻塞accept方式不驚群)。
            方案2:主線程負(fù)責(zé)監(jiān)聽,通過線程池方式處理連接。(通常的方法)
            方案3:主線程負(fù)責(zé)監(jiān)聽,客戶端連接上來后由主線程分配實(shí)際的端口,客戶端根據(jù)此端口重新連接,然后處理數(shù)據(jù)。

            先考慮客戶端單連接的情況
            方案1:每當(dāng)有新的連接到來時(shí),系統(tǒng)內(nèi)核會(huì)從隊(duì)列中以FIFO的方式選擇一個(gè)監(jiān)聽線程來服務(wù)此連接,因此可以充分發(fā)揮系統(tǒng)的系能并且多線程負(fù)載均衡。對(duì)于單連接的場(chǎng)景,這種方案無(wú)疑是非常優(yōu)越的。遺憾的是,對(duì)于select、epoll,內(nèi)核目前無(wú)法解決驚群?jiǎn)栴}。(nginx對(duì)于驚群?jiǎn)栴}的解決方法)
            方案2:由于只有一個(gè)線程在監(jiān)聽,其瞬時(shí)的并發(fā)處理連接請(qǐng)求的能力必然不如多線程。同時(shí),需要對(duì)線程池做調(diào)度管理,必然涉及資源共享訪問,相對(duì)于方案一來說管理成本要增加不少,代碼復(fù)雜度提高,性能也有所下降。
            方案3:與方案2有不少類似的地方,其優(yōu)勢(shì)是不需要做線程調(diào)度。缺點(diǎn)是增加了主線程的負(fù)擔(dān),除了接收連接外還需要發(fā)送數(shù)據(jù),而且需要兩次連接,孰優(yōu)孰劣,有待測(cè)試。

            再考慮客戶端多連接的情況:
            對(duì)于數(shù)據(jù)傳輸類的應(yīng)用,為了充分利用帶寬,往往會(huì)開啟多個(gè)連接來傳輸數(shù)據(jù),連接之間的數(shù)據(jù)有相互依賴性,因此Server端要想很好的維護(hù)這種依賴性,把同一個(gè)客戶端的所有連接放在一個(gè)線程中處理是非常有必要的。
            A、同一客戶端在一個(gè)線程中處理
            方案1:如果沒有更底層的解決方案的話,Server則需要維護(hù)一個(gè)全局列表,來記錄當(dāng)前連接請(qǐng)求該由哪個(gè)線程處理。多線程需要同時(shí)競(jìng)爭(zhēng)一個(gè)全局資源,似乎有些不妙。
            方案2:主線程負(fù)責(zé)監(jiān)聽并分發(fā),因此與單連接相比沒有帶來額外的性能開銷。僅僅會(huì)造成主線程忙于更多的連接請(qǐng)求。
            方案3:較單線程來說,主線程工作量沒有任何增加,由于多連接而造成的額外開銷由實(shí)際工作線程分擔(dān),因此對(duì)于這種場(chǎng)景,方案3似乎是最佳選擇。

            B、同一客戶端在不同線程中處理
            方案1:同樣需要競(jìng)爭(zhēng)資源。
            方案2:沒理由。
            方案3:不可能。

            另外:
            (《UNIX網(wǎng)絡(luò)編程》第三版是在第30章)
            讀《UNIX網(wǎng)絡(luò)編程》第二版的第一卷時(shí),發(fā)現(xiàn)作者在第27章“客戶-服務(wù)器程序其它設(shè)計(jì)方法”中的27.6節(jié)“TCP預(yù)先派生子進(jìn)程服務(wù)器程序,accept無(wú)上鎖保護(hù)”中提到了一種由子進(jìn)程去競(jìng)爭(zhēng)客戶端連接的設(shè)計(jì)方法,用偽碼描述如下:

            服務(wù)器主進(jìn)程:

            listen_fd = socket(...);
            bind(listen_fd, ...);
            listen(listen_fd, ...);
            pre_fork_children(...);
            close(listen_fd);
            wait_children_die(...);


            服務(wù)器服務(wù)子進(jìn)程:

            while (1) {
            conn_fd = accept(listen_fd, ...);
            do_service(conn_fd, ...);
            }


            初識(shí)上述代碼,真有眼前一亮的感覺,也正如作者所說,以上代碼確實(shí)很少見(反正我讀此書之前是確實(shí)沒見過)。作者真是構(gòu)思精巧,巧妙地繞過了常見的預(yù)先創(chuàng)建 子進(jìn)程的多進(jìn)程服務(wù)器當(dāng)主服務(wù)進(jìn)程接收到新的連接必須想辦法將這個(gè)連接傳遞給服務(wù)子進(jìn)程的“陷阱”,上述代碼通過共享的傾聽套接字,由子進(jìn)程主動(dòng)地去向內(nèi) 核“索要”連接套接字,從而避免了用UNIX域套接字傳遞文件描述符的“淫技”。

            不過,當(dāng)接著往下讀的時(shí)候,作者談到了“驚群” (Thundering herd)問題。所謂的“驚群”就是,當(dāng)很多進(jìn)程都阻塞在accept系統(tǒng)調(diào)用的時(shí)候,即使只有一個(gè)新的連接達(dá)到,內(nèi)核也會(huì)喚醒所有阻塞在accept上 的進(jìn)程,這將給系統(tǒng)帶來非常大的“震顫”,降低系統(tǒng)性能。

            除了這個(gè)問題,accept還必須是原子操作。為此,作者在接下來的27.7節(jié)講述了加了互斥鎖的版本:

            while (1) {
            lock(...);
            conn_fd = accept(listen_fd, ...);
            unlock(...);
            do_service(conn_fd, ...);
            }


            原子操作的問題算是解決了,那么“驚群”呢?文中只是提到在Solaris系統(tǒng)上當(dāng)子進(jìn)程數(shù)由75變成90后,CPU時(shí)間顯著增加,并且作者認(rèn)為這是因?yàn)檫M(jìn) 程過多,導(dǎo)致內(nèi)存互換。對(duì)“驚群”問題回答地十分含糊。通過比較書中圖27.2的第4列和第7列的內(nèi)容,我們可以肯定“真兇”絕對(duì)不是“內(nèi)存對(duì)換”。

            “元兇”到底是誰(shuí)?

            仔細(xì)分析一下,加鎖真的有助于“驚群”問題么?不錯(cuò),確實(shí)在同一時(shí)間只有一個(gè)子進(jìn)程在調(diào)用accept,其它子進(jìn)程都阻塞在了lock語(yǔ)句,但是,當(dāng) accept返回并unlock之后呢?unlock肯定是要喚醒阻塞在這個(gè)鎖上的進(jìn)程的,不過誰(shuí)都沒有規(guī)定是喚醒一個(gè)還是喚醒多個(gè)。所以,潛在的“驚 群”問題還是存在,只不過換了個(gè)地方,換了個(gè)形式。而造成Solaris性能驟降的“罪魁禍?zhǔn)?#8221;很有可能就是“驚群”問題。

            崩潰了!這么說所有的鎖都有可能產(chǎn)生驚群?jiǎn)栴}了?

            似乎真的是這樣,所以減少鎖的使用很重要。特別是在競(jìng)爭(zhēng)比較激烈的地方。

            作者在27.9節(jié)所實(shí)現(xiàn)的“傳遞文件描述符”版本的服務(wù)器就有效地克服了“驚群”問題,在現(xiàn)實(shí)的服務(wù)器實(shí)現(xiàn)中,最常用的也是此節(jié)所提到的基于“分配”形式。

            把“競(jìng)爭(zhēng)”換成“分配”是避免“驚群”問題的有效方法,但是也不要忽視“分配”的“均衡”問題,不然后果可能更加嚴(yán)重哦!

            posted @ 2014-08-07 14:21 鑫龍 閱讀(563) | 評(píng)論 (0)編輯 收藏

            2014年7月25日

            程序設(shè)計(jì)類
            《C++ PRIMER》
            《STL源碼解析》


            系統(tǒng)編程類
            《UNIX環(huán)境高級(jí)編程》W.Richard Stevens:非常經(jīng)典的書。雖然初學(xué)者就可以看,但是事實(shí)上它是《Unix Network Programing》的一本輔助資料。國(guó)內(nèi)的翻譯的《UNIX環(huán)境高級(jí)編程》的水平不怎么樣,現(xiàn)在有影印版,直接讀英文比讀中文來得容易。
            《Unix網(wǎng)絡(luò)編程卷二》Unix網(wǎng)絡(luò)編程卷第二卷沒有涉及網(wǎng)絡(luò)的東西,主要講進(jìn)程間通訊和Posix線程。



            網(wǎng)絡(luò)編程類
            《Unix網(wǎng)絡(luò)編程》第一卷講BSD Socket網(wǎng)絡(luò)編程接口和另外一種網(wǎng)絡(luò)編程接口的,不過現(xiàn)在一般都用BSD Socket,所以這本書只要看大約一半多就可以了。
            《TCP/IP詳解》一共三卷,卷一講協(xié)議,卷二講實(shí)現(xiàn),卷三講編程應(yīng)用,也很經(jīng)典的。
            《用TCP/IP進(jìn)行網(wǎng)際互連》一共三卷,內(nèi)容講解十分精彩。卷一講原理,卷二講實(shí)現(xiàn),卷三講高級(jí)協(xié)議。感覺上這一套要比Stevens的那一套要好,就連Stevens也不得不承認(rèn)它的第一卷非常經(jīng)典。事實(shí)上,第一卷即使你沒有一點(diǎn)網(wǎng)絡(luò)的知識(shí),看完以后也會(huì)對(duì)網(wǎng)絡(luò)的來龍去脈了如指掌。第一卷中還有很多習(xí)題也設(shè)計(jì)得經(jīng)典和實(shí)用,因?yàn)樽髡弑旧砭褪且晃唤處煟⑶揖硪皇菄?guó)外研究生的教材。習(xí)題并沒有答案,留給讀者思考,因?yàn)閱栴}得答案可以讓你成為一個(gè)中級(jí)的Hacker,這些問題的答案可以象Douglus索取,不過只有他只給教師卷二我沒有怎么看,卷三可以作為參考手冊(cè),其中地例子也很經(jīng)典。
            Linux 多線程服務(wù)端編程:使用 muduo C++ 網(wǎng)絡(luò)庫(kù)》


            Linux系統(tǒng)管理類
            《linux系統(tǒng)管理手冊(cè)》
            《LINUX與UNIX SHELL編程指南》
            《Advanced Bash Scripting Guide》


            系統(tǒng)內(nèi)核類
            《Linux內(nèi)核代碼情景分析》
            《深入Linux內(nèi)核源碼》


            面向?qū)ο笤O(shè)計(jì)類
            《設(shè)計(jì)模式》
            《敏捷軟件開發(fā):原則、模式與實(shí)踐》
            《敏捷項(xiàng)目管理》


            內(nèi)功修煉類
            《操作系統(tǒng):設(shè)計(jì)與實(shí)現(xiàn)(第二版)》
            《操作系統(tǒng)概念》
            《數(shù)據(jù)結(jié)構(gòu)與算法-面向?qū)ο蟮腃++設(shè)計(jì)模式》
            《編譯原理》國(guó)防陳火旺
            《離散數(shù)學(xué)及其應(yīng)用》
            《計(jì)算機(jī)組織與體系結(jié)構(gòu)?性能分析》
            《深入理解計(jì)算機(jī)系統(tǒng)》【美】Randal E. Bryant David O'Hallaron著v

            posted @ 2014-07-25 20:29 鑫龍 閱讀(1670) | 評(píng)論 (0)編輯 收藏

            2014年4月8日

            程序如下:

            import java.util.*;

            import java.io.*;

            public class BadExecJavac

            {

                public static void main(String args[])

                {

                    try

                    {           

                        Runtime rt = Runtime.getRuntime();

                        Process proc = rt.exec("javac");

                        int exitVal = proc.exitValue();

                        System.out.println("Process exitValue: " + exitVal);

                    } catch (Throwable t)

                      {

                        t.printStackTrace();

                      }

                }

            }

            運(yùn)行結(jié)果如下

            E:\classes\com\javaworld\jpitfalls\article2>java BadExecJavac
            java.lang.IllegalThreadStateException: process has not exited       
            at java.lang.Win32Process.exitValue(Native Method)       
            at BadExecJavac.main(BadExecJavac.java:13)

            這是因?yàn)楫?dāng)進(jìn)程還沒有結(jié)束的情況下,調(diào)用exitValue方法會(huì)拋出IllegalThreadStateException.當(dāng)然了我們會(huì)問為什嗎這個(gè)方法不會(huì)等到進(jìn)程結(jié)束在返回一個(gè)合理的值?

            在檢查Process類的所有可用方法以后我們發(fā)現(xiàn)WairFor()是一個(gè)更合適的方法。事實(shí)上waitFor也會(huì)返回exit value。這意味著你不可以同時(shí)用exitvalue和waitfor,而是只能選擇一個(gè)。

            當(dāng)然了也有情況你要在waitfor之前用exitvalue方法:就是你不想因?yàn)橥獠砍绦蛴肋h(yuǎn)無(wú)法完成而一直等待下去。

            因此為了避免這個(gè)陷阱,我們要么捕獲IllegalThreadStateException異常,要么等待進(jìn)程完成。我們相當(dāng)然的以為可以用waitfor來等待程序的結(jié)束。代碼如下:

            import java.util.*;
            import java.io.*;
            public class BadExecJavac2{   
               public static void main(String args[])    {       
                  try        {                        
                     Runtime rt = Runtime.getRuntime();           
                     Process proc = rt.exec("javac");           
                     int exitVal = proc.waitFor();           
                     System.out.println("Process exitValue: " + exitVal);       
                  } catch (Throwable t)          {           
                     t.printStackTrace();         
                  }   
               }
            }

            這次在linux下面返回的結(jié)果是2,而在windows下面據(jù)說程序會(huì)掛起,關(guān)于其原因我們可以在jdk文檔中找到部分解釋:因?yàn)橐恍┎僮飨到y(tǒng)為標(biāo)準(zhǔn)的輸入輸出僅僅提供有限的緩沖區(qū),當(dāng)不能正確的將信息寫進(jìn)輸入流或者從輸出流中獲取信息時(shí),就會(huì)導(dǎo)致子進(jìn)程的阻塞,甚至死鎖。現(xiàn)在我們就根據(jù)jdk文檔來處理javac進(jìn)程的輸出,當(dāng)你不帶任何參數(shù)運(yùn)行javac時(shí),它會(huì)打印出一系列的有用的提示信息。而這些會(huì)被傳送到stderr流中。我們可以寫程序在其返回前獲取這些信息。下面的代碼提供了一個(gè)平庸的解決方案。

            import java.util.*;import java.io.*;
            public class MediocreExecJavac{   
               public static void main(String args[])    {       
                   try        {                        
                     Runtime rt = Runtime.getRuntime();           
                     Process proc = rt.exec("javac");           
                     InputStream stderr = proc.getErrorStream();           
                     InputStreamReader isr = new InputStreamReader(stderr);           
                     BufferedReader br = new BufferedReader(isr);           
                     String line = null;           
                     System.out.println("<ERROR>");           
                     while ( (line = br.readLine()) != null)               
                        System.out.println(line);           
                     System.out.println("</ERROR>");           
                     int exitVal = proc.waitFor();           
                     System.out.println("Process exitValue: " + exitVal);       
                  } catch (Throwable t)          {           
                      t.printStackTrace();         
                  }   
               }
            }

            這次程序可以正確的輸出了提示信息,但是我們應(yīng)該注意到其返回代碼是2,我們知道任何非0的返回代碼都表示程序不正常。所以我們需要進(jìn)一步的查找原因。對(duì)于win32而言是file not found,很明顯javac期望我們提供編譯的文件。所以對(duì)于永遠(yuǎn)掛起的問題,如果你運(yùn)行的程序會(huì)有輸出或者要求輸出入時(shí),你需要處理輸出和輸入。我在linux下面運(yùn)行的結(jié)果是正確的。前面說了在win32下面2代表是文件沒有找到,而在這種情況下表明是dir.exe沒有找到,(因?yàn)楦揪蜎]有這個(gè)文件,他們都被封裝到common.com (win95)或者cmd.exe中了。

            下面我們列出一個(gè)正確的處理Process的輸入輸出流的方法。需要用一個(gè)線程類。

            import java.util.*;
            import java.io.*;
            class StreamGobbler extends Thread{   
               InputStream is;   
               String type;        
               StreamGobbler(InputStream is, String type)    {       
                   this.is = is;       
                   this.type = type;   
               }        

               public void run()    {       
                  try        {           
                      InputStreamReader isr = new InputStreamReader(is);           
                      BufferedReader br = new BufferedReader(isr);           
                      String line=null;           
                  while ( (line = br.readLine()) != null)               
                  System.out.println(type + ">" + line);                
                  } catch (IOException ioe)             {               
                   ioe.printStackTrace();               
                  }   
               }
            }

            用于專門的處理輸入輸出。

            public class GoodWindowsExec{   
                public static void main(String args[])    {       
                    if (args.length < 1)        {           
                        System.out.println("USAGE: java GoodWindowsExec <cmd>");           
                        System.exit(1);       
                    }                
                    try        {                        
                        String osName = System.getProperty("os.name" );           
                        String[] cmd = new String[3];           
                        if( osName.equals( "Windows NT" ) )            {               
                            cmd[0] = "cmd.exe" ;               
                            cmd[1] = "/C" ;               
                            cmd[2] = args[0];           
                        }            else if( osName.equals( "Windows 95" ) )            {               
                            cmd[0] = "command.com" ;               
                            cmd[1] = "/C" ;               
                            cmd[2] = args[0];            }                        
                        Runtime rt = Runtime.getRuntime();            
                        System.out.println("Execing " + cmd[0] + " " + cmd[1] + " " + cmd[2]);           
                        Process proc = rt.exec(cmd);            // any error message?           
                        StreamGobbler errorGobbler = new StreamGobbler(proc.getErrorStream(), "ERROR");  // any output?           
                        StreamGobbler outputGobbler = new StreamGobbler(proc.getInputStream(), "OUTPUT");                            // kick them off           
                        errorGobbler.start();           
                        outputGobbler.start();                                                // any error???           
                        int exitVal = proc.waitFor();           
                        System.out.println("ExitValue: " + exitVal);                
                    } catch (Throwable t)          {           
                        t.printStackTrace();          
                    }   
                }
            }

            如果運(yùn)行如下命令上面的代碼會(huì)調(diào)用word程序

            >java GoodWindowExec “abc.doc”

            也就是說文件類型如果window能夠識(shí)別它就會(huì)調(diào)用對(duì)應(yīng)的程序處理。

            StreamGlobbler的最重要作用是他會(huì)清空所有的傳遞給他的inputstream,這樣不會(huì)造成Process阻塞或者死鎖。

            posted @ 2014-04-08 15:20 鑫龍 閱讀(3460) | 評(píng)論 (0)編輯 收藏

            99久久亚洲综合精品成人| 久久无码精品一区二区三区| 欧美精品乱码99久久蜜桃| 久久人妻无码中文字幕| 久久综合丁香激情久久| 一本久久a久久精品vr综合| 99热精品久久只有精品| 久久综合偷偷噜噜噜色| 99精品久久精品| 亚洲中文字幕伊人久久无码 | 久久亚洲色一区二区三区| 久久久久亚洲AV成人网人人网站 | 久久久WWW免费人成精品| 国产精品99久久久久久人| 麻豆国内精品久久久久久| 97久久超碰国产精品旧版| 人人狠狠综合久久亚洲高清| 中文字幕成人精品久久不卡| 国内精品伊人久久久久777| 国产一区二区三精品久久久无广告 | 草草久久久无码国产专区| 亚洲国产另类久久久精品| 亚洲国产成人精品91久久久| 国产精品久久久天天影视| 77777亚洲午夜久久多喷| 久久无码精品一区二区三区| 久久香蕉一级毛片| 77777亚洲午夜久久多喷| 中文字幕日本人妻久久久免费| 久久精品国产亚洲av瑜伽| 99热成人精品免费久久| 精品久久久久久99人妻| 久久精品免费一区二区三区| 亚洲成色999久久网站| 久久99热只有频精品8| 粉嫩小泬无遮挡久久久久久 | 久久99精品综合国产首页| 99久久国产热无码精品免费 | 99久久超碰中文字幕伊人| 久久精品国产精品亚洲毛片| 72种姿势欧美久久久久大黄蕉|