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

            置頂隨筆

            給學習c++的新手,這些書籍都是很經典的。經典中的經典
            深度探索C++對象模型
            英文版:http://www.nengxia.com/soft.asp?id=5
            中文版:http://www.nengxia.com/soft.asp?id=19

            Modern C++ Design
            http://www.nengxia.com/soft.asp?id=7

            c++編程思想
            第一卷:
            中文版:http://www.nengxia.com/soft.asp?id=1039
            英文版: Prentice Hall Bruce Eckel Thinking In C++, Second EditionVolume.1
            第二卷:
            中文版:http://www.nengxia.com/soft.asp?id=1040
            英文版:http://www.nengxia.com/soft.asp?id=1041


            c++ Programming language
            中文版:http://www.nengxia.com/soft.asp?id=1038
            英文版:http://www.nengxia.com/soft.asp?id=368

            C++ Primer
            第三版中文版:http://www.nengxia.com/soft.asp?id=6
            第四版
            英文版:http://www.nengxia.com/soft.asp?id=117
            中文版:http://www.nengxia.com/soft.asp?id=635
            c++ primer 題解
            http://www.nengxia.com/soft.asp?id=17


            C++ Primer plus 第4版中文:
            中文版:http://www.nengxia.com/soft.asp?id=987
            英文版:
            Third.Editionhttp://www.nengxia.com/soft.asp?id=1037
            Special.Edition:http://www.nengxia.com/soft.asp?id=369


            Effective C++
            中文版:http://www.nengxia.com/soft.asp?id=9
            英文版:http://www.nengxia.com/soft.asp?id=1033

            More Effective C++
            中文版:http://www.nengxia.com/soft.asp?id=8

            STL源碼剖析
            http://www.nengxia.com/soft.asp?id=11


            c++ template
            英文版:
            http://www.nengxia.com/soft.asp?id=1034
            簡體中文版:
            http://www.nengxia.com/soft.asp?id=15
            繁體中文版:
            http://www.nengxia.com/soft.asp?id=16

            Effective STL
            http://www.nengxia.com/soft.asp?id=54

            c++ 標準庫
            http://www.nengxia.com/soft.asp?id=47

            Exception c++
            中文版:http://www.nengxia.com/soft.asp?id=1035
            英文版:http://www.nengxia.com/soft.asp?id=18

            More Excetption c++
            英文版:http://www.nengxia.com/soft.asp?id=20

            C++ Coding Standards:
            http://www.nengxia.com/soft.asp?id=114

            STL輕松入門
            http://www.nengxia.com/soft.asp?id=162

            c/c++標準函數庫 中文版
            http://www.nengxia.com/soft.asp?id=641

            the design and evolution of c++
            英文版:http://nengxia.com/soft.asp?id=1042

            高質量C++編程指南
            http://www.nengxia.com/soft.asp?id=1043



            posted @ 2007-10-24 16:45 c++ 學習 閱讀(11460) | 評論 (8)編輯 收藏

            2010年8月21日

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

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

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

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


            1安裝cscope

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

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


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

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

                cscope -Rbkq

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

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


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

            #!/bin/sh

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


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

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



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

            在vim的網站上,有很多和cscope相關的插件,可以去找一下你有沒有所感興趣的。搜索結果在這里:
            點這里


            為了方便地使用cscope,我們還需要下載cscope的鍵盤映射設置,
            這樣就可以在gvim中簡單地通過快捷鍵來使用 cscope,而不必敲復雜的命令了。鍵盤映射可以從
            這里下載: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 中。

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

            瀏覽源代碼 †
            使用 gvim 打開你的源代碼目錄中任意一個C程序文件。然后在gvim中執行如下命令:
            :cscope add D:\src\myproject\cscope.out
            由于在 gvim 中可以使用命令縮寫,因此上面的命令可以寫成:
            :cs a D:\src\myproject\cscope.out
            這樣就打開了剛剛建立的符號數據庫。通過下面的命令可以檢查數據庫連接的存在。
            :cscope show
            該命令可以縮寫為
            :cs s
            現在將光標移動到源代碼中的某個函數名上,依次按下一下組合鍵:
            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 這個標識符的位置。此時輸入 4,回車,
            即可跳轉到 main.c 的 385 行調用 display() 函數的地方進行瀏覽。瀏覽結束后按 或者
            可以回到跳轉前的位置。
            然后將光標移動到源代碼某個函數名上,迅速地依次安下面的組合鍵:
            s
            其中 按 Ctrl-2 即可輸入。同樣,屏幕上出現了一排結果,選擇之后你會發現,
            跳轉到的文件將在水平方向的新窗口中打開。
            然后將光標移動到源代碼某個函數名上,迅速地依次安下面的組合鍵:
            s
            選擇之后你會發現,跳轉到的文件將在垂直方向的新窗口中打開。
            以上我們簡單介紹了cscope的使用方法,其中我們只用到了一個 s 命令,即跟在 和 后面的 s 鍵。
            同樣,我們可以使用以下的功能鍵實現不同的跳轉功能。
            c: 查找該函數被調用的位置 
            d: 查找該函數調用了哪些函數
            e: 查找指定的正規表達式
            f: 查找指定的文件
            g: 查找指定標識符的定義位置
            i: 查找該文件在哪些地方被包含
            s: 查找指定標識符的使用位置
            t: 查找指定的文本字符串

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

            版權 †
            Cscope雖然不是GPL版權,但是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中關于 cscope 的幫助,使用 :help cscope 命令查看
            posted @ 2010-08-21 22:28 c++ 學習 閱讀(1172) | 評論 (0)編輯 收藏

            2010年8月3日

            2.用完成例程方式實現的重疊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);
            }
            }

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

            五.完成端口模型
            “完成端口”模型是迄今為止最為復雜的一種I/O模型。然而,假若一個應用程序同時需要管理為數眾多的套接字,那么采用這種模型,往往可以達到最佳的系統性能!但不幸的是,該模型只適用于Windows NT和Windows 2000操作系統。因其設計的復雜性,只有在你的應用程序需要同時管理數百乃至上千個套接字的時候,而且希望隨著系統內安裝的CPU數量的增多,應用程序的性能也可以線性提升,才應考慮采用“完成端口”模型。要記住的一個基本準則是,假如要為Windows NT或Windows 2000開發高性能的服務器應用,同時希望為大量套接字I/O請求提供服務(Web服務器便是這方面的典型例子),那么I/O完成端口模型便是最佳選擇!(節選自《Windows網絡編程》第八章)
            完成端口模型是我最喜愛的一種模型。雖然其實現比較復雜(其實我覺得它的實現比用事件通知實現的重疊I/O簡單多了),但其效率是驚人的。我在T公司的時候曾經幫同事寫過一個郵件服務器的性能測試程序,用的就是完成端口模型。結果表明,完成端口模型在多連接(成千上萬)的情況下,僅僅依靠一兩個輔助線程,就可以達到非常高的吞吐量。下面我還是從代碼說起:
            #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.創建完成端口對象
            2.創建工作者線程(這里工作者線程的數量是按照CPU的個數來決定的,這樣可以達到最佳性能)
            3.創建監聽套接字,綁定,監聽,然后程序進入循環
            4.在循環中,我做了以下幾件事情:
            (1).接受一個客戶端連接
            (2).將該客戶端套接字與完成端口綁定到一起(還是調用CreateIoCompletionPort,但這次的作用不同),注意,按道理來講,此時傳遞給CreateIoCompletionPort的第三個參數應該是一個完成鍵,一般來講,程序都是傳遞一個單句柄數據結構的地址,該單句柄數據包含了和該客戶端連接有關的信息,由于我們只關心套接字句柄,所以直接將套接字句柄作為完成鍵傳遞;
            (3).觸發一個WSARecv異步調用,這次又用到了“尾隨數據”,使接收數據所用的緩沖區緊跟在WSAOVERLAPPED對象之后,此外,還有操作類型等重要信息。

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

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

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

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

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

            posted @ 2010-08-03 17:27 c++ 學習 閱讀(439) | 評論 (0)編輯 收藏
             
                 摘要: 如果你想在Windows平臺上構建服務器應用,那么I/O模型是你必須考慮的。Windows操作系統提供了選擇(Select)、異步選擇(WSAAsyncSelect)、事件選擇(WSAEventSelect)、重疊I/O(Overlapped I/O)和完成端口(Completion Port)共五種I/O模型。每一種模型均適用于一種特定的應用場景。程序員應該對自己的應用需求非常明確,而且綜合考慮...  閱讀全文
            posted @ 2010-08-03 17:26 c++ 學習 閱讀(456) | 評論 (0)編輯 收藏
             
                 摘要: 本文簡單介紹了當前Windows支持的各種Socket I/O模型,如果你發現其中存在什么錯誤請務必賜教。一:select模型二:WSAAsyncSelect模型三:WSAEventSelect模型四:Overlapped I/O 事件通知模型五:Overlapped I/O 完成例程模型六:IOCP模型老陳有一個在外地工作的女兒,不能經常回來,老陳和她通過信件聯系。他們的信會被郵遞員投遞到他們的...  閱讀全文
            posted @ 2010-08-03 15:50 c++ 學習 閱讀(352) | 評論 (0)編輯 收藏

            2010年8月2日

            這種使用間接引用的方法是一個小技巧. 如果第二個變量更改了它的值, 那么第一個變量
            必須被適當的解除引用(就像上邊的例子一樣). 幸運的是, 在Bash版本2中引入
            的${!variable}形式使得使用間接引用更加直觀了.
            假設一個變量的值是第二個變量的名字. 那么我們如何從第一個變量中取得第二個變量的值呢? 比如,
            如果a=letter_of_alphabet并且letter_of_alphabet=z, 那么我們能夠通過引用變量a來獲得z么? 這確
            實是可以做到的, 它被稱為間接引用. 它使用eval var1=\$$var2這種不平常的形式.
            posted @ 2010-08-02 16:46 c++ 學習 閱讀(1356) | 評論 (0)編輯 收藏

            2010年6月30日

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

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

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

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

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

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

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

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

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

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

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

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

              "XXX$" 匹配行尾的XXX.

              "^$" 匹配空行.

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

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

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

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

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

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

              將多個中括號字符集組合使用, 能夠匹配一般的單詞或數字. "[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).

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

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

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

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

              "\<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.
            • 擴展的正則表達式. 添加了一些額外的匹配字符到基本集合中. 用于egrep, awk, 和Perl.

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

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

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

              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.
            • 轉義"大括號" -- \{ \} -- 在轉義后的大括號中加上一個數字, 這個數字就是它前面的RE所能匹配的次數.

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

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

              Note

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

               

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

              Perl與某些版本的egrep不需要轉義大括號.

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

            • 豎線 -- | -- 就是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一樣能夠支持擴展正則表達式, 上邊這部分就描述了擴展正則表達式.

            • POSIX字符類. [:class:]

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

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

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

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

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

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

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

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

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

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

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

            • [:xdigit:] 匹配16進制數字. 等價于0-9A-Fa-f.

              Important

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

               

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

              如果在一個受限的范圍內, 這些字符類甚至可以用在通配(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為參數, 對文件或者I/O流進行"過濾"或轉換. 請參考例子 A-12例子 A-17, 來詳細了解這種用法.

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

            注意事項

            [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++ 學習 閱讀(7783) | 評論 (1)編輯 收藏

            2010年6月28日

            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++ 學習 閱讀(2745) | 評論 (0)編輯 收藏

            2010年6月27日

            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.


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

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

            一個命令的結果發給了另一個命令).

            命令替換的模版

            用圓括號擴起來的命令

            >(command)

            <(command)

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

            者注: 實際上現代的UNIX類操作系統提供的/dev/fd/n文件是與文件描述符相關的, 整數n指的就

            是進程運行時對應數字的文件描述符)

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

            bash$ echo >(true)

            /dev/fd/63

            bash$ echo <(true)

            /dev/fd/63

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

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

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

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

            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

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

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

            一些進程替換的其他用法與技巧:

            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 # 列出系統3個主要'bin'目錄中的所有文件, 并且按文件名進行排序.

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

            7

            8

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

            10

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

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

            13 #

            14 # 因為/dev/fd/<n>的系統屬性,

            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

            一個讀者給我發了一個有趣的例子, 是關于進程替換的, 如下.

            1 # 摘自SuSE發行版中的代碼片斷:

            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 # 這將產生出與上邊相同的輸出.

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

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

            27 #+ 因此當管道結束后, 變量就消失了.

            28

            29

            30

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

            32 #+ 如下所示.

            33

            34 (

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

            36 echo $y # $y 仍然沒有被聲明或設置

            37

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

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

            40 )

            41

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

            43 (

            44 : | x=x

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

            46 : | ( x=x )

            47 # 但

            48 x=x < <(:)

            49 # 其實不是

            50 )

            51

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

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

            注意事項

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

            替換中.

            posted @ 2010-06-27 17:15 c++ 學習 閱讀(1313) | 評論 (0)編輯 收藏

            2010年6月25日

            幾個知識點
            1.Bash在實現pipeline(管道|)時會發起兩個subshell(子shell)來運行|兩邊的命令,對于系統來說就是發起兩個childprocess(子進程)

            2.fork是產生process的唯一途徑,exec*是執行程序的唯一途徑

            3.子進程會完全復制父進程,除了$PID與$PPID

            4.fork子進程時繼承父進程的進程名,在exec*執行命令時才由exec*替換為子進程對應的命令,同一進程的命令名可以由一個個exec*任意多次的改變



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

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

            查了些資料,發現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. 在當前shell執行外部命令,如shell> date, 是fork+exec, 算不是subshell? 
            B. ()是fork一個child shell,該child再fork+exec來執行命令。這個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.
            而執行腳本(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中執行。



            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這樣執行時,大部分內部命令也是在subshell中執行。
            可見,UNIX at Fermilab認為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++ 學習 閱讀(825) | 評論 (0)編輯 收藏
             
            good job!
            總算有人看得懂了。

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

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

            第一步使用 metacharacter,與IFS沒有關系

            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. 文件名擴展
            (Bourne shell 的解釋順序本質上是一樣的,除了它不執行歷史替換和別名替換之外)

            所以
            A=B  echo    $A

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

            所以
            A=B  echo    $A

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

            posted @ 2010-06-25 16:29 c++ 學習 閱讀(532) | 評論 (0)編輯 收藏
            僅列出標題  下一頁
             
            精品久久久久久综合日本| 色88久久久久高潮综合影院| 精品久久人人爽天天玩人人妻| 亚洲精品国产自在久久| 久久国产色AV免费看| 欧美激情精品久久久久久| 久久福利青草精品资源站免费| 国产高清国内精品福利99久久| 青青热久久综合网伊人| 91亚洲国产成人久久精品网址| 亚洲女久久久噜噜噜熟女| 亚洲日本久久久午夜精品| 国内精品伊人久久久久777| 久久最新免费视频| 看久久久久久a级毛片| 人妻丰满?V无码久久不卡| 99久久久精品| 2020国产成人久久精品| 亚洲成色www久久网站夜月| 日本欧美久久久久免费播放网| 91精品久久久久久无码| 日韩人妻无码一区二区三区久久| 久久精品女人天堂AV麻| 久久精品国产99久久久香蕉| 久久久久久久久久久久中文字幕 | 精品人妻伦九区久久AAA片69| 亚洲精品久久久www| 香蕉99久久国产综合精品宅男自| 久久国产精品偷99| 色妞色综合久久夜夜 | 国产情侣久久久久aⅴ免费| 精品国产一区二区三区久久| 成人妇女免费播放久久久| 热re99久久6国产精品免费| 亚洲精品无码专区久久久| 久久婷婷五月综合色奶水99啪| 国产成人精品久久二区二区| 国内精品久久久久久不卡影院| 亚洲AV日韩精品久久久久久久| 亚洲国产精品无码久久SM| 麻豆精品久久精品色综合|