A. 動態庫 是什麼
首先,想要知道動態庫,我們得了解C++/C以及計算機的一些背景知識。
一般而言,在Windows下,*.dll文件就是動態庫文件。用C++/C開發的程序,在發布的時候,會出現兩種情況,第一,整個軟體就只有一個文件,你只要雙擊那個exe文件,就可以運行。第二,除了exe之外,還有dll等文件。在這里,我們假設的文件只有exe文件和dll文件, 不討論什麼圖標之類文件。
只有一個文件的,庫已經嵌到那個exe裡面。而有很多dll文件的程序,庫沒有嵌入到exe裡面。所以,你可以看一下,如果那個exe文件大小非常大,那就說明是靜態鏈接,在開發的時候是使用靜態庫。如果那個exe非常小,那麼一般是使用的動態庫。
那麼問題來了,動態庫與靜態庫相比優勢又是什麼。動態庫節約內存,為什麼這么說呢。假如兩個類型的程序,如果他們都有一個共同使用的dll,那麼在內存裡面,只有一份,而不是兩份。如果是使用了靜態庫,這會有兩份,會有很大的浪費空間。
當然,使用動態庫還有需要注意的地方。比如,有兩個名字一模一樣的動態庫Qtcore4.dll,但是呢,一個dll是用vs2010編譯器生成的,一個是用vs2015編譯器生成的。如果,exe使用的dll弄錯的話,程序結果會不對或者其他奇葩的問題。
以上均是一個大致的講解,細節部分請參考程序員的自我修養這本書!
B. LIB靜態庫怎麼定義導出函數
靜態庫不需要定義導出,只要有.h頭文件就可以調用。
可以閱讀《程序員的自我修養--鏈接、裝載與庫》的pdf了解靜態庫、動態庫的原理
靜態庫可能只是定位DLL用,也可能含有代碼,鏈接時會把這部分代碼直接包含到程序不需要導出
C. 《阿里工程師的自我修養》pdf下載在線閱讀全文,求百度網盤雲資源
《阿里工程師的自我修養》網路網盤pdf最新全集下載:
鏈接:https://pan..com/s/1OOfBjFlVLk68xRWztMtm1g
D. 求t《tcp/ip詳解》《軟體測試的藝術》 《程序員自我修養--》電子書
《軟體測試的藝術》中文版清晰.pdf免費下載
鏈接:https://pan..com/s/1GYbyq1bSXiM4F2V9ezrVgA
《軟體測試的藝術》(原書第3版)是2012年機械工業出版社出版的圖書。本書展示了各類軟體測試的方法和智慧,闡述了如何將經典軟體測試法則應用到解決當今計算機行業所面臨的問題之中。
E. C文件如何成為可執行文件(編譯、鏈接、執行)——摘自《程序員的自我修養》
本文算是我閱讀《程序員的自我修養》(俞甲子等著)相關章節的筆記,文中直接引用了原書中的敘述,強烈建議大家去看原書,本文只做概要介紹而用。——註:文中有很多引用圖的地方,請大家自己去找原書看,支持正版!我遇到一個問題,Linux C編程中的問題:.. char *p; unsigned int i = 0xcccccccc; unsigned int j; p = (char *) &i; printf("%.2x %.2x %.2x %.2x\n", *p, p[1], p[2], p[3]); memcpy(&j, p, sizeof(unsigned int)); printf("%x\n", j); ... Output: ffffffcc ffffffcc ffffffcc ffffffcc 0xcccccccc My questions are: 1. Why it prints "ffffffcc ffffffcc ffffffcc ffffffcc"? (if p is unsigned char* then it will print correctly "cc cc cc cc") 2. Why pointer to char p copied to j correctly, why not every member in p overflow? since it is a signed char. 這是別人在郵件列表中提出的問題,在試圖回答這個問題的過程中,突然發現,自己對連接器的工作並不熟悉,因此拿來好書《程序員的自我修養》來看,並做如下匯報,強烈推薦《程序員的自我修養》!!!寫好的C語言文件,最終能夠執行,大致要經過預處理、編譯、匯編、鏈接、裝載五個過程。預編譯完成的工作: (1)將所有的"#define"刪除,並展開所有的宏定義 (2)處理所有條件預編譯指令 (3)處理#include預編譯指令,將被包含的文件插入到預編譯指令的位置,這個過程是遞歸進行的。 (4)刪除所有的注釋 (5)添加行號和文件名標識,以便調試 (6)保留所有的#pragma編譯器命令,因為編譯器需要使用它們。編譯完成的工作: (1)詞法分析 掃描源代碼序列,並將其分割為一系列的記號(Token)。 (2)語法分析 用語法分析器生成語法樹,確定運算符號的優先順序和含義、報告語法錯誤。 (3)語義分析 靜態語義分析包括生命和類型的匹配,類型的轉換;動態語義分析一般是在運行期出現的與語義相關性的問題,如除0錯。 (4)源代碼生成 源代碼級優化器在源代碼級別進行優化:如將如(6+2)之類的表達式,直接優化為(8)等等。將語法書轉換為中間代碼,如三地址碼、P-代碼等。 (5)代碼生成 將源代碼轉換為目標代碼,依賴於目標機器。 (6)目標代碼優化匯編完成的工作: 將匯編代碼變成機器可以執行的指令鏈接完成的工作: 鏈接完成的工作主要是將各個模塊之間相互引用的部分處理好,使得各個模塊之間正確銜接。鏈接過程包括:地址和空間分配、符號決議和重定位。 首先講靜態鏈接,基本的靜態鏈接如下: 我們可能在main函數中調用到定義在另一個文件中的函數foo(),但是由於每個模塊式單獨編譯的,因此main並不知道foo的地址,所以它暫時把這些調用foo的指令的目標地址擱置,等到最後鏈接的時候讓連接器去修正這些地址(重定位),這就是靜態鏈接最基本的過程和作用;對於定義在其他文件中的變數,也存在相同的問題。具體過程如下: (1)空間和地址分配 1)空間與地址分配:掃描所有輸入目標文件,獲得各個段的屬性、長度和位置,並且將目標文件中的符號表中所有的符號定義和符號引用收集起來,放到一個全局符號表中。 2)符號解析和重定位:使用第一步收集到的信息,讀取輸入文件中段的數據、重定位信息,並進行符號解析與重定位、調整代碼中的地址等。 動態鏈接的過程更為復雜,但是完成的工作類似。 動態鏈接的初衷是為了解決空間浪費和更新困難的問題,把鏈接過程推遲到運行時進行 首先介紹一個重要的概念——地址無關代碼。為了解決固定裝載地址沖突的問題,我們希望對所有絕對地址的引用不作重定位,而把這一步推遲到裝載的時候再完成,一旦模塊裝載地址確定,即目標地址確定,那麼系統對程序中所有的絕對地址引用進行重定位。同時我們希望,模塊中共享的指令部分在裝載時不需要因為裝載地址的改變而改變,所以把指令中那些需要被修改的部分分離出來,跟數據放在一起,這樣指令部分就可以保持不變,而數據部分可以在每個進程中擁有一個副本,這種方案目前被稱為地址無關代碼(PIC,Position-independent Code)。 我們需要解決如下四種引用中的重定位問題: 1)模塊內部調用或者跳轉:這個可以用相對地址調用或者基於寄存器的相對調用,所以不需要重定位2)模塊內部數據的訪問:用相對定址的方法,不過鏈接器實現得十分巧妙: call494 <__i686.get_pc_thunk.cx> add$0x188c, %ecx mov$0x1, 0x28(%ecx) //a=1 調用一個叫做__i686.get_pc_thunk.cx的函數,把call的下一條指令的地址放到ecx寄存器中,接著執行一條mov指令和一個add指令3)模塊間數據的訪問:在數據段里建立一個指向全局變數的指針數組,也成全局便宜表(GOT),當要引用全局變數時,可以通過GOT相對應的項間接引用: GOT是做到指令無關的重要的一環:在編譯時可以確定GOT相對於當前指令的偏移,根據變數地址在GOT中的偏移就可以得到變數的地址,當然GOT中哪個每個地址對應於哪個變數是由編譯器決定的。4)模塊間的調用、跳轉:採用上面類似的方法,不同的是GOT中相應的項存儲的是目標函數的地址,當模塊需要調用目標函數時,可以通過GOT中的項進行間接跳轉。 地址無關代碼小結: 現在,來看動態鏈接中的另一個重要問題——延遲綁定(PLT)。當函數第一次被用到時才進行綁定,否則不綁定。PLT為了實現延遲綁定,增加了一層間接跳轉。調用函數並不是通過GOT跳轉的,而是通過一個叫PLT項的結構進行跳轉的,每個外部函數在PLT中都有對應的項,如函數bar,其在PLT對應的項的地址記為bar@plt,實現方式如下: bar@plt: jmp* (bar@GOT) pushn pushmoleID jump_dl_runtime_resolve 鏈接器的這個實現至為巧妙: 如果在連接器初始化階段,已經正確的初始化了bar@GOT,那麼這個跳轉指令的結果正是我們所期望的,但是,為了實現PLT,一般在連接器初始化時,將"pushn"的地址放入到bar@GOT中,這樣就直接跳轉到第二條指令,相當於沒有進行任何操作。第二條指令「pushn」,n是bar這個符號引用在重定位表「.rel.plt」中的下標。接著將模塊的ID壓棧,跳轉到_dl_runtime_resolve完成符號解析和重定位工作,然後將bar的地址填入到bar@GOT中。下次再調用到bar時,則bar@GOT中存儲的是一個正確的地址,這樣就完成了整個過程。 在鏈接完成之後,就生成了你要的可執行文件了,如ELF文件,至於這個文件的詳細的信息,可以參考相關的文檔。 現在,你要運行你的可執行文件,這是如何做到的呢? 我們從操作系統的角度來看可執行文件的裝載過程。操作系統主要做如下三件事情:(1)創建一個獨立的虛擬地址空間,但由於採用了COW機制,這里只是復制了父進程的頁目錄和頁表,甚至不設置映射關系(參考操作系統相關書籍)。(2)讀取可執行文件頭,並且建立虛擬空間與可執行文件的映射關系。(3)將CPU的指令寄存器設置成可執行文件的入口地址,啟動運行。我們來看一下執行過程中,進程虛擬空間的分布。 首先我們來區分Section和Segment,都可以翻譯為「段」,那麼有什麼不同呢?從鏈接的角度來講,elf文件是按照Section存儲的,從裝載的角度講,elf文件是按照Segment存儲的。」Segment」實際上是從裝載的角度重新劃分了ELF的各個段,將其中屬性相似的Section合並為一個Segment,而系統是按照Segment來映射可執行文件的。
F. 程序員的自我修養的作品目錄
第1部分 我叫邵總
第1章 溫故而知新
第2章 編譯和鏈接
第3章 目標文件里有什麼
第4章 靜態鏈接
第5章 WINDOWS PE/COFF
第3部分 裝載與動態鏈接
第6章可執行文件的裝載與進程
第7章 動態鏈接
第8章 LINUX 共享庫的組織
第9章 WINDOWS 下的動態鏈接
第4部分 庫與運行庫
第10章 內存
第11章 運行庫
第12章 系統調用與API
第13章 運行庫實現
附錄
索引
G. 《程序員的自我修養鏈接、裝載與庫》epub下載在線閱讀,求百度網盤雲資源
《程序員的自我修養》(俞甲子)電子書網盤下載免費在線閱讀
資源鏈接:
鏈接: https://pan..com/s/1wM51_NPLIH9aRZF1r7e26A
書名:程序員的自我修養
作者:俞甲子
豆瓣評分:8.8
出版社:電子工業出版社
出版年份:2009-4
頁數:459
內容簡介:
這本書主要介紹系統軟體的運行機制和原理,涉及在Windows和Linux兩個系統平台上,一個應用程序在編譯、鏈接和運行時刻所發生的各種事項,包括:代碼指令是如何保存的,庫文件如何與應用程序代碼靜態鏈接,應用程序如何被裝載到內存中並開始運行,動態鏈接如何實現,C/C++運行庫的工作原理,以及操作系統提供的系統服務是如何被調用的。每個技術專題都配備了大量圖、表和代碼實例,力求將復雜的機制以簡潔的形式表達出來。本書最後還提供了一個小巧且跨平台的C/C++運吵纖行庫MiniCRT,綜合展示了與運行庫相關的各種技術。
對裝載、鏈接和庫進行了深入淺升迅仿出的剖析,並且輔以大量的例子和圖表,可以作為計昌岩算機軟體專業和其他相關專業大學本科高年級學生深入學習系統軟體的參考書。同時,還可作為各行業從事軟體開發的工程師、研究人員以及其他對系統軟體實現機制和技術感興趣者的自學教材。
H. python 是否有能列出動態鏈接庫中有哪些方法的庫
最近看了《Gray hat python》一書,這才知道為什麼python是黑客必學的編程語言。通過python的ctypes模塊,可以直接調用動態鏈接庫中的導出函數,而且甚至可以直接在python中構建出復雜的C結構體!!!使得python也具備了底層內存操作的能力,再配合python本身強大的表達能力,能不讓人激動么。
之前為了在python中調用動態鏈接庫導出的函數,你需要自行解析出這些導出函數的地址。而現在ctypes庫會替我們完成這個麻煩的過程,大大方便了我們直接在python中調用C函數的能力。
ctypes模塊中有三種不同的動態鏈接庫載入方式:cdll, windll, oledll。不同之處在於鏈接庫中的函數所遵從的函數調用方式(calling convention)以及返回方式有所不同。
cdll用於載入遵循cdecl標准函數調用約定的鏈接庫。windll則用於載入遵循stdcall調用約定的動態鏈接庫。oledll與windll完全相同,只是會默認其載入的函數會統一返回一個Windows HRESULT錯誤編碼。
先復習一下有關函數調用約定的知識:函數調用約定指的是函數參數入棧的順序、哪些參數入棧、哪些通過寄存器傳值、函數返回時棧幀的回收方式(是由調用者負責清理,還是被調用者清理)、函數名稱的修飾方法等等。基本上我們最常見的調用約定就是cdecl和stdcall兩種。在《程序員的自我修養--鏈接、裝載與庫》一書的第10章有對函數調用約定的更詳細介紹。
cdecl規定函數參數列表以從右到左的方式入棧,且由函數的調用者負責清除棧幀上的參數。stdcall的參數入棧方式與cdecl一致,但函數返回時是由被調用者自己負責清理棧幀。而且stdcall是Win32 API函數所使用的調用約定。OK,就這么多,夠了。
測試一下在Linux平台和Windows平台下通過ctypes模塊導入C庫中函數的小例子:
Windows 下:
from ctypes import *
msvcrt = cdll.msvcrt
msg = "Hello world!\n"
msvcrt.printf("Testing: %s", msg)
Linux下:
from ctypes import *
libc = CDLL("libc.so.6")
msg = "Hello, world!\n"
libc.printf("Testing: %s", msg)
可以看到動態鏈接庫中的printf被直接導入到python中來調用了。
那麼,在python中怎麼表示C的類型?不用擔心,下面這張表就能搞定。
有了這個映射關系,多復雜的C類型也能在python中表達出來。
在C中定義一個聯合:
union
{
long barley_long;
int barley_int;
char barley_char[8];
}barley_amount;
而在python中同等的定義為:注意一下python中定義數組的方式。
class barley_amount(Union):
_fields_ = [
("barley_long", c_long),
("barley_int", c_int),
("barley_char", c_char * 8),
]
測試一下這個例子,在python中定義一個聯合體,為其賦值,再分別訪問其成員。
from ctypes import *
class barley_amount(Union):
_fields_ = [
("barley_long", c_long),
("barley_int", c_int),
("barley_char", c_char * 8),
]
value = raw_input("Enter the amount of barley to put into the beer vat:")
my_barley = barley_amount(int(value))
print "Barley amount as a long: %ld" % my_barley.barley_long
print "Barley amount as an int: %d" % my_barley.barley_int
print "Barley amount as a char: %s" % my_barley.barley_char