• <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>

            Source Insight的強(qiáng)大的代碼分析功能讓所有windows下的眾生受益菲淺。

            而Source insight的價格即使是面對Windows Vista也不逞多。嘿嘿。東西是好東西。

            個人認(rèn)為它也對得起這個價格。可惜沒米。用不起呀。

            咋辦呢。用vim,cscope打造一個免費的吧。


            1安裝cscope

            cscope的編譯和安裝沒有特別之處,./configure - make - make install即可。
            安轉(zhuǎn)完畢后先閱讀說明: vi /usr/share/vim/vim63/doc/if_cscop.txt.gz
            網(wǎng)上也有中文版本:http://vcd.gro.clinux.org/doc/if_cscop.html
            在vim中使用并不需要進(jìn)行太多的設(shè)置,不過首先vim編譯時必須加入了cscope的支持

            $ vim --version | grep cscope
            +cryptv +cscope +dialog_con +diff +digraphs -dnd -ebcdic +emacs_tags +eval


            嗯,我用的這個版本的vim是支持cscope的。

            按 照vim里cscope的參考手冊(在vim中執(zhí)行":help cscope"命令),把cscope功能加到.vimrc里后(需要你的vim在編譯時選擇了"--enable-cscope"選項,否則你需要重新 編譯vim),配置就算完成了。然后用下面的命令生成代碼的符號索引文件:

                cscope -Rbkq

            這個命令會生成三個文件:cscope.out, cscope.in.out, cscope.po.out。
            其中cscope.out是基本的符號索引,后兩個文件是使用"-q"選項生成的,可以加快cscope的索引速度。
            上面所用到的命令參數(shù),含義如下:

            -R: 在生成索引文件時,搜索子目錄樹中的代碼
            -b: 只生成索引文件,不進(jìn)入cscope的界面
            -k: 在生成索引文件時,不搜索
            /usr/include目錄
            -q: 生成cscope
            .in.out和cscope.po.out文件,加快cscope的索引速度


            接下來,就可以在vim里讀代碼了。
            不 過在使用過程中,發(fā)現(xiàn)無法找到C++的類、函數(shù)定義、調(diào)用關(guān)系。仔細(xì)閱讀了cscope的手冊后發(fā)現(xiàn),原來cscope在產(chǎn)生索引文件時,只搜索類型為 C, lex和yacc的文件(后綴名為.c, .h, .l, .y),C++的文件根本沒有生成索引。不過按照手冊上的說明,cscope支持c++和Java語言的文件。
            于是按照cscope手冊上提供的方法,先產(chǎn)生一個文件列表,然后讓cscope為這個列表中的每個文件都生成索引。
            為了方便使用,編寫了下面的腳本來更新cscope和ctags的索引文件:

            #!/bin/sh

            find . -name "*.h" -o -name "*.c" -o -name "*.cc" > cscope.files
            cscope -bkq -i cscope.files
            ctags -R


            這個腳本,首先使用find命令,查找當(dāng)前目錄及子目錄中所有后綴名為".h", ".c"和".cc"的文件,并把查找結(jié)果重定向到文件cscope.files中。
            然后cscope根據(jù)cscope.files中的所有文件,生成符號索引文件。
            最后一條命令使用ctags命令,生成一個tags文件,在vim中執(zhí)行":help tags"命令查詢它的用法。它可以和cscope一起使用。

            目前只能在unix系列操作系統(tǒng)下使用cscope,雖然也有windows版本的cscope,不過還有很多bug。在Linux技術(shù)中堅站上看到有作者在win2000上成功運行了gvim + cscope,詳情可以參閱:
            http://www.chinalinuxpub.com/bbs/showthread.php?t=30185



            cscope的主頁在:http://cscope.sourceforge.net/

            在vim的網(wǎng)站上,有很多和cscope相關(guān)的插件,可以去找一下你有沒有所感興趣的。搜索結(jié)果在這里:
            點這里


            為了方便地使用cscope,我們還需要下載cscope的鍵盤映射設(shè)置,
            這樣就可以在gvim中簡單地通過快捷鍵來使用 cscope,而不必敲復(fù)雜的命令了。鍵盤映射可以從
            這里下載:http://cscope.sourceforge.net/cscope_maps.vim
            將下載到的 cscope_maps.vim  
            文件: cscope_maps.vim.tar.gz
            大小: 2KB
            下載: 下載

            放在gvim的插件目錄里,如 C:\Program Files\Vim\vimfiles\plugin 中。Linux用戶可以放在
            $HOME/.vim/plugin 中。

            建立符號數(shù)據(jù)庫 †
            我們假設(shè)我們要閱讀的代碼放在 D:\src\myproject 下。然后打開命令行,進(jìn)入源代碼所在的目錄,
            為 cscope 建立搜索文件列表。在命令行中執(zhí)行以下命令:
            dir /s /b *.c *.h  > cscope.files
            如果你的源代碼是C++,則可以將 cpp 等擴(kuò)展名也加入到上面的命令中。
            dir /s /b *.c *.h *cpp *.hpp  > cscope.files
            如果是Linux用戶,則可以使用 find 命令實現(xiàn)同樣的功能:
            find $(pwd) -name "*.[ch]"
            然后執(zhí)行以下命令:
            cscope -b
            執(zhí)行結(jié)束后你可以在當(dāng)前目錄下發(fā)現(xiàn) cscope.out 文件,這就是 cscope 建立的符號數(shù)據(jù)庫。
            上面這個命令中,-b參數(shù)使得cscope不啟動自帶的用戶界面,而僅僅建立符號數(shù)據(jù)庫。

            瀏覽源代碼 †
            使用 gvim 打開你的源代碼目錄中任意一個C程序文件。然后在gvim中執(zhí)行如下命令:
            :cscope add D:\src\myproject\cscope.out
            由于在 gvim 中可以使用命令縮寫,因此上面的命令可以寫成:
            :cs a D:\src\myproject\cscope.out
            這樣就打開了剛剛建立的符號數(shù)據(jù)庫。通過下面的命令可以檢查數(shù)據(jù)庫連接的存在。
            :cscope show
            該命令可以縮寫為
            :cs s
            現(xiàn)在將光標(biāo)移動到源代碼中的某個函數(shù)名上,依次按下一下組合鍵:
            s
            稍等片刻之后你會在屏幕下放看到如下的字樣*1:
            Cscope tag: display
               #   line filename / context / line
               1    342 D:\src\myproject\src\global.h <>
                         void display(void );
               2    616 D:\src\myproject\src\command.c <>
                         display();
               3    138 D:\src\myproject\src\display.c <>
                         display(void )
               4    385 D:\src\myproject\src\main.c <>
                         display();
               5    652 D:\src\myproject\src\main.c <>
                         display();
               6    663 D:\src\myproject\src\main.c <>
                         display();
            Enter nr or choice ( to abort):
            這里顯示出的就是整個工程中使用到了 display 這個標(biāo)識符的位置。此時輸入 4,回車,
            即可跳轉(zhuǎn)到 main.c 的 385 行調(diào)用 display() 函數(shù)的地方進(jìn)行瀏覽。瀏覽結(jié)束后按 或者
            可以回到跳轉(zhuǎn)前的位置。
            然后將光標(biāo)移動到源代碼某個函數(shù)名上,迅速地依次安下面的組合鍵:
            s
            其中 按 Ctrl-2 即可輸入。同樣,屏幕上出現(xiàn)了一排結(jié)果,選擇之后你會發(fā)現(xiàn),
            跳轉(zhuǎn)到的文件將在水平方向的新窗口中打開。
            然后將光標(biāo)移動到源代碼某個函數(shù)名上,迅速地依次安下面的組合鍵:
            s
            選擇之后你會發(fā)現(xiàn),跳轉(zhuǎn)到的文件將在垂直方向的新窗口中打開。
            以上我們簡單介紹了cscope的使用方法,其中我們只用到了一個 s 命令,即跟在 和 后面的 s 鍵。
            同樣,我們可以使用以下的功能鍵實現(xiàn)不同的跳轉(zhuǎn)功能。
            c: 查找該函數(shù)被調(diào)用的位置 
            d: 查找該函數(shù)調(diào)用了哪些函數(shù)
            e: 查找指定的正規(guī)表達(dá)式
            f: 查找指定的文件
            g: 查找指定標(biāo)識符的定義位置
            i: 查找該文件在哪些地方被包含
            s: 查找指定標(biāo)識符的使用位置
            t: 查找指定的文本字符串

            命令行使用說明 †
            除了上述通過快捷鍵映射的方式使用cscope之外,也可以直接在gvim命令行中使用cscope。這樣就可以
            隨意定義查找字符串,而不必局限于源代碼中已有的標(biāo)識符。命令格式如下:
            :cscope find  <關(guān)鍵字>
            該命令可以縮寫為
            :cs f  <關(guān)鍵字>
            一個比較實用的技巧是使用cscope打開文件。使用以下命令即可直接打開名為display.c的文件,
            而不必先切換到display.c所在的目錄。
            :cs f f display.c
            cscope也支持正規(guī)表達(dá)式。如果記不清某個函數(shù)的名稱,可以用下面的方式來找到該函數(shù)的定義位置。
            :cs f g .*SetConfiguration.*

            版權(quán) †
            Cscope雖然不是GPL版權(quán),但是Cscope是開放源碼的自由軟件,使用Cscope無須支付任何費用。

            參考 †
            Cscope官方主頁, http://cscope.sourceforge.net/
            The Vim/Cscope tutorial, http://cscope.sourceforge.net/cscope_vim_tutorial.html
            Cscope on Win32, http://iamphet.nm.ru/cscope/
            Vim中關(guān)于 cscope 的幫助,使用 :help cscope 命令查看
            posted @ 2010-08-21 22:28 c++ 學(xué)習(xí) 閱讀(1173) | 評論 (0)編輯 收藏
             

            2.用完成例程方式實現(xiàn)的重疊I/O模型
            #include <WINSOCK2.H>
            #include <stdio.h>

            #define PORT    5150
            #define MSGSIZE 1024

            #pragma comment(lib, "ws2_32.lib")

            typedef struct
            {
            WSAOVERLAPPED overlap;
            WSABUF        Buffer;
            char          szMessage[MSGSIZE];
            DWORD         NumberOfBytesRecvd;
            DWORD         Flags; 
            SOCKET        sClient;
            }PER_IO_OPERATION_DATA, *LPPER_IO_OPERATION_DATA;

            DWORD WINAPI WorkerThread(LPVOID);
            void CALLBACK CompletionROUTINE(DWORD, DWORD, LPWSAOVERLAPPED, DWORD);

            SOCKET g_sNewClientConnection;
            BOOL   g_bNewConnectionArrived = FALSE;

            int main()
            {
            WSADATA     wsaData;
            SOCKET      sListen;
            SOCKADDR_IN local, client;
            DWORD       dwThreadId;
            int         iaddrSize = sizeof(SOCKADDR_IN);

            // Initialize Windows Socket library
            WSAStartup(0x0202, &wsaData);

            // Create listening socket
            sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

            // Bind
            local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
            local.sin_family = AF_INET;
            local.sin_port = htons(PORT);
            bind(sListen, (struct sockaddr *)&local, sizeof(SOCKADDR_IN));

            // Listen
            listen(sListen, 3);

            // Create worker thread
            CreateThread(NULL, 0, WorkerThread, NULL, 0, &dwThreadId);

            while (TRUE)
            {
                // Accept a connection
                g_sNewClientConnection = accept(sListen, (struct sockaddr *)&client, &iaddrSize);
                g_bNewConnectionArrived = TRUE;
                printf("Accepted client:%s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
            }
            }

            DWORD WINAPI WorkerThread(LPVOID lpParam)
            {
            LPPER_IO_OPERATION_DATA lpPerIOData = NULL;

            while (TRUE)
            {
                if (g_bNewConnectionArrived)
                {
                  // Launch an asynchronous operation for new arrived connection
                  lpPerIOData = (LPPER_IO_OPERATION_DATA)HeapAlloc(
                    GetProcessHeap(),
                    HEAP_ZERO_MEMORY,
                    sizeof(PER_IO_OPERATION_DATA));
                  lpPerIOData->Buffer.len = MSGSIZE;
                  lpPerIOData->Buffer.buf = lpPerIOData->szMessage;
                  lpPerIOData->sClient = g_sNewClientConnection;
                  
                  WSARecv(lpPerIOData->sClient,
                    &lpPerIOData->Buffer,
                    1,
                    &lpPerIOData->NumberOfBytesRecvd,
                    &lpPerIOData->Flags,
                    &lpPerIOData->overlap,
                    CompletionROUTINE);      
                  
                  g_bNewConnectionArrived = FALSE;
                }

                SleepEx(1000, TRUE);
            }
            return 0;
            }

            void CALLBACK CompletionROUTINE(DWORD dwError,
                                            DWORD cbTransferred,
                                            LPWSAOVERLAPPED lpOverlapped,
                                            DWORD dwFlags)
            {
            LPPER_IO_OPERATION_DATA lpPerIOData = (LPPER_IO_OPERATION_DATA)lpOverlapped;

            if (dwError != 0 || cbTransferred == 0)
            {
                // Connection was closed by client
            closesocket(lpPerIOData->sClient);
            HeapFree(GetProcessHeap(), 0, lpPerIOData);
            }
            else
            {
                lpPerIOData->szMessage[cbTransferred] = '\0';
                send(lpPerIOData->sClient, lpPerIOData->szMessage, cbTransferred, 0);
                
                // Launch another asynchronous operation
                memset(&lpPerIOData->overlap, 0, sizeof(WSAOVERLAPPED));
                lpPerIOData->Buffer.len = MSGSIZE;
                lpPerIOData->Buffer.buf = lpPerIOData->szMessage;   

                WSARecv(lpPerIOData->sClient,
                  &lpPerIOData->Buffer,
                  1,
                  &lpPerIOData->NumberOfBytesRecvd,
                  &lpPerIOData->Flags,
                  &lpPerIOData->overlap,
                  CompletionROUTINE);
            }
            }

            用完成例程來實現(xiàn)重疊I/O比用事件通知簡單得多。在這個模型中,主線程只用不停的接受連接即可;輔助線程判斷有沒有新的客戶端連接被建立,如果有,就為那個客戶端套接字激活一個異步的WSARecv操作,然后調(diào)用SleepEx使線程處于一種可警告的等待狀態(tài),以使得I/O完成后CompletionROUTINE可以被內(nèi)核調(diào)用。如果輔助線程不調(diào)用SleepEx,則內(nèi)核在完成一次I/O操作后,無法調(diào)用完成例程(因為完成例程的運行應(yīng)該和當(dāng)初激活WSARecv異步操作的代碼在同一個線程之內(nèi))。
            完成例程內(nèi)的實現(xiàn)代碼比較簡單,它取出接收到的數(shù)據(jù),然后將數(shù)據(jù)原封不動的發(fā)送給客戶端,最后重新激活另一個WSARecv異步操作。注意,在這里用到了“尾隨數(shù)據(jù)”。我們在調(diào)用WSARecv的時候,參數(shù)lpOverlapped實際上指向一個比它大得多的結(jié)構(gòu)PER_IO_OPERATION_DATA,這個結(jié)構(gòu)除了WSAOVERLAPPED以外,還被我們附加了緩沖區(qū)的結(jié)構(gòu)信息,另外還包括客戶端套接字等重要的信息。這樣,在完成例程中通過參數(shù)lpOverlapped拿到的不僅僅是WSAOVERLAPPED結(jié)構(gòu),還有后邊尾隨的包含客戶端套接字和接收數(shù)據(jù)緩沖區(qū)等重要信息。這樣的C語言技巧在我后面介紹完成端口的時候還會使用到。

            五.完成端口模型
            “完成端口”模型是迄今為止最為復(fù)雜的一種I/O模型。然而,假若一個應(yīng)用程序同時需要管理為數(shù)眾多的套接字,那么采用這種模型,往往可以達(dá)到最佳的系統(tǒng)性能!但不幸的是,該模型只適用于Windows NT和Windows 2000操作系統(tǒng)。因其設(shè)計的復(fù)雜性,只有在你的應(yīng)用程序需要同時管理數(shù)百乃至上千個套接字的時候,而且希望隨著系統(tǒng)內(nèi)安裝的CPU數(shù)量的增多,應(yīng)用程序的性能也可以線性提升,才應(yīng)考慮采用“完成端口”模型。要記住的一個基本準(zhǔn)則是,假如要為Windows NT或Windows 2000開發(fā)高性能的服務(wù)器應(yīng)用,同時希望為大量套接字I/O請求提供服務(wù)(Web服務(wù)器便是這方面的典型例子),那么I/O完成端口模型便是最佳選擇!(節(jié)選自《Windows網(wǎng)絡(luò)編程》第八章)
            完成端口模型是我最喜愛的一種模型。雖然其實現(xiàn)比較復(fù)雜(其實我覺得它的實現(xiàn)比用事件通知實現(xiàn)的重疊I/O簡單多了),但其效率是驚人的。我在T公司的時候曾經(jīng)幫同事寫過一個郵件服務(wù)器的性能測試程序,用的就是完成端口模型。結(jié)果表明,完成端口模型在多連接(成千上萬)的情況下,僅僅依靠一兩個輔助線程,就可以達(dá)到非常高的吞吐量。下面我還是從代碼說起:
            #include <WINSOCK2.H>
            #include <stdio.h>

            #define PORT    5150
            #define MSGSIZE 1024

            #pragma comment(lib, "ws2_32.lib")

            typedef enum
            {
            RECV_POSTED
            }OPERATION_TYPE;

            typedef struct
            {
            WSAOVERLAPPED overlap;
            WSABUF         Buffer;
            char           szMessage[MSGSIZE];
            DWORD          NumberOfBytesRecvd;
            DWORD          Flags;
            OPERATION_TYPE OperationType;
            }PER_IO_OPERATION_DATA, *LPPER_IO_OPERATION_DATA;

            DWORD WINAPI WorkerThread(LPVOID);

            int main()
            {
            WSADATA                 wsaData;
            SOCKET                  sListen, sClient;
            SOCKADDR_IN             local, client;
            DWORD                   i, dwThreadId;
            int                     iaddrSize = sizeof(SOCKADDR_IN);
            HANDLE                  CompletionPort = INVALID_HANDLE_VALUE;
            SYSTEM_INFO             systeminfo;
            LPPER_IO_OPERATION_DATA lpPerIOData = NULL;

            // Initialize Windows Socket library
            WSAStartup(0x0202, &wsaData);

            // Create completion port
            CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);

            // Create worker thread
            GetSystemInfo(&systeminfo);
            for (i = 0; i < systeminfo.dwNumberOfProcessors; i++)
            {
                CreateThread(NULL, 0, WorkerThread, CompletionPort, 0, &dwThreadId);
            }

            // Create listening socket
            sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

            // Bind
            local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
            local.sin_family = AF_INET;
            local.sin_port = htons(PORT);
            bind(sListen, (struct sockaddr *)&local, sizeof(SOCKADDR_IN));

            // Listen
            listen(sListen, 3);

            while (TRUE)
            {
                // Accept a connection
                sClient = accept(sListen, (struct sockaddr *)&client, &iaddrSize);
                printf("Accepted client:%s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));

                // Associate the newly arrived client socket with completion port
                CreateIoCompletionPort((HANDLE)sClient, CompletionPort, (DWORD)sClient, 0);
                
                // Launch an asynchronous operation for new arrived connection
                lpPerIOData = (LPPER_IO_OPERATION_DATA)HeapAlloc(
                  GetProcessHeap(),
                  HEAP_ZERO_MEMORY,
                  sizeof(PER_IO_OPERATION_DATA));
                lpPerIOData->Buffer.len = MSGSIZE;
                lpPerIOData->Buffer.buf = lpPerIOData->szMessage;
                lpPerIOData->OperationType = RECV_POSTED;
                WSARecv(sClient,
                  &lpPerIOData->Buffer,
                  1,
                  &lpPerIOData->NumberOfBytesRecvd,
                  &lpPerIOData->Flags,
                  &lpPerIOData->overlap,
                  NULL);
            }

            PostQueuedCompletionStatus(CompletionPort, 0xFFFFFFFF, 0, NULL);
            CloseHandle(CompletionPort);
            closesocket(sListen);
            WSACleanup();
            return 0;
            }

            DWORD WINAPI WorkerThread(LPVOID CompletionPortID)
            {
            HANDLE                  CompletionPort=(HANDLE)CompletionPortID;
            DWORD                   dwBytesTransferred;
            SOCKET                  sClient;
            LPPER_IO_OPERATION_DATA lpPerIOData = NULL;

            while (TRUE)
            {
                GetQueuedCompletionStatus(
                  CompletionPort,
                  &dwBytesTransferred,
                  &sClient,
                  (LPOVERLAPPED *)&lpPerIOData,
                  INFINITE);
                if (dwBytesTransferred == 0xFFFFFFFF)
                {
                  return 0;
                }
                
                if (lpPerIOData->OperationType == RECV_POSTED)
                {
                  if (dwBytesTransferred == 0)
                  {
                    // Connection was closed by client
                    closesocket(sClient);
                    HeapFree(GetProcessHeap(), 0, lpPerIOData);        
                  }
                  else
                  {
                    lpPerIOData->szMessage[dwBytesTransferred] = '\0';
                    send(sClient, lpPerIOData->szMessage, dwBytesTransferred, 0);
                    
                    // Launch another asynchronous operation for sClient
                    memset(lpPerIOData, 0, sizeof(PER_IO_OPERATION_DATA));
                    lpPerIOData->Buffer.len = MSGSIZE;
                    lpPerIOData->Buffer.buf = lpPerIOData->szMessage;
                    lpPerIOData->OperationType = RECV_POSTED;
                    WSARecv(sClient,
                      &lpPerIOData->Buffer,
                      1,
                      &lpPerIOData->NumberOfBytesRecvd,
                      &lpPerIOData->Flags,
                      &lpPerIOData->overlap,
                      NULL);
                  }
                }
            }
            return 0;
            }


            首先,說說主線程:
            1.創(chuàng)建完成端口對象
            2.創(chuàng)建工作者線程(這里工作者線程的數(shù)量是按照CPU的個數(shù)來決定的,這樣可以達(dá)到最佳性能)
            3.創(chuàng)建監(jiān)聽套接字,綁定,監(jiān)聽,然后程序進(jìn)入循環(huán)
            4.在循環(huán)中,我做了以下幾件事情:
            (1).接受一個客戶端連接
            (2).將該客戶端套接字與完成端口綁定到一起(還是調(diào)用CreateIoCompletionPort,但這次的作用不同),注意,按道理來講,此時傳遞給CreateIoCompletionPort的第三個參數(shù)應(yīng)該是一個完成鍵,一般來講,程序都是傳遞一個單句柄數(shù)據(jù)結(jié)構(gòu)的地址,該單句柄數(shù)據(jù)包含了和該客戶端連接有關(guān)的信息,由于我們只關(guān)心套接字句柄,所以直接將套接字句柄作為完成鍵傳遞;
            (3).觸發(fā)一個WSARecv異步調(diào)用,這次又用到了“尾隨數(shù)據(jù)”,使接收數(shù)據(jù)所用的緩沖區(qū)緊跟在WSAOVERLAPPED對象之后,此外,還有操作類型等重要信息。

            在工作者線程的循環(huán)中,我們
            1.調(diào)用GetQueuedCompletionStatus取得本次I/O的相關(guān)信息(例如套接字句柄、傳送的字節(jié)數(shù)、單I/O數(shù)據(jù)結(jié)構(gòu)的地址等等)
            2.通過單I/O數(shù)據(jù)結(jié)構(gòu)找到接收數(shù)據(jù)緩沖區(qū),然后將數(shù)據(jù)原封不動的發(fā)送到客戶端
            3.再次觸發(fā)一個WSARecv異步操作

            六.五種I/O模型的比較
            我會從以下幾個方面來進(jìn)行比較
            *有無每線程64連接數(shù)限制
            如果在選擇模型中沒有重新定義FD_SETSIZE宏,則每個fd_set默認(rèn)可以裝下64個SOCKET。同樣的,受MAXIMUM_WAIT_OBJECTS宏的影響,事件選擇、用事件通知實現(xiàn)的重疊I/O都有每線程最大64連接數(shù)限制。如果連接數(shù)成千上萬,則必須對客戶端套接字進(jìn)行分組,這樣,勢必增加程序的復(fù)雜度。
            相反,異步選擇、用完成例程實現(xiàn)的重疊I/O和完成端口不受此限制。

            *線程數(shù)
            除了異步選擇以外,其他模型至少需要2個線程。一個主線程和一個輔助線程。同樣的,如果連接數(shù)大于64,則選擇模型、事件選擇和用事件通知實現(xiàn)的重疊I/O的線程數(shù)還要增加。

            *實現(xiàn)的復(fù)雜度
            我的個人看法是,在實現(xiàn)難度上,異步選擇<選擇<用完成例程實現(xiàn)的重疊I/O<事件選擇<完成端口<用事件通知實現(xiàn)的重疊I/O

            *性能
            由于選擇模型中每次都要重設(shè)讀集,在select函數(shù)返回后還要針對所有套接字進(jìn)行逐一測試,我的感覺是效率比較差;完成端口和用完成例程實現(xiàn)的重疊I/O基本上不涉及全局?jǐn)?shù)據(jù),效率應(yīng)該是最高的,而且在多處理器情形下完成端口還要高一些;事件選擇和用事件通知實現(xiàn)的重疊I/O在實現(xiàn)機(jī)制上都是采用WSAWaitForMultipleEvents,感覺效率差不多;至于異步選擇,不好比較。所以我的結(jié)論是:選擇<用事件通知實現(xiàn)的重疊I/O<事件選擇<用完成例程實現(xiàn)的重疊I/O<完成端口

            posted @ 2010-08-03 17:27 c++ 學(xué)習(xí) 閱讀(440) | 評論 (0)編輯 收藏
             
                 摘要: 如果你想在Windows平臺上構(gòu)建服務(wù)器應(yīng)用,那么I/O模型是你必須考慮的。Windows操作系統(tǒng)提供了選擇(Select)、異步選擇(WSAAsyncSelect)、事件選擇(WSAEventSelect)、重疊I/O(Overlapped I/O)和完成端口(Completion Port)共五種I/O模型。每一種模型均適用于一種特定的應(yīng)用場景。程序員應(yīng)該對自己的應(yīng)用需求非常明確,而且綜合考慮...  閱讀全文
            posted @ 2010-08-03 17:26 c++ 學(xué)習(xí) 閱讀(457) | 評論 (0)編輯 收藏
             
                 摘要: 本文簡單介紹了當(dāng)前Windows支持的各種Socket I/O模型,如果你發(fā)現(xiàn)其中存在什么錯誤請務(wù)必賜教。一:select模型二:WSAAsyncSelect模型三:WSAEventSelect模型四:Overlapped I/O 事件通知模型五:Overlapped I/O 完成例程模型六:IOCP模型老陳有一個在外地工作的女兒,不能經(jīng)常回來,老陳和她通過信件聯(lián)系。他們的信會被郵遞員投遞到他們的...  閱讀全文
            posted @ 2010-08-03 15:50 c++ 學(xué)習(xí) 閱讀(353) | 評論 (0)編輯 收藏
             
            這種使用間接引用的方法是一個小技巧. 如果第二個變量更改了它的值, 那么第一個變量
            必須被適當(dāng)?shù)慕獬?就像上邊的例子一樣). 幸運的是, 在Bash版本2中引入
            的${!variable}形式使得使用間接引用更加直觀了.
            假設(shè)一個變量的值是第二個變量的名字. 那么我們?nèi)绾螐牡谝粋€變量中取得第二個變量的值呢? 比如,
            如果a=letter_of_alphabet并且letter_of_alphabet=z, 那么我們能夠通過引用變量a來獲得z么? 這確
            實是可以做到的, 它被稱為間接引用. 它使用eval var1=\$$var2這種不平常的形式.
            posted @ 2010-08-02 16:46 c++ 學(xué)習(xí) 閱讀(1357) | 評論 (0)編輯 收藏
             

            正則表達(dá)式就是由一系列特殊字符組成的字符串, 其中每個特殊字符都被稱為元字符, 這些元字符并不表示為它們字面上的含義, 而會被解釋為一些特定的含義. 具個例子, 比如引用符號, 可能就是表示某人的演講內(nèi)容, 同上, 也可能表示為我們下面將要講到的符號的元-含義. 正則表達(dá)式其實是由普通字符和元字符共同組成的集合, 這個集合用來匹配(或指定)模式.

            一個正則表達(dá)式會包含下列一項或多項:

            • 一個字符集. 這里所指的字符集只包含普通字符, 這些字符只表示它們的字面含義. 正則表達(dá)式的最簡單形式就是包含字符集, 而不包含元字符.

            • . 指定了正則表達(dá)式所要匹配的文本在文本行中所處的位置. 比如, ^, 和$就是錨.

            • 修飾符. 它們擴(kuò)大或縮小(修改)了正則表達(dá)式匹配文本的范圍. 修飾符包含星號, 括號, 和反斜杠.

            正則表達(dá)式最主要的目的就是用于(RE)文本搜索與字符串操作. (譯者注: 以下正則表達(dá)式也會被簡稱為RE.) RE能夠匹配單個字符或者一個字符集 -- 即, 一個字符串, 或者一個字符串的一部分.

            • 星號 -- * -- 用來匹配它前面字符的任意多次, 包括0次.

              "1133*"匹配11 + 一個或多個3 + 也允許后邊還有其他字符: 113, 1133, 111312, 等等.

            • 點 -- . -- 用于匹配任意一個字符, 除了換行符. [1]

              "13." 匹配13 + 至少一個任意字符(包括空格): 1133, 11333, 但不能匹配13 (因為缺少"."所能匹配的至少一個任意字符).

            • 脫字符號 -- ^ -- 匹配行首, 但是某些時候需要依賴上下文環(huán)境, 在RE中, 有時候也表示對一個字符集取反.

            • 美元符 -- $ -- 在RE中用來匹配行尾.

              "XXX$" 匹配行尾的XXX.

              "^$" 匹配空行.

            • 中括號 -- [...] -- 在RE中, 將匹配中括號字符集中的某一個字符.

              "[xyz]" 將會匹配字符x, y, 或z.

              "[c-n]" 匹配字符c到字符n之間的任意一個字符.

              "[B-Pk-y]" 匹配從BP, 或者從ky之間的任意一個字符.

              "[a-z0-9]" 匹配任意小寫字母或數(shù)字.

              "[^b-d]" 將會匹配范圍在bd之外的任意一個字符. 這就是使用^對字符集取反的一個實例. (就好像在某些情況下, !所表達(dá)的含義).

              將多個中括號字符集組合使用, 能夠匹配一般的單詞或數(shù)字. "[Yy][Ee][Ss]"能夠匹配yes, Yes, YES, yEs, 等等. "[0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9][0-9][0-9]" 可以匹配社保碼(Social Security number).

            • 反斜杠 -- \ -- 用來轉(zhuǎn)義某個特殊含義的字符, 這意味著, 這個特殊字符將會被解釋為字面含義.

              "\$"將會被解釋成字符"$", 而不是RE中匹配行尾的特殊字符. 相似的, "\\"將會被解釋為字符"\".

            • 轉(zhuǎn)義"尖括號" -- \<...\> -- 用于匹配單詞邊界.

              尖括號必須被轉(zhuǎn)義才含有特殊的含義, 否則它就表示尖括號的字面含義.

              "\<the\>" 完整匹配單詞"the", 不會匹配"them", "there", "other", 等等.

               

              bash$ cat textfile
              This is line 1, of which there is only one instance.
              This is the only instance of line 2.
              This is line 3, another line.
              This is line 4.



              bash$ grep 'the' textfile
              This is line 1, of which there is only one instance.
              This is the only instance of line 2.
              This is line 3, another line.



              bash$ grep '\<the\>' textfile
              This is the only instance of line 2.
            • 擴(kuò)展的正則表達(dá)式. 添加了一些額外的匹配字符到基本集合中. 用于egrep, awk, 和Perl.

            • 問號 -- ? -- 匹配它前面的字符, 但是只能匹配1次或0次. 通常用來匹配單個字符.

            • 加號 -- + -- 匹配它前面的字符, 能夠匹配一次或多次. 與前面講的*號作用類似, 但是不能匹配0個字符的情況.

                1 # GNU版本的sed和awk能夠使用"+",
              2 # 但是它需要被轉(zhuǎn)義一下.

              4 echo a111b | sed -ne '/a1\+b/p'
              5 echo a111b | grep 'a1\+b'
              6 echo a111b | gawk '/a1+b/'
              7 # 上邊3句的作用相同.

              9 # 感謝, S.C.
            • 轉(zhuǎn)義"大括號" -- \{ \} -- 在轉(zhuǎn)義后的大括號中加上一個數(shù)字, 這個數(shù)字就是它前面的RE所能匹配的次數(shù).

              大括號必須經(jīng)過轉(zhuǎn)義, 否則, 大括號僅僅表示字面含意. 這種用法并不是基本RE集合中的一部分, 僅僅是個技巧而以.

              "[0-9]\{5\}" 精確匹配5個數(shù)字 (所匹配的字符范圍是0到9).

              Note

              使用大括號形式的RE是不能夠在"經(jīng)典"(非POSIX兼容)的awk版本中正常運行的. 然而, gawk命令中有一個--re-interval選項, 使用這個選項就允許使用大括號形式的RE了(無需轉(zhuǎn)義).

               

              bash$ echo 2222 | gawk --re-interval '/2{3}/'
              2222

              Perl與某些版本的egrep不需要轉(zhuǎn)義大括號.

            • 圓括號 -- ( ) -- 括起一組正則表達(dá)式. 當(dāng)你想使用expr進(jìn)行子字符串提取(substring extraction)的時候, 圓括號就有用了. 如果和下面要講的"|"操作符結(jié)合使用, 也非常有用.

            • 豎線 -- | -- 就是RE中的"或"操作符, 使用它能夠匹配一組可選字符中的任意一個.

               

              bash$ egrep 're(a|e)d' misc.txt
              People who read seem to be better informed than those who do not.
              The clarinet produces sound by the vibration of its reed.

            Note

            與GNU工具一樣, 某些版本的sed, ed, 和ex一樣能夠支持?jǐn)U展正則表達(dá)式, 上邊這部分就描述了擴(kuò)展正則表達(dá)式.

            • POSIX字符類. [:class:]

              這是另外一種, 用于指定匹配字符范圍的方法.

            • [:alnum:] 匹配字母和數(shù)字. 等價于A-Za-z0-9.

            • [:alpha:] 匹配字母. 等價于A-Za-z.

            • [:blank:] 匹配一個空格或是一個制表符(tab).

            • [:cntrl:] 匹配控制字符.

            • [:digit:] 匹配(十進(jìn)制)數(shù)字. 等價于0-9.

            • [:graph:] (可打印的圖形字符). 匹配ASCII碼值范圍在33 - 126之間的字符. 與下面所提到的[:print:]類似, 但是不包括空格字符(空格字符的ASCII碼是32).

            • [:lower:] 匹配小寫字母. 等價于a-z.

            • [:print:] (可打印的圖形字符). 匹配ASCII碼值范圍在32 - 126之間的字符. 與上邊的[:graph:]類似, 但是包含空格.

            • [:space:] 匹配空白字符(空格和水平制表符).

            • [:upper:] 匹配大寫字母. 等價于A-Z.

            • [:xdigit:] 匹配16進(jìn)制數(shù)字. 等價于0-9A-Fa-f.

              Important

              POSIX字符類通常都要用引號或雙中括號([[ ]])引起來.

               

              bash$ grep [[:digit:]] test.file
              abc=723

              如果在一個受限的范圍內(nèi), 這些字符類甚至可以用在通配(globbing)中.

               

              bash$ ls -l ?[[:digit:]][[:digit:]]?
              -rw-rw-r-- 1 bozo bozo 0 Aug 21 14:47 a33b

              如果想了解POSIX字符類在腳本中的使用情況, 請參考例子 12-18例子 12-19.

            Sed, awk, 和Perl在腳本中一般都被用作過濾器, 這些過濾器將會以RE為參數(shù), 對文件或者I/O流進(jìn)行"過濾"或轉(zhuǎn)換. 請參考例子 A-12例子 A-17, 來詳細(xì)了解這種用法.

            對于RE這個復(fù)雜的主題, 標(biāo)準(zhǔn)的參考材料是Friedl的Mastering Regular Expressions. 由Dougherty和Robbins所編寫的Sed & Awk這本書, 也對RE進(jìn)行了清晰的論述. 如果想獲得這些書的更多信息, 請察看參考文獻(xiàn).

            注意事項

            [1]

            因為sed, awk, 和grep通常用于處理單行, 但是不能匹配一個換行符. 如果你想處理多行輸入的話, 那么你可以使用"點"來匹配換行符.

              1 #!/bin/bash

            3 sed -e 'N;s/.*/[&]/' << EOF # Here Document
            4 line1
            5 line2
            6 EOF
            7 # 輸出:
            8 # [line1
            9 # line2]
            10 
            11 
            12 
            13 echo
            14 
            15 awk '{ $0=$1 "\n" $2; if (/line.1/) {print}}' << EOF
            16 line 1
            17 line 2
            18 EOF
            19 # 輸出:
            20 # line
            21 # 1
            22 
            23 
            24 # 感謝, S.C.
            25
            26 exit 0

             

            posted @ 2010-06-30 22:15 c++ 學(xué)習(xí) 閱讀(7783) | 評論 (1)編輯 收藏
             

            1.       expr expression

            expr只能用于一元操作符,不支持二元操作符

            1 x=1

            2 x=$(expr $x + 1)

            $x + 1之間必須有空格

            2.       let expression

            let 的使用方式

            x=10

            let x=$x+1

            let x+=1

            let x*=10

            Let沒有返回值

            3.       使用$((expression ))((expression))形式

            ((expression))的使用方法

            x=10

            ((x+=10))

            (( expression)) 用法和let類似

            $(())的使用用法

            $((x=$x+10))

            echo $x

            y=$((x=$x-10))

            echo $y

            y=$(($x+1))

            echo $y

            echo $x

             

            4.       使用$[  ]形式

            例如:

            n=1

            :  $[ n=$n+1 ](:和$之間有空格)

            y=$[ n = $n + 1 ]

            echo $y

            y=$[ $n+1 ]

            echo $y

             

            5.       使用decalare 

            例子:

            decare –i num

            num=$num+1

            echo $num

            posted @ 2010-06-28 17:03 c++ 學(xué)習(xí) 閱讀(2746) | 評論 (0)編輯 收藏
             

            Bash Process Substitution

            In addition to the fairly common forms of input/output redirection the shell recognizes something called process substitution. Although not documented as a form of input/output redirection, its syntax and its effects are similar.

            The syntax for process substitution is:

              <(list)
            or
              >(list)
            
            where each list is a command or a pipeline of commands. The effect of process substitution is to make each list act like a file. This is done by giving the list a name in the file system and then substituting that name in the command line. The list is given a name either by connecting the list to named pipe or by using a file in /dev/fd (if supported by the O/S). By doing this, the command simply sees a file name and is unaware that its reading from or writing to a command pipeline.

             

            To substitute a command pipeline for an input file the syntax is:

              command ... <(list) ...
            
            To substitute a command pipeline for an output file the syntax is:
              command ... >(list) ...
            

             

            At first process substitution may seem rather pointless, for example you might imagine something simple like:

              uniq <(sort a)
            
            to sort a file and then find the unique lines in it, but this is more commonly (and more conveniently) written as:
              sort a | uniq
            
            The power of process substitution comes when you have multiple command pipelines that you want to connect to a single command.

             

            For example, given the two files:

              # cat a
              e
              d
              c
              b
              a
              # cat b
              g
              f
              e
              d
              c
              b
            
            To view the lines unique to each of these two unsorted files you might do something like this:
              # sort a | uniq >tmp1
              # sort b | uniq >tmp2
              # comm -3 tmp1 tmp2
              a
                    f
                    g
              # rm tmp1 tmp2
            
            With process substitution we can do all this with one line:
              # comm -3 <(sort a | uniq) <(sort b | uniq)
              a
                    f
                    g
            

             

            Depending on your shell settings you may get an error message similar to:

              syntax error near unexpected token `('
            
            when you try to use process substitution, particularly if you try to use it within a shell script. Process substitution is not a POSIX compliant feature and so it may have to be enabled via:
              set +o posix
            
            Be careful not to try something like:
              if [[ $use_process_substitution -eq 1 ]]; then
                set +o posix
                comm -3 <(sort a | uniq) <(sort b | uniq)
              fi
            
            The command set +o posix enables not only the execution of process substitution but the recognition of the syntax. So, in the example above the shell tries to parse the process substitution syntax before the "set" command is executed and therefore still sees the process substitution syntax as illegal.

             

            Of course, note that all shells may not support process substitution, these examples will work with bash.


            進(jìn)程替換與命令替換很相似. 命令替換把一個命令的結(jié)果賦值給一個變量, 比如dir_contents=`ls -

            al`或xref=$( grep word datafile). 進(jìn)程替換把一個進(jìn)程的輸出提供給另一個進(jìn)程(換句話說, 它把

            一個命令的結(jié)果發(fā)給了另一個命令).

            命令替換的模版

            用圓括號擴(kuò)起來的命令

            >(command)

            <(command)

            啟動進(jìn)程替換. 它使用/dev/fd/<n>文件將圓括號中的進(jìn)程處理結(jié)果發(fā)送給另一個進(jìn)程. [1] (譯

            者注: 實際上現(xiàn)代的UNIX類操作系統(tǒng)提供的/dev/fd/n文件是與文件描述符相關(guān)的, 整數(shù)n指的就

            是進(jìn)程運行時對應(yīng)數(shù)字的文件描述符)

            在"<"或">"與圓括號之間是沒有空格的. 如果加了空格, 會產(chǎn)生錯誤.

            bash$ echo >(true)

            /dev/fd/63

            bash$ echo <(true)

            /dev/fd/63

            Bash在兩個文件描述符之間創(chuàng)建了一個管道, --fIn和fOut--. true命令的stdin被連接到fOut

            (dup2(fOut, 0)), 然后Bash把/dev/fd/fIn作為參數(shù)傳給echo. 如果系統(tǒng)缺乏/dev/fd/<n>文件, Bash會

            使用臨時文件. (感謝, S.C.)

            進(jìn)程替換可以比較兩個不同命令的輸出, 甚至能夠比較同一個命令不同選項情況下的輸出.

            bash$ comm <(ls -l) <(ls -al)

            total 12

            -rw-rw-r-- 1 bozo bozo 78 Mar 10 12:58 File0

            -rw-rw-r-- 1 bozo bozo 42 Mar 10 12:58 File2

            -rw-rw-r-- 1 bozo bozo 103 Mar 10 12:58 t2.sh

            total 20

            drwxrwxrwx 2 bozo bozo 4096 Mar 10 18:10 .

            drwx------ 72 bozo bozo 4096 Mar 10 17:58 ..

            -rw-rw-r-- 1 bozo bozo 78 Mar 10 12:58 File0

            -rw-rw-r-- 1 bozo bozo 42 Mar 10 12:58 File2

            -rw-rw-r-- 1 bozo bozo 103 Mar 10 12:58 t2.sh

            使用進(jìn)程替換來比較兩個不同目錄的內(nèi)容(可以查看哪些文件名相同, 哪些文件名不同):

            1 diff <(ls $first_directory) <(ls $second_directory)

            一些進(jìn)程替換的其他用法與技巧:

            1 cat <(ls -l)

            2 # 等價于 ls -l | cat

            3

            4 sort -k 9 <(ls -l /bin) <(ls -l /usr/bin) <(ls -l /usr/X11R6/bin)

            5 # 列出系統(tǒng)3個主要'bin'目錄中的所有文件, 并且按文件名進(jìn)行排序.

            6 # 注意是3個(查一下, 上面就3個圓括號)明顯不同的命令輸出傳遞給'sort'.

            7

            8

            9 diff <(command1) <(command2) # 給出兩個命令輸出的不同之處.

            10

            11 tar cf >(bzip2 -c > file.tar.bz2) $directory_name

            12 # 調(diào)用"tar cf /dev/fd/?? $directory_name", 和"bzip2 -c > file.tar.bz2".

            13 #

            14 # 因為/dev/fd/<n>的系統(tǒng)屬性,

            15 # 所以兩個命令之間的管道不必被命名.

            16 #

            17 # 這種效果可以被模擬出來.

            18 #

            19 bzip2 -c < pipe > file.tar.bz2&

            20 tar cf pipe $directory_name

            21 rm pipe

            22 # 或

            23 exec 3>&1

            24 tar cf /dev/fd/4 $directory_name 4>&1 >&3 3>&- | bzip2 -c > file.tar.bz2 3>&-

            25 exec 3>&-

            26

            27

            28 # 感謝, Stephane Chazelas

            一個讀者給我發(fā)了一個有趣的例子, 是關(guān)于進(jìn)程替換的, 如下.

            1 # 摘自SuSE發(fā)行版中的代碼片斷:

            2

            3 while read des what mask iface; do

            4 # 這里省略了一些命令...

            5 done < <(route -n)

            6

            7

            8 # 為了測試它, 我們讓它做點事.

            9 while read des what mask iface; do

            10 echo $des $what $mask $iface

            11 done < <(route -n)

            12

            13 # 輸出:

            14 # Kernel IP routing table

            15 # Destination Gateway Genmask Flags Metric Ref Use Iface

            16 # 127.0.0.0 0.0.0.0 255.0.0.0 U 0 0 0 lo

            17

            18

            19

            20 # 就像Stephane Chazelas所給出的那樣, 一個更容易理解的等價代碼是:

            21 route -n |

            22 while read des what mask iface; do # 管道的輸出被賦值給了變量.

            23 echo $des $what $mask $iface

            24 done # 這將產(chǎn)生出與上邊相同的輸出.

            25 # 然而, Ulrich Gayer指出 . . .

            26 #+ 這個簡單的等價版本在while循環(huán)中使用了一個子shell,

            27 #+ 因此當(dāng)管道結(jié)束后, 變量就消失了.

            28

            29

            30

            31 # 更進(jìn)一步, Filip Moritz解釋了上面兩個例子之間存在一個細(xì)微的不同之處,

            32 #+ 如下所示.

            33

            34 (

            35 route -n | while read x; do ((y++)); done

            36 echo $y # $y 仍然沒有被聲明或設(shè)置

            37

            38 while read x; do ((y++)); done < <(route -n)

            39 echo $y # $y 的值為route -n的輸出行數(shù).

            40 )

            41

            42 # 一般來說, (譯者注: 原書作者在這里并未加注釋符號"#", 應(yīng)該是筆誤)

            43 (

            44 : | x=x

            45 # 看上去是啟動了一個子shell

            46 : | ( x=x )

            47 # 但

            48 x=x < <(:)

            49 # 其實不是

            50 )

            51

            52 # 當(dāng)你要解析csv或類似東西的時侯, 這非常有用.

            53 # 事實上, 這就是SuSE的這個代碼片斷所要實現(xiàn)的功能.

            注意事項

            [1] 這與命名管道(臨時文件)具有相同的作用, 并且, 事實上, 命名管道也被同時使用在進(jìn)程

            替換中.

            posted @ 2010-06-27 17:15 c++ 學(xué)習(xí) 閱讀(1314) | 評論 (0)編輯 收藏
             
            幾個知識點
            1.Bash在實現(xiàn)pipeline(管道|)時會發(fā)起兩個subshell(子shell)來運行|兩邊的命令,對于系統(tǒng)來說就是發(fā)起兩個childprocess(子進(jìn)程)

            2.fork是產(chǎn)生process的唯一途徑,exec*是執(zhí)行程序的唯一途徑

            3.子進(jìn)程會完全復(fù)制父進(jìn)程,除了$PID與$PPID

            4.fork子進(jìn)程時繼承父進(jìn)程的進(jìn)程名,在exec*執(zhí)行命令時才由exec*替換為子進(jìn)程對應(yīng)的命令,同一進(jìn)程的命令名可以由一個個exec*任意多次的改變



            [注]對于linux平臺,JB上就是這樣的,其它平臺不好發(fā)表意見,當(dāng)然對于2中的兩個唯一有一個例外,就是在kenerl  init的初期;
            暫時找不到相關(guān)參考,也沒有功力讀源碼,所以此論是道聽途說級別,錯誤之處請指出改正,如果沒有改正的價值可一笑而過

            我覺得要先弄清楚sub shell的定義。

            查了些資料,發(fā)現(xiàn)subshell的定義有些混亂。
            bashMan:



            QUOTE:
            Each command in a pipeline is executed as a separate process (i.e., in a subshell).  




            QUOTE:
            When a simple command other than a builtin or shell function is to be executed, it is invoked in a
            separate execution environment that consists of the following. Unless otherwise noted, the values are
            inherited from the shell.



            這個separate execution是subshell嗎?
            A. 在當(dāng)前shell執(zhí)行外部命令,如shell> date, 是fork+exec, 算不是subshell? 
            B. ()是fork一個child shell,該child再fork+exec來執(zhí)行命令。這個subshell和A中的"subshell"顯然是不同的。

            UNIX: The Textbook, by Syed Mansoor Sarvar, Robert Koretsky and Syed Aqeel Sarvar中提到:


            QUOTE:
            A child shell is also called subshell


            問題是fork+exec是fork一個child shell,然后在該child shell中exec.
            而執(zhí)行腳本(shell>scriptname)時,是fort一個child shell A,該child shell A再fork一個child shell B, 在B中再exec.

            那么child shell是指哪種情況?



            UNIX at Fermilab中的Important UNIX Concepts:



            QUOTE:
            When you give the shell a command associated with a compiled executable or shell script, the shell
            creates, or forks, a new process called a subshell.


            外部命令也在subshell中執(zhí)行。



            QUOTE:
            To execute most built-in commands, the shell forks a subshell which executes the command directly (no
            exec system call). For the built-in commands cd, set, alias and source, the current shell executes the
            command; no subshell is forked.



            shell> built-inCommands這樣執(zhí)行時,大部分內(nèi)部命令也是在subshell中執(zhí)行。
            可見,UNIX at Fermilab認(rèn)為fork 一個child shell就是subshell, 不管是fork-fork+exec, 還是 fork+exec。

            我剛才又去翻了下ABS,定義與我的理解不一樣


            QUOTE:
            A subshell is a separate instance of the command processor -- the shell that gives you the prompt at the
            console or in an xterm window. Just as your commands are interpreted at the command line prompt, similarly
            does a script batch-process a list of commands. Each shell script running is, in effect, a subprocess (child
            process) of the parent shell.
            ./script也是external_cmd的一種啊,都是fork-exec,至于external-cmd再作什么動作完全是external-cmd的

            posted @ 2010-06-25 16:35 c++ 學(xué)習(xí) 閱讀(826) | 評論 (0)編輯 收藏
             
            good job!
            總算有人看得懂了。

            不過,要細(xì)說的話,要扯上 shell 在 interpret 一個 command line 時的 priority 。
            基本上,其順序如下:
            1,將 line 拆成 words (IFS很重要)
            2,括展 alias
            3,擴(kuò)展 { }
            4,擴(kuò)展 ~
            5,擴(kuò)展 $variable, $(command), `command`
            6,重組再拆成 words
            7,括展 wildcards
            8,處理 I/O redirection
            9,載入命令運行
            如果大家有O'Reilly英文版的 Learning the Bash(2nd)的話,請多端詳p178的圖(細(xì)節(jié)略異)

            回到LZ的問題,看上面 5 跟 6 的順序然後才是 9 。
            也就是在 6 重組命令時 $A 已經(jīng)完成替換,當(dāng)時的 environment 是沒賦值,
            因此重組後就是 A=B echo
            然後在第 9 的步驟運行命令時, A=B 是給 echo 命令的 local environment,
            不管是否 built-in command,都不影響當(dāng)前的 shell (不同的 shell 在實作上或有差異)
            所以第二行的 echo $A 也是得到?jīng)]賦值
            我通過eval說明賦值是成功的,而不是65樓所說的賦值不成功。

            第一步使用 metacharacter,與IFS沒有關(guān)系

            The following is a brief description of the shell's operation when it
            reads and executes a command.  Basically, the shell does the following:

              1. Reads its input from a file (*note Shell Scripts::), from a string
                 supplied as an argument to the `-c' invocation option (*note
                 Invoking Bash::), or from the user's terminal.

              2. Breaks the input into words and operators, obeying the quoting
                 rules described in *Note Quoting::.  These tokens are separated by
                 `metacharacters'.  Alias expansion is performed by this step
                 (*note Aliases::).
            QUOTE:
            `IFS'
                 A list of characters that separate fields; used when the shell
                 splits words as part of expansion.
            `metacharacter'
                 A character that, when unquoted, separates words.  A metacharacter
                 is a `blank' or one of the following characters: `|', `&', `;',
                 `(', `)', `<', or `>'.
            8.05 命令行的評價(evaluation)
            下面是C shell 解釋命令行的順序:
            1. 歷史替換
            2. 分裂詞(包括特殊字符)
            3. 更新歷史表
            4. 解釋單引號(') 和 雙引號(")
            5. 別名替換
            6. 輸入和輸出的重定向(如 >  < 和 |)
            7. 變量替換
            8. 命令替換
            9. 文件名擴(kuò)展
            (Bourne shell 的解釋順序本質(zhì)上是一樣的,除了它不執(zhí)行歷史替換和別名替換之外)

            所以
            A=B  echo    $A

            的執(zhí)行過程應(yīng)該是這樣的:
            1. 沒有歷史操作符, 因此不進(jìn)行歷史替換(Bourne shell 不執(zhí)行這一步)
            2. 分裂詞,每碰到未加引號的空白字符就會產(chǎn)生一個新“詞”。這些詞是 A=B、echo、$A。
            3. shell 將命令行放到歷史列表中。(Bourne shell 不執(zhí)行這一步)
            4. 沒有引號需要解釋
            5. 沒有別名需要替換
            6. 沒有輸入或輸出重定向需要處理
            7. shell注意到變量$A,并把它替換成空
            8. shell尋找左單引號,執(zhí)行左單引號中的任何命令,并且將命令的輸出插入到命令行中。在本例中,沒有這方面的事需要做。(如果左單引號內(nèi)有通配符或者變量,那么在shell運行左單引號中的命令之前它們是不會被解釋的)
            9. shell尋找通配符。本例中沒有,不需要處理
            10. shell 執(zhí)行 A=B, 執(zhí)行 echo 。
            8.05 命令行的評價(evaluation)
            下面是C shell 解釋命令行的順序:
            1. 歷史替換
            2. 分裂詞(包括特殊字符)
            3. 更新歷史表
            4. 解釋單引號(') 和 雙引號(")
            5. 別名替換
            6. 輸入和輸出的重定向(如 >  < 和 |)
            7. 變量替換
            8. 命令替換
            9. 文件名擴(kuò)展
            (Bourne shell 的解釋順序本質(zhì)上是一樣的,除了它不執(zhí)行歷史替換和別名替換之外)

            所以
            A=B  echo    $A

            的執(zhí)行過程應(yīng)該是這樣的:
            1. 沒有歷史操作符, 因此不進(jìn)行歷史替換(Bourne shell 不執(zhí)行這一步)
            2. 分裂詞,每碰到未加引號的空白字符就會產(chǎn)生一個新“詞”。這些詞是 A=B、echo、$A。
            3. shell 將命令行放到歷史列表中。(Bourne shell 不執(zhí)行這一步)
            4. 沒有引號需要解釋
            5. 沒有別名需要替換
            6. 沒有輸入或輸出重定向需要處理
            7. shell注意到變量$A,并把它替換成空
            8. shell尋找左單引號,執(zhí)行左單引號中的任何命令,并且將命令的輸出插入到命令行中。在本例中,沒有這方面的事需要做。(如果左單引號內(nèi)有通配符或者變量,那么在shell運行左單引號中的命令之前它們是不會被解釋的)
            9. shell尋找通配符。本例中沒有,不需要處理
            10. shell 執(zhí)行 A=B, 執(zhí)行 echo 。

            posted @ 2010-06-25 16:29 c++ 學(xué)習(xí) 閱讀(532) | 評論 (0)編輯 收藏
            僅列出標(biāo)題
            共3頁: 1 2 3 
             
            久久99久久成人免费播放| 久久笫一福利免费导航| 亚洲中文字幕无码久久精品1 | 人人狠狠综合久久亚洲婷婷| 国产成人精品白浆久久69| 99久久国产综合精品成人影院| 久久亚洲2019中文字幕| 亚洲中文精品久久久久久不卡| 国产精品久久久久久久久| 香蕉aa三级久久毛片| 久久99精品久久久久子伦| 欧美国产成人久久精品| 99久久婷婷国产综合亚洲| 久久婷婷色综合一区二区| 东京热TOKYO综合久久精品 | 国产人久久人人人人爽| 久久久综合香蕉尹人综合网| 久久久精品人妻一区二区三区四 | 97久久精品无码一区二区| 久久国产精品免费一区二区三区| 久久久久av无码免费网| 国产真实乱对白精彩久久| 久久婷婷五月综合国产尤物app | 狠狠色婷婷久久一区二区| 久久中文字幕一区二区| 久久亚洲美女精品国产精品| 香蕉aa三级久久毛片| 久久久久亚洲爆乳少妇无 | 伊人久久无码中文字幕| 亚洲一区精品伊人久久伊人| 国产精品99久久精品爆乳| 久久九九亚洲精品| 久久精品午夜一区二区福利 | 久久人人爽人人爽人人AV东京热| 亚洲欧美成人久久综合中文网 | 看久久久久久a级毛片| 久久SE精品一区二区| 久久精品国产亚洲AV久| 国产精品久久久久久久久久影院| 欧美伊人久久大香线蕉综合69| 久久国产三级无码一区二区|