青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

置頂隨筆

給學(xué)習(xí)c++的新手,這些書籍都是很經(jīng)典的。經(jīng)典中的經(jīng)典
深度探索C++對(duì)象模型
英文版: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
簡(jiǎn)體中文版:
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++ 標(biāo)準(zhǔn)庫(kù)
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++標(biāo)準(zhǔn)函數(shù)庫(kù) 中文版
http://www.nengxia.com/soft.asp?id=641

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

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



posted @ 2007-10-24 16:45 c++ 學(xué)習(xí) 閱讀(11485) | 評(píng)論 (8)編輯 收藏

2010年8月21日

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

而Source insight的價(jià)格即使是面對(duì)Windows Vista也不逞多。嘿嘿。東西是好東西。

個(gè)人認(rèn)為它也對(duì)得起這個(gè)價(jià)格。可惜沒(méi)米。用不起呀。

咋辦呢。用vim,cscope打造一個(gè)免費(fèi)的吧。


1安裝cscope

cscope的編譯和安裝沒(méi)有特別之處,./configure - make - make install即可。
安轉(zhuǎn)完畢后先閱讀說(shuō)明: 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è)置,不過(guò)首先vim編譯時(shí)必須加入了cscope的支持

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


嗯,我用的這個(gè)版本的vim是支持cscope的。

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

    cscope -Rbkq

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

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


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

#!/bin/sh

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


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

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



cscope的主頁(yè)在:http://cscope.sourceforge.net/

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


為了方便地使用cscope,我們還需要下載cscope的鍵盤映射設(shè)置,
這樣就可以在gvim中簡(jiǎn)單地通過(guò)快捷鍵來(lái)使用 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 中。

建立符號(hào)數(shù)據(jù)庫(kù) †
我們假設(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 命令實(shí)現(xiàn)同樣的功能:
find $(pwd) -name "*.[ch]"
然后執(zhí)行以下命令:
cscope -b
執(zhí)行結(jié)束后你可以在當(dāng)前目錄下發(fā)現(xiàn) cscope.out 文件,這就是 cscope 建立的符號(hào)數(shù)據(jù)庫(kù)。
上面這個(gè)命令中,-b參數(shù)使得cscope不啟動(dòng)自帶的用戶界面,而僅僅建立符號(hào)數(shù)據(jù)庫(kù)。

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

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

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

參考 †
Cscope官方主頁(yè), 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í) 閱讀(1184) | 評(píng)論 (0)編輯 收藏

2010年8月3日

2.用完成例程方式實(shí)現(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);
}
}

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

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


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

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

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

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

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

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

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

2010年8月2日

這種使用間接引用的方法是一個(gè)小技巧. 如果第二個(gè)變量更改了它的值, 那么第一個(gè)變量
必須被適當(dāng)?shù)慕獬?就像上邊的例子一樣). 幸運(yùn)的是, 在Bash版本2中引入
的${!variable}形式使得使用間接引用更加直觀了.
假設(shè)一個(gè)變量的值是第二個(gè)變量的名字. 那么我們?nèi)绾螐牡谝粋€(gè)變量中取得第二個(gè)變量的值呢? 比如,
如果a=letter_of_alphabet并且letter_of_alphabet=z, 那么我們能夠通過(guò)引用變量a來(lái)獲得z么? 這確
實(shí)是可以做到的, 它被稱為間接引用. 它使用eval var1=\$$var2這種不平常的形式.
posted @ 2010-08-02 16:46 c++ 學(xué)習(xí) 閱讀(1376) | 評(píng)論 (0)編輯 收藏

2010年6月30日

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

一個(gè)正則表達(dá)式會(huì)包含下列一項(xiàng)或多項(xiàng):

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

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

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

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

  • 星號(hào) -- * -- 用來(lái)匹配它前面字符的任意多次, 包括0次.

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

  • 點(diǎn) -- . -- 用于匹配任意一個(gè)字符, 除了換行符. [1]

    "13." 匹配13 + 至少一個(gè)任意字符(包括空格): 1133, 11333, 但不能匹配13 (因?yàn)槿鄙?."所能匹配的至少一個(gè)任意字符).

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

  • 美元符 -- $ -- 在RE中用來(lái)匹配行尾.

    "XXX$" 匹配行尾的XXX.

    "^$" 匹配空行.

  • 中括號(hào) -- [...] -- 在RE中, 將匹配中括號(hào)字符集中的某一個(gè)字符.

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

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

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

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

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

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

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

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

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

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

    "\<the\>" 完整匹配單詞"the", 不會(huì)匹配"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.

  • 問(wèn)號(hào) -- ? -- 匹配它前面的字符, 但是只能匹配1次或0次. 通常用來(lái)匹配單個(gè)字符.

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

      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)義"大括號(hào)" -- \{ \} -- 在轉(zhuǎn)義后的大括號(hào)中加上一個(gè)數(shù)字, 這個(gè)數(shù)字就是它前面的RE所能匹配的次數(shù).

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

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

    Note

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

     

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

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

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

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

     

    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ù)字. 等價(jià)于A-Za-z0-9.

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

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

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

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

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

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

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

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

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

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

    Important

    POSIX字符類通常都要用引號(hào)或雙中括號(hào)([[ ]])引起來(lái).

     

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

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

     

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

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

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

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

注意事項(xiàng)

[1]

因?yàn)?a >sed, awk, 和grep通常用于處理單行, 但是不能匹配一個(gè)換行符. 如果你想處理多行輸入的話, 那么你可以使用"點(diǎn)"來(lái)匹配換行符.

  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í) 閱讀(7796) | 評(píng)論 (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沒(méi)有返回值

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í) 閱讀(2757) | 評(píng)論 (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.


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

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

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

命令替換的模版

用圓括號(hào)擴(kuò)起來(lái)的命令

>(command)

<(command)

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

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

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

在"<"或">"與圓括號(hào)之間是沒(méi)有空格的. 如果加了空格, 會(huì)產(chǎn)生錯(cuò)誤.

bash$ echo >(true)

/dev/fd/63

bash$ echo <(true)

/dev/fd/63

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

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

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

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

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)程替換來(lái)比較兩個(gè)不同目錄的內(nèi)容(可以查看哪些文件名相同, 哪些文件名不同):

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

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

1 cat <(ls -l)

2 # 等價(jià)于 ls -l | cat

3

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

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

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

7

8

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

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 # 因?yàn)?dev/fd/<n>的系統(tǒng)屬性,

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

16 #

17 # 這種效果可以被模擬出來(lái).

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

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

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

2

3 while read des what mask iface; do

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

5 done < <(route -n)

6

7

8 # 為了測(cè)試它, 我們讓它做點(diǎn)事.

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所給出的那樣, 一個(gè)更容易理解的等價(jià)代碼是:

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 #+ 這個(gè)簡(jiǎn)單的等價(jià)版本在while循環(huán)中使用了一個(gè)子shell,

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

28

29

30

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

32 #+ 如下所示.

33

34 (

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

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

37

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

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

40 )

41

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

43 (

44 : | x=x

45 # 看上去是啟動(dòng)了一個(gè)子shell

46 : | ( x=x )

47 # 但

48 x=x < <(:)

49 # 其實(shí)不是

50 )

51

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

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

注意事項(xiàng)

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

替換中.

posted @ 2010-06-27 17:15 c++ 學(xué)習(xí) 閱讀(1333) | 評(píng)論 (0)編輯 收藏

2010年6月25日

幾個(gè)知識(shí)點(diǎn)
1.Bash在實(shí)現(xiàn)pipeline(管道|)時(shí)會(huì)發(fā)起兩個(gè)subshell(子shell)來(lái)運(yùn)行|兩邊的命令,對(duì)于系統(tǒng)來(lái)說(shuō)就是發(fā)起兩個(gè)childprocess(子進(jìn)程)

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

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

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



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

我覺(jué)得要先弄清楚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.



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

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


QUOTE:
A child shell is also called subshell


問(wèn)題是fork+exec是fork一個(gè)child shell,然后在該child shell中exec.
而執(zhí)行腳本(shell>scriptname)時(shí),是fort一個(gè)child shell A,該child shell A再fork一個(gè)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í)行時(shí),大部分內(nèi)部命令也是在subshell中執(zhí)行。
可見,UNIX at Fermilab認(rèn)為fork 一個(gè)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再作什么動(dòng)作完全是external-cmd的

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

不過(guò),要細(xì)說(shuō)的話,要扯上 shell 在 interpret 一個(gè) command line 時(shí)的 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,載入命令運(yùn)行
如果大家有O'Reilly英文版的 Learning the Bash(2nd)的話,請(qǐng)多端詳p178的圖(細(xì)節(jié)略異)

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

第一步使用 metacharacter,與IFS沒(méi)有關(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 命令行的評(píng)價(jià)(evaluation)
下面是C shell 解釋命令行的順序:
1. 歷史替換
2. 分裂詞(包括特殊字符)
3. 更新歷史表
4. 解釋單引號(hào)(') 和 雙引號(hào)(")
5. 別名替換
6. 輸入和輸出的重定向(如 >  < 和 |)
7. 變量替換
8. 命令替換
9. 文件名擴(kuò)展
(Bourne shell 的解釋順序本質(zhì)上是一樣的,除了它不執(zhí)行歷史替換和別名替換之外)

所以
A=B  echo    $A

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

所以
A=B  echo    $A

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

posted @ 2010-06-25 16:29 c++ 學(xué)習(xí) 閱讀(549) | 評(píng)論 (0)編輯 收藏
僅列出標(biāo)題  下一頁(yè)
 
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            久久久久久久久久久久久女国产乱| 中文国产一区| 一区二区国产日产| 一区二区欧美精品| 国产精品一区二区三区久久久| 午夜日韩av| 欧美中文字幕在线| 在线视频亚洲| 欧美一区二区精品在线| 久久综合给合久久狠狠狠97色69| 99精品99| 亚洲永久精品大片| 亚洲精品麻豆| 日韩一区二区久久| 小嫩嫩精品导航| 欧美肥婆在线| 国产日韩欧美一区| 日韩小视频在线观看| 欧美在线日韩精品| 欧美日韩国产片| 亚洲午夜国产成人av电影男同| 久久久噜噜噜久久人人看| 免费不卡亚洲欧美| 一区二区三区av| 久久久亚洲国产天美传媒修理工| 亚洲一级电影| 久久夜色精品国产欧美乱极品 | 免费在线看成人av| 亚洲人精品午夜| 欧美一区免费| 欧美午夜精品久久久久久超碰| 欧美福利影院| 国产精品永久免费| 夜夜嗨一区二区| 欧美成人午夜免费视在线看片| 久久久久**毛片大全| 亚洲激情偷拍| 亚洲高清色综合| 欧美伊人久久| 国产欧美日韩91| 亚洲主播在线观看| 亚洲美女电影在线| 欧美成人自拍视频| 精品二区视频| 久久久伊人欧美| 亚洲欧美日韩国产一区二区| 欧美午夜免费影院| 国产精品一区毛片| 亚洲丝袜av一区| 日韩视频在线观看免费| 亚洲图片激情小说| 欧美日韩一区二区三区在线看 | 久久精品欧美日韩| 久久久久久亚洲综合影院红桃| 久久国产精品久久久久久久久久 | 欧美一区二区视频在线| 欧美一区二区大片| 国产美女高潮久久白浆| 亚洲欧美在线免费| 亚洲尤物影院| 国产有码在线一区二区视频| 91久久中文字幕| 欧美国产一区在线| 欧美电影在线观看| 一区二区三区日韩| 亚洲一区二区免费| 蜜臀av在线播放一区二区三区| 欧美黄色一区| 亚洲美女色禁图| 一区二区三区高清在线| 久久精品亚洲一区二区| 国产主播一区二区三区| 国产一区二区三区在线观看免费| 久久精品国产精品亚洲精品| 久久精品一区二区三区中文字幕| 免费久久久一本精品久久区| 亚洲娇小video精品| 亚洲另类在线一区| 国产精品久久久久久久第一福利| 亚洲第一精品夜夜躁人人爽| 欧美福利电影在线观看| 欧美日韩1区2区3区| 久久se精品一区精品二区| 久久人人精品| 亚洲影视九九影院在线观看| 欧美中文字幕在线播放| 99re成人精品视频| 性娇小13――14欧美| 亚洲黄色影院| 亚洲一区免费观看| 亚洲激情成人网| 亚洲欧美激情一区| 亚洲国产精品久久精品怡红院| 午夜国产精品视频| 亚洲巨乳在线| 国产一区二区三区av电影| 亚洲女同精品视频| 久热成人在线视频| 欧美一级欧美一级在线播放| 免费国产一区二区| 尤妮丝一区二区裸体视频| 91久久久久久| 欧美福利电影在线观看| 亚洲欧美国产制服动漫| 免费黄网站欧美| 久久资源在线| 国产乱子伦一区二区三区国色天香 | 亚洲一区二区三区精品动漫| 欧美激情偷拍| 国产欧美日本| 99精品免费网| 亚洲日韩视频| 久久综合激情| 久久亚洲春色中文字幕| 国产精品美女久久久久aⅴ国产馆| 亚洲一区二区日本| 欧美成人蜜桃| 一区二区三区 在线观看视频 | 亚洲老司机av| 亚洲电影在线看| 久久国产精品久久久久久| 午夜视频久久久久久| 欧美三级免费| 亚洲免费电影在线| 国产精品美女诱惑| 亚洲免费播放| 亚洲网友自拍| 欧美日韩精品| 亚洲毛片在线观看.| 亚洲免费成人av电影| 欧美福利在线| 亚洲精品人人| 欧美国产日韩在线观看| 亚洲伦理中文字幕| 久久精品国产亚洲a| 久久久福利视频| 国产一区二区久久久| 欧美一区二区日韩| 亚洲福利在线观看| 久久久久久久久久看片| 麻豆91精品91久久久的内涵| 激情另类综合| 免费不卡视频| 亚洲人成网站在线播| 一区二区三区不卡视频在线观看 | 久久亚洲欧洲| 久久这里只有| 欧美日韩在线三级| 99国产精品久久久久老师| 亚洲尤物在线视频观看| 国产精品自拍网站| 久久国内精品视频| 亚洲色图综合久久| 国产精品一区二区三区四区 | 国产亚洲人成网站在线观看| 午夜久久美女| 欧美福利电影在线观看| 宅男噜噜噜66国产日韩在线观看| 欧美专区日韩视频| 欧美承认网站| 一区二区三区四区五区在线| 国产精品成人在线观看| 欧美亚洲综合久久| 欧美国产欧美综合 | 亚洲精品乱码久久久久久蜜桃91| 国产亚洲精品bv在线观看| 久久精品官网| 亚洲精品在线观看免费| 在线观看日韩国产| 欧美日韩精品一区二区天天拍小说 | 免费一级欧美在线大片| 国产精品久久久999| 久久国产精品久久精品国产| 亚洲国产片色| 久久久国产视频91| 亚洲一区精彩视频| 欧美日韩国产va另类| 亚洲欧美制服中文字幕| 亚洲国产精品久久久久婷婷884| 好吊日精品视频| 欧美日韩精品久久久| 久久久精品一区| 一本大道久久精品懂色aⅴ| 欧美freesex8一10精品| 小嫩嫩精品导航| 一区二区三区久久久| 亚洲成色777777女色窝| 国产视频欧美视频| 欧美承认网站| 在线观看国产精品淫| 欧美午夜久久| 欧美日韩国产色站一区二区三区| 亚洲欧洲精品一区二区| 久热国产精品视频| 久久aⅴ乱码一区二区三区| 一区二区三区不卡视频在线观看 | 欧美激情麻豆| 久久久久久色| 久久精品国产99国产精品| 亚洲综合99|