VC++(十五)網(wǎng)絡(luò)編程

  • 程序和進(jìn)程
    程序是計算機(jī)指令的集合,它以文件的形式存儲在磁盤上达皿。
    進(jìn)程通常被定義為一個正在運行的程序的實例天吓。是一個程序在其自身的地址空間中的一次執(zhí)行活動。
    一個程序可以對應(yīng)多個進(jìn)程峦椰。

  • 進(jìn)程是資源申請失仁、調(diào)度和獨立運行的單位,因此们何,它使用系統(tǒng)中的運行資源。
    程序不能申請系統(tǒng)資源控轿,不能被系統(tǒng)調(diào)度冤竹,也不能作為獨立運行的單位拂封。因此它不占用系統(tǒng)的運行資源。
    真正完成代碼執(zhí)行的是線程鹦蠕,而進(jìn)程只是線程的容器冒签,或者說是線程的執(zhí)行環(huán)境。

  • 主線程钟病,也就是執(zhí)行main函數(shù)或者WinMain函數(shù)的線程萧恕,可以把main函數(shù)或者WinMain函數(shù)看做是主線程的進(jìn)入點函數(shù)。此后肠阱,主線程可以創(chuàng)建其他線程票唆。
    系統(tǒng)賦予每個進(jìn)程獨立的虛擬地址空間。32位進(jìn)程屹徘,4GB走趋。

磁盤緩存、虛擬內(nèi)存噪伊、頁面文件和物理內(nèi)存的關(guān)系
磁盤緩存分為讀緩存和寫緩存簿煌。讀緩存是指,操作系統(tǒng)為已讀取的文件數(shù)據(jù)鉴吹,在內(nèi)存較空閑的情況下留在內(nèi)存空間中(這個內(nèi)存空間被稱之為“內(nèi)存池”)姨伟,當(dāng)下次軟件或用戶再次讀取同一文件時就不必重新從磁盤上讀取,從而提高速度豆励。寫緩存實際上就是將要寫入磁盤的數(shù)據(jù)先保存于系統(tǒng)為寫緩存分配的內(nèi)存空間中夺荒,當(dāng)保存到內(nèi)存池中的數(shù)據(jù)達(dá)到一個程度時,便將數(shù)據(jù)保存到硬盤中肆糕。這樣可以減少實際的磁盤操作般堆,有效的保護(hù)磁盤免于重復(fù)的讀寫操作而導(dǎo)致的損壞,也能減少寫入所需的時間诚啃。
虛擬內(nèi)存是用硬盤空間做內(nèi)存來彌補(bǔ)計算機(jī)RAM空間的缺乏淮摔。當(dāng)實際RAM滿時(實際上,在RAM滿之前)始赎,虛擬內(nèi)存就在硬盤上創(chuàng)建了和橙。當(dāng)物理內(nèi)存用完后,虛擬內(nèi)存管理器選擇最近沒有用過的造垛,低優(yōu)先級的內(nèi)存部分寫到交換文件上魔招。這個過程對應(yīng)用是隱藏的,應(yīng)用把虛擬內(nèi)存和實際內(nèi)存看作是一樣的五辽。虛擬內(nèi)存文件也就是頁面文件办斑。

線程有兩個部分組成

  1. 線程的內(nèi)核對象。操作系統(tǒng)用它來對線程實施管理。關(guān)于線程的統(tǒng)計信息組成的一個小型數(shù)據(jù)結(jié)構(gòu)乡翅。
  2. 線程棧鳞疲。它用于維護(hù)線程在執(zhí)行代碼時需要的所有函數(shù)參數(shù)和局部變量。
    新線程可以訪問進(jìn)程的內(nèi)核對象的所有句柄蠕蚜、進(jìn)程中的所有內(nèi)存和在這個相同的進(jìn)程中的所有其他線程的堆棧尚洽。

進(jìn)程、線程及內(nèi)核對象

內(nèi)核對象
每個內(nèi)核對象只是內(nèi)核分配的一個內(nèi)存塊靶累,并且只能由該內(nèi)核訪問腺毫,這個內(nèi)存塊是一種數(shù)據(jù)結(jié)構(gòu),他的成員負(fù)責(zé)維護(hù)該對象的各種信息挣柬,如進(jìn)程對象有一個進(jìn)程ID潮酒、一個基本優(yōu)先級和一個退出代碼。

由于內(nèi)核對象的數(shù)據(jù)結(jié)構(gòu)只能被內(nèi)核訪問凛忿,so應(yīng)用程序是無法在內(nèi)存中找到這些數(shù)據(jù)結(jié)構(gòu)的并直接改變其內(nèi)容的澈灼。Windows提出這個限制為了確保內(nèi)核對象結(jié)構(gòu)保持狀態(tài)的一致,也是為了保證Microsoft能夠在不破壞應(yīng)用程序的情況下在這些內(nèi)核對象的結(jié)構(gòu)中添加店溢、刪除叁熔、修改這些數(shù)據(jù)成員;

內(nèi)核對象使用引用計數(shù)
內(nèi)核對象由內(nèi)核所有床牧,而不是進(jìn)程所有荣回,舉例說明,在做單進(jìn)程限制時戈咳,我們一般會CreateMutex來創(chuàng)建一個命名的Mutex心软,再另外一個進(jìn)程中再來創(chuàng)建或者打開相同命名的Mutex來檢驗有相同進(jìn)程被創(chuàng)建。也可以這么說進(jìn)程調(diào)用一個創(chuàng)建的內(nèi)核對象函數(shù)著蛙,進(jìn)程終止了但是內(nèi)核對象不一定被撤銷删铃;

每個線程都有自己的一組cpu寄存器和堆棧,每個進(jìn)程至少有一個線程踏堡,來執(zhí)行進(jìn)程的地址空間中的代碼猎唁。如果沒有線程來執(zhí)行進(jìn)程地址空間中的代碼,那么這個進(jìn)程就沒有存在的理由顷蟆,系統(tǒng)會自動撤銷該進(jìn)程诫隅;

進(jìn)程的實例句柄
加載到進(jìn)程地址空間的每個可執(zhí)行文件或者dll均被賦予了一個獨一無二的實例句柄≌寿耍可執(zhí)行的文件的實例作為(w)WinMain的第一個參數(shù)hinstExe來傳遞逐纬;

  • WinMain的hinstExe參數(shù)的實際值是系統(tǒng)將可執(zhí)行文件的映像加載到進(jìn)程的地址空間時使用的基本地址空間。例如削樊,如果系統(tǒng)打開了可執(zhí)行文件并將它的內(nèi)容加載到地址的0x00400000豁生,那么WinMain的hinstExe的參數(shù)值就是0x00400000;

  • 可執(zhí)行文件的映像加載到的基地址是由連接程序決定的,不同的鏈接程序可以使用不同的默認(rèn)基地址沛硅。VC++鏈接程序使用的默認(rèn)基地址是0x00400000眼刃;只是運行在windows98時可執(zhí)行文件的映像可以加載到的最低地址。

  • 進(jìn)程的前一個實例句柄
    C/C++運行期啟動代碼總是將NULL傳遞給WinMain的hinstExePrev參數(shù)摇肌,該參數(shù)使用在16位windows中的,并且保留了WinMain的一個參數(shù)仪际,目的僅僅是為了能夠容易的轉(zhuǎn)用16位windows應(yīng)用程序围小,so絕不應(yīng)該在代碼中引用這個參數(shù)。

  • 線程有兩部分組成:
    一個是線程的內(nèi)核對象树碱,操作系統(tǒng)用他來對線程實施管理肯适。內(nèi)核對象也是系統(tǒng)用來存放線程統(tǒng)計信息的地方;
    一個是線程堆棧成榜,用于維護(hù)線程在執(zhí)行代碼時需要的所有函數(shù)參數(shù)和局部變量框舔;

進(jìn)程是不活潑的,進(jìn)程從不執(zhí)行任何東西赎婚,它只是線程的容器刘绣。線程總是在某個進(jìn)程環(huán)境中創(chuàng)建的,而且它的整個壽命期都在該進(jìn)程中挣输。那么這意味著線程在他的進(jìn)程地址空間中執(zhí)行代碼纬凤,并在進(jìn)程的地址空間中對數(shù)據(jù)進(jìn)行操作。so在單進(jìn)程中撩嚼,如果存在多個線程的運行停士,那么這些線程將共享單個地址空間。這些線程能夠執(zhí)行相同的代碼對相同數(shù)據(jù)進(jìn)行操作完丽。而且他們還能共享內(nèi)核對象句柄恋技,因為句柄表依賴于每個進(jìn)程而非每個線程存在的。

進(jìn)程使用的系統(tǒng)資源要比線程對很多逻族,原因是他需要更多的地址空間蜻底。為進(jìn)程創(chuàng)建一個虛擬的地址空間需要系統(tǒng)很多的資源。系統(tǒng)中要保留大量的記錄瓷耙,這需要占用大量的內(nèi)存朱躺。另外,由于exe和dll文件需要加載到一個地址空間搁痛,因此也需要文件資源长搀,而線程使用的系統(tǒng)資源就小很多了,他只需要一個內(nèi)核對象和一個堆棧就ok鸡典,保留的記錄也很少源请,so只需要很少的內(nèi)存。

So能用線程解決的問題,要避免創(chuàng)建新進(jìn)程來解決谁尸。

注:CreateThread函數(shù)是用來創(chuàng)建線程的windows函數(shù)舅踪,如果你正在編寫C/C++代碼,絕不應(yīng)該調(diào)用CreateThread良蛮,相反抽碌,應(yīng)該使用VC++運行庫函數(shù)_beginthreadex。原因是决瞳,標(biāo)準(zhǔn)C/C++運行庫在最初設(shè)計的時候并沒有考慮到多線程的問題货徙。若要使多線程C/C++程序能夠正常的運行起來,必須創(chuàng)建一個數(shù)據(jù)結(jié)構(gòu)皮胡,并將它與使用的C/C++運行庫函數(shù)的每一個線程關(guān)聯(lián)起來痴颊,當(dāng)你調(diào)用C/C++運行庫時,這些函數(shù)必須知道查看調(diào)用線程的數(shù)據(jù)塊屡贺,這樣就不會對別的線程產(chǎn)生不良影響蠢棱。那么系統(tǒng)是否知道在創(chuàng)建新線程時分配該數(shù)據(jù)塊呢?no甩栈,系統(tǒng)是無法知道你的應(yīng)用程序使用C/C++編寫的泻仙,自然不知道你調(diào)用的線程本身不安全,所以不要調(diào)用操作系統(tǒng)的CreateThread函數(shù)谤职,而需要調(diào)用_beginthreadex.

線程的運行時間
有時要計算線程執(zhí)行某個任務(wù)需要多長時間饰豺,很多時候我們會采用如下代碼:

//start time

DWORD starttime = GetTickCount();

//complex algorithm here;

//subtract start time from current time to get duration;

DWORD dwElapsedTime = GetTickCount() – dwStartTime;

這個代碼有個簡單的假設(shè):運行不會被中斷;但是在搶占式的windows操作系統(tǒng)是不可能存在的允蜈,so我們需要用另外一個方式來計算冤吨,該函數(shù)為GetThreadTime;

內(nèi)存映射文件

Windows提供了3種進(jìn)行內(nèi)存管理的方法:

  • 虛擬內(nèi)存饶套,最適合用來管理大型對象或結(jié)構(gòu)數(shù)組漩蟆。

  • 內(nèi)存映射文件,最適合用來管理大型數(shù)據(jù)流(通常來自文件)以及在單個計算機(jī)上運行的多個進(jìn)程之間共享數(shù)據(jù)妓蛮。

  • 內(nèi)存堆棧怠李,最適合用來管理大量的小對象。

這里將先講述一下關(guān)于內(nèi)存映射文件的使用情況蛤克。

與虛擬內(nèi)存一樣捺癞,內(nèi)存映射文件可以用來保留一個地址空間的區(qū)域,并將物理存儲器提交給該區(qū)域构挤,他們之間的差別是物理存儲器來自一個已經(jīng)位于磁盤上的文件髓介,而不是系統(tǒng)的頁文件。一旦該文件被映射筋现,就可以訪問它唐础,就像是整個文件已經(jīng)加載到內(nèi)存中一樣箱歧。

內(nèi)存映射文件的3個目的:

  • 系統(tǒng)使用內(nèi)存映射文件,以便快速加載和執(zhí)行exe和dll文件一膨。

  • 可以使用內(nèi)存映射文件快速訪問磁盤上的數(shù)據(jù)文件呀邢,這樣使得你不必對文件執(zhí)行I/O操作,并且不必對文件內(nèi)容進(jìn)行緩存豹绪。

  • 可以使用內(nèi)存映射文件來進(jìn)行進(jìn)程間的數(shù)據(jù)共享价淌。

內(nèi)存映射文件的步驟:

若要使用內(nèi)存映射文件,必須執(zhí)行下列操作步驟:

  1. 創(chuàng)建或打開一個文件內(nèi)核對象森篷,該對象用于標(biāo)識磁盤上你想用作內(nèi)存映射文件的文件输钩。(CreateFile)

  2. 創(chuàng)建一個文件映射內(nèi)核對象,告訴系統(tǒng)該文件的大小和你打算如何訪問該文件仲智。(CreateFileMapping)

  3. 讓系統(tǒng)將文件映射對象的全部或一部分映射到你的進(jìn)程地址空間中。(MapViewOfFile)

當(dāng)完成對內(nèi)存映射文件的使用時姻氨,必須執(zhí)行下面這些步驟將它清除:

  1. 告訴系統(tǒng)從你的進(jìn)程的地址空間中撤消文件映射內(nèi)核對象的映像钓辆。(UnmapViewOfFile)

  2. 關(guān)閉文件映射內(nèi)核對象和文件內(nèi)核對象。(CloseHandle)

CreateThread,該函數(shù)將創(chuàng)建一個線程肴焊。

很多程序在創(chuàng)建線程都這樣寫的:
ThreadHandle = CreateThread(NULL,0,.....);
CloseHandle(ThreadHandle );
1前联,線程和線程句柄(Handle)不是一個東西,線程句柄是一個內(nèi)核對象娶眷。我們可以通過句柄來操作線程似嗤,但是線程的生命周期和線程句柄的生命周期不一樣的。線程的生命周期就是線程函數(shù)從開始執(zhí)行到return届宠,線程句柄的生命周期是從CreateThread返回到你CloseHandle()烁落。
2,線程句柄是一種內(nèi)核對象,系統(tǒng)維護(hù)著每一個內(nèi)核對象,當(dāng)每個內(nèi)核對象引用記數(shù)為0時,系統(tǒng)就從內(nèi)存中釋放該對象,CloseHandle就是將該線程對象的引用記數(shù)減1豌注。所有的內(nèi)核對象(包括線程Handle)都是系統(tǒng)資源伤塌,用了要還的,也就是說用完后一定要closehandle關(guān)閉之轧铁,如果不這么做每聪,你系統(tǒng)的句柄資源很快就用光了。
只是關(guān)閉了一個線程句柄對象齿风,表示我不再使用該句柄药薯,即不對這個句柄對應(yīng)的線程做任何干預(yù)了。并沒有結(jié)束線程.

  • 如果線程需要訪問共享資源救斑,就需要進(jìn)行線程之間的同步處理童本。

  • 利用互斥對象實現(xiàn)線程同步
    互斥對象mutex屬于內(nèi)核對象。它能確保線程擁有對單個資源的互斥訪問權(quán)系谐。
    互斥對象包含一個使用數(shù)量巾陕,一個線程ID和一個計數(shù)器讨跟。
    ID用于標(biāo)識系統(tǒng)中的哪個對象擁有互斥對象。
    計數(shù)器用于指明該線程擁有互斥對象的次數(shù)鄙煤。

HANDLE hMutex; 互斥對象的句柄
hMutex=CreateMutex(NULL,FALSE,NULL);

WaitForSingleObject(hMutex,INFINITE);
ReleaseMutex(hMutex);
對互斥對象來說晾匠,誰擁有誰釋放。

如果多次在同一線程中請求同一個互斥對象梯刚,那么就需要相應(yīng)的多次調(diào)用
ReleaseMutex函數(shù)釋放該互斥對象凉馆。

注:主線程擁有該互斥對象時,該對象就處于未通知狀態(tài)了亡资。主線程通過WaitForSingleObject函數(shù)再次請求該互斥對象的所有權(quán)時澜共,因為ID相同,所以仍然能夠請求到這個互斥對象锥腻。操作系統(tǒng)通過互斥對象內(nèi)部的計數(shù)器來維護(hù)同一個線程請求到該互斥對象的次數(shù)嗦董。

  • 保證程序只有一個實例運行
    對這種同時只能有應(yīng)用程序的一個實例運行的功能,可以通過命名的互斥對象來實現(xiàn)瘦黑。
    CreateMutex
    GetLastError 返回值 ERROR_ALREADY_EXISTS 或其他

  • LPVOID,是一個沒有類型的指針京革,也就是說你可以將任意類型的指針賦值給LPVOID類型的變量(一般作為參數(shù)傳遞),然后在使用的時候再轉(zhuǎn)換回來幸斥。

  • 不能使用全局函數(shù)和全局變量匹摇,我們就可以采用靜態(tài)成員函數(shù)和靜態(tài)成員變量的方法來解決上述問題。

  • PVOID是void*的別名甲葬。

  • 在windef.h中廊勃,LPVOID是這么定義的:typedef void far *LPVOID。

  • 和void*的區(qū)別是遠(yuǎn)指針经窖,因為win32編程中坡垫,經(jīng)常要調(diào)用外部DLL堆變量。但現(xiàn)在的大部分平臺已經(jīng)無所謂了钠至,因為尋址方式成flat了葛虐。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市棉钧,隨后出現(xiàn)的幾起案子屿脐,更是在濱河造成了極大的恐慌,老刑警劉巖宪卿,帶你破解...
    沈念sama閱讀 222,464評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件的诵,死亡現(xiàn)場離奇詭異,居然都是意外死亡佑钾,警方通過查閱死者的電腦和手機(jī)西疤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,033評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來休溶,“玉大人代赁,你說我怎么就攤上這事扰她。” “怎么了芭碍?”我有些...
    開封第一講書人閱讀 169,078評論 0 362
  • 文/不壞的土叔 我叫張陵徒役,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么误甚? 我笑而不...
    開封第一講書人閱讀 59,979評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮器腋,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好晌砾,可當(dāng)我...
    茶點故事閱讀 69,001評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著烦磁,像睡著了一般贡羔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上个初,一...
    開封第一講書人閱讀 52,584評論 1 312
  • 那天,我揣著相機(jī)與錄音猴蹂,去河邊找鬼院溺。 笑死,一個胖子當(dāng)著我的面吹牛磅轻,可吹牛的內(nèi)容都是我干的珍逸。 我是一名探鬼主播,決...
    沈念sama閱讀 41,085評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼聋溜,長吁一口氣:“原來是場噩夢啊……” “哼谆膳!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起撮躁,我...
    開封第一講書人閱讀 40,023評論 0 277
  • 序言:老撾萬榮一對情侶失蹤漱病,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后把曼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體杨帽,經(jīng)...
    沈念sama閱讀 46,555評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,626評論 3 342
  • 正文 我和宋清朗相戀三年嗤军,在試婚紗的時候發(fā)現(xiàn)自己被綠了注盈。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,769評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡叙赚,死狀恐怖老客,靈堂內(nèi)的尸體忽然破棺而出僚饭,到底是詐尸還是另有隱情,我是刑警寧澤胧砰,帶...
    沈念sama閱讀 36,439評論 5 351
  • 正文 年R本政府宣布鳍鸵,位于F島的核電站,受9級特大地震影響朴则,放射性物質(zhì)發(fā)生泄漏权纤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,115評論 3 335
  • 文/蒙蒙 一乌妒、第九天 我趴在偏房一處隱蔽的房頂上張望汹想。 院中可真熱鬧,春花似錦撤蚊、人聲如沸古掏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,601評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽槽唾。三九已至,卻和暖如春光涂,著一層夾襖步出監(jiān)牢的瞬間庞萍,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,702評論 1 274
  • 我被黑心中介騙來泰國打工忘闻, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留钝计,地道東北人。 一個月前我還...
    沈念sama閱讀 49,191評論 3 378
  • 正文 我出身青樓齐佳,卻偏偏與公主長得像私恬,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子炼吴,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,781評論 2 361

推薦閱讀更多精彩內(nèi)容