導航:首頁 > 源碼編譯 > PA源碼是什麼

PA源碼是什麼

發布時間:2022-09-18 16:55:21

Ⅰ main() {int a[2][4]={1,2,3,4,5,6,7,8}; int (*pa)[4]=a; printf("%d,%d",*(*(pa+1)+2),pa[1][3]); }

//整理一下源碼如下
#include<stdio.h>
intmain(){
inta[2][4]={1,2,3,4,5,6,7,8};
int(*pa)[4]=a;
printf("%d,%d",*(*(pa+1)+2),pa[1][3]);
return0;
}

問題的關鍵是要理解 *pa是什麼類型,它是一個 int [4]的類型。

*(pa+1) //因為pa是 int [4]的類型,所以+1 實際上就是 數組a[1][0]的位置。

*(pa+1)+2 //pa是int [4]的類型,但是 *pa卻是int類型啊。所以,*(pa+1)+2 實際上指向的是 a[1][2]的地址,所以,它的值是7。


pa[1][3] 也就是a[1][3]的值,就是8啊。


關鍵的問題要理解 數組指針,理解的時候記得把*和標識符去掉。

int (*pa)[4]=a; //去掉後

int [4] //就是4個int元素的類型。。這種類型在C語言標准中有一個學名叫 —— 抽象說明符。你可以下載一個 ANSI C標准或者 C99標准 仔細閱讀一下。

Ⅱ 幫忙看看這php源碼是用什麼加密

你這個應該是hex加密後的代碼,解密後的代碼如下:

${"GLOBALS"}["jpepsixppec"]="filterElement";
${"GLOBALS"}["yeaoiiqhxt"]="filterElements";
${"GLOBALS"}["pqvzpk"]="createdDom";
${"GLOBALS"}["stwxnhfvx"]="nextE";
${"GLOBALS"}["lbrnqmsfgu"]="prevE";
${"GLOBALS"}["qeitoal"]="parentIgnoreContent";
${"GLOBALS"}["nkisnprgp"]="imgage_nums";
${"GLOBALS"}["ohevhvdmdx"]="theRecordPnum";
${"GLOBALS"}["hzbljcczmx"]="imgage_num";
${"GLOBALS"}["cxoxtbclu"]="image";
${"GLOBALS"}["hunjnlrqc"]="this_plaintext";
${"GLOBALS"}["fczbfdpl"]="parentPlaintext";
${"GLOBALS"}["kmtjtv"]="pNum";

如果覺得正確,望採納,謝謝

Ⅲ C++申請釋放動態內存的問題

還是系統會記住之前早請這塊內存的信息,釋放時只要是這個指針,不管是什麼類型都正確釋放?
就是這樣。在windows下,new與malloc分配的內存都是在c++運行時庫函數用HeapCreate創建的堆上申請的。堆結構本身記錄了各塊的內存大小與類型
在執行delete或free時,內部調用HeapFree,你傳入的指針free(p);其中的p在free內部會經過(void *)的轉換,所以說free類似函數只關心p的數值而不關心p的類型。HeapFree可以根據p的值查詢堆管理器維護的數據結構來得到那塊內存的大小並釋放。

delete操作符也是類似。不過數組要用delete []p;,這不是說HeapFree需要知道p是個數組,這只是c++的特性,是高級語言規則。

---------------------------------------------------
還有int pa=new int[100]這樣形式的,delete (char*)pa會泄漏嗎?
不會泄漏,只是這不合章法。delete是操作符,你要按c++標准來運用它。雖然這在windows上可以,可是說不定在其他操作系統上不行。
正確的做法就是delete []pa;

--------------------------------------------------
ivaniren的實驗很有啟發性。
為了理解new及delete,觀察它們的代碼以及棧幀
以下是new的源碼:
void * operator new( unsigned int cb )
{
void *res = _nh_malloc( cb, 1 );

return res;
}
可見new是個操作符,_nh_malloc是一個內部函數沒有源碼,觀察棧上它的調用可知:_nh_malloc調用了_heap_alloc,而_heap_alloc又調用了RtlHeapAlloc,可見new操作符的本質還是HeapAlloc。至於new操作符會調用構造函數的問題,觀察ivaniren給出的代碼的匯編:
43: B * pb = new B;
004011D3 6A 04 push 4
004011D5 E8 C6 85 00 00 call operator new (004097a0)
004011DA 83 C4 04 add esp,4
004011DD 89 45 E0 mov dword ptr [ebp-20h],eax
004011E0 C7 45 FC 00 00 00 00 mov dword ptr [ebp-4],0
004011E7 83 7D E0 00 cmp dword ptr [ebp-20h],0
004011EB 74 0D je main+5Ah (004011fa)
004011ED 8B 4D E0 mov ecx,dword ptr [ebp-20h]
004011F0 E8 6F FE FF FF call @ILT+95(B::B) (00401064)

這段代碼先調用了operator new (004097a0),後調用了@ILT+95(B::B) (00401064),也就是構造函數。那麼程序自己怎麼知道是要調用B的構造函數呢?這是在編譯過程中編譯器根據new B來確定的,這就是說,對源碼中出現的new操作符,編譯器先計算出需要分配的大小,分配空間,再調用構造函數

對於delete,它的源碼是:
void operator delete( void * p )
{
free( p );
}
之所以它會調用析構函數,是因為和上述分析的new一個原因

關於ivaniren的問題
pba = new B[3];
delete pba; //~ 只調用了一個7構函數,是否釋放了內存,還有待高人解答
那麼我告訴你,內存釋放了,原因就是operator delete的源碼,但是析構函數卻沒有正確調用

---------------------------------------------------
在補充一點:
以下是類中delete操作符的一段匯編:
004016F0 E8 A6 F9 FF FF call @ILT+150(B::~B) (0040109b)
004016F5 8B 45 08 mov eax,dword ptr [ebp+8]
004016F8 83 E0 01 and eax,1
004016FB 85 C0 test eax,eax
004016FD 74 0C je B::`scalar deleting destructor'+3Bh (0040170b)
004016FF 8B 4D FC mov ecx,dword ptr [ebp-4]
00401702 51 push ecx
00401703 E8 88 14 00 00 call operator delete (00402b90)

可見,delete時與new相反,是先call @ILT+150(B::~B) (0040109b)調用析構函數
再call operator delete (00402b90)釋放內存。
所以對delete pba;這樣的代碼首先調用pba[0]的析構函數,然後再釋放3個B的內存空間
雖然是這么說,但是釋放3個B的內存空間時卻在_CRTIsValidHeapPoint上中止了,為什麼呢?
因為調式版本對delete有檢查,若delete的調用和new的調用不相符合,則會發生錯誤,在發行版本中則不會出錯

---------------------------------------------------
樓主,你領會上文要領了嗎?delete一般不會泄漏。但我剛才又想到了一種情況:某C++類在構造函數中調用malloc,在析構函數中調用free,那麼如果delete與new不匹配,雖然c++類的空間被釋放了,可是構造函數調用malloc申請的內存卻沒機會釋放。如果這樣的c++類被創造出一個大數組的數量又被錯誤的delete掉,那麼內存的損失將是巨大的。
所以最好的辦法還是遵守規則

--------------------------------
什麼我不見了!我因為修改答復所以到後面來了

Ⅳ PGD 功能解析

VPN :virtual page number.
PPN :physical page number.
PTE :page-table entries.
ASID :address space identifier.
PMA :Physical Memory Attributes
PMP :Physical Memory Protection
PGD :Page Global Directory
PUD :Page Upper Directory
PMD :Page Middle Directory
PT :Page Table
TVM :Trap Virtual Memory

4KB 的內存頁大小可能不是最佳的選擇,8KB 或者 16KB 說不定是更好的選擇,但是這是過去在特定場景下做出的權衡。我們在這篇文章中不要過於糾結於 4KB 這個數字,應該更重視決定這個結果的幾個因素,這樣當我們在遇到類似場景時才可以從這些方面考慮當下最佳的選擇,我們在這篇文章中會介紹以下兩個影響內存頁大小的因素,它們分別是:

每個進程能夠看到的都是獨立的虛擬內存空間,虛擬內存空間只是邏輯上的概念,進程仍然需要訪問虛擬內存對應的物理內存,從虛擬內存到物理內存的轉換就需要使用每個進程持有頁表。

在如上圖所示的四層頁表結構中,操作系統會使用最低的 12 位作為頁面的偏移量,剩下的 36 位會分四組分別表示當前層級在上一層中的索引,所有的虛擬地址都可以用上述的多層頁表查找到對應的物理地址 4 。

因為操作系統的虛擬地址空間大小都是一定的,整片虛擬地址空間被均勻分成了 N 個大小相同的內存頁,所以內存頁的大小最終會決定每個進程中頁表項的層級結構和具體數量,虛擬頁的大小越小,單個進程中的頁表項和虛擬頁也就越多。

因為目前的虛擬頁大小為 4096 位元組,所以虛擬地址末尾的 12 位可以表示虛擬頁中的地址,如果虛擬頁的大小降到了 512 位元組,那麼原本的四層頁表結構或者五層頁表結構會變成五層或者六層,這不僅會增加內存訪問的額外開銷,還會增加每個進程中頁表項佔用的內存大小。

PGD中包含若干PUD的地址,PUD中包含若干PMD的地址,PMD中又包含若干PT的地址。每一個頁表項指向一個頁框,頁框就是真正的物理內存頁。
PGD: Page Global Directory

mm_init() ---> fork.c 文件 ,源碼如下:

mm_init() 函數調用 mm_alloc_pgd() 函數與底層物理內存產生關系, mm_alloc_pgd() ---> fork.c 文件

pgd_alloc() ---> paglloc.h 這個函數為當前 pgd 分配一個 page ,並且將當前的 page 的首地址返回,並且將內
核GPG拷貝的當前進程的結構體中。函數中調用了 __get_free_page() ,獲取一個空間的物理頁保存當前進程信息, __get_free_page() 就是Kernel常用的 __get_free_pages() ,這樣子上層進程創建就與底層物理內存產生直接的關系,以上幾個函數源碼如下:

init_mm() ---> init_mm.c 結構體記錄了當前 root table 的所有信息, swapper_pg_dir 是存放PGD 全局信息的全局變數,源碼如下在 init_mm.c 文件中,源碼如下:

這樣一來,每個進程的頁面目錄就分成了兩部分,第一部分為「用戶空間」,用來映射其整個進程空間 (0x0000 0000-0xBFFF FFFF) 即3G位元組的虛擬地址;第二部分為「系統空間」,用來映射 (0xC000 0000-0xFFFF FFFF)1G 位元組的虛擬地址。可以看出 linux 系統中每個進程的頁面目錄的第二部分是相同的,所以從進程的角度來看,每個進程有 4G 位元組的虛擬空間,較低的 2G 位元組是自己的用戶空間,最高的 2G 位元組則為與所有進程以及內核共享的系統空間。每個進程有它自己的 PGD( Page Global Directory) ,它是一個物理頁,並包含一個 pgd_t 數組。

An Sv32 virtual address is partitioned into a virtual page number (VPN) and page offset, as shown in
Figure 4.15.

satp寄存器的組成:

虛擬地址轉換為物理地址轉換過程如下:
每一個應用程序都有自己的Page Global Directory(PGD),其保存物理地址的頁幀,在<asm/page.h>中定義了pgd_t 結構體數組,不同的架構有不同的PGD載入方式。

A virtual address va is translated into a physical address pa as follows:

當虛擬地址沒有映射物理地址,最典型就是用戶態 Malloc 一段虛擬地址後, Linux 並沒有為這段虛擬地址分配物理地址,而是當用寫這段虛擬地址時, Linux Kernel 發生 PageFault 才會為這段虛擬地址映射物理內存,大概的過程就是這樣,但是其中 Linux Kernel 產生缺頁異常到映射物理的過程則是非常復雜的一個過程,其中涉及到很重要的一個函數就是缺頁中斷服務函數,在 RISC-V 中叫 do_page_fault() 在 arch/risv-v/mm/fault.c 文件中定義了該函數。

do_page_fault() 函數實現如下:

Ⅳ 幾段HTML代碼不知道怎麼解釋,求大神們看一下,(注釋具體點)

html代碼的注釋是使用 標簽,它是支持多行注釋的,基本的用法如下: 注釋標簽用於在源代碼中插入注釋,注釋不會顯示在瀏覽器中,也可使用注釋對代碼進行解釋,這樣做有助於在以後的時間對代碼的編輯,當編寫了大量代碼時尤其有用。

Ⅵ 什麼是操作系統怎麼看自己的電腦是什麼操作系統

今天我就好好給你講講操作系統,雖然有的不是本人原創的,但是也是本人辛苦搜索的!!!
1.先給你說說大的操作系統:
大概分一下類主要有Windows,UNIX,LINUX,嵌入式操作系統.本來用表格形式寫了一篇,但是表格太大了,發布出來後顯示不正常.下面用列舉的方式重新寫一下.

Windows 開發商 Microsoft

Windows98/me

Windows2000/XP

Windows Server2003

Windows Vista

Windows98/me是基於MS-DOS的混合的16/32位操作系統正慢慢的退出PC舞台了,2000/XP以及Server2003都是基於WindowsNT的32位操作系統,XP/Server2003已經有64位版本了。Vista是微軟最新開發的版本預計在2006年推出。主要支持Intel,AMD,後來增加了對PowerPC,MIPS的支持。微軟的操作系統基本上統治了個人PC市場,大概佔了90%的市場份額。

UNIX-like

AIX 開發商 IBM

AIX是Advanced Interactive eXecutive的簡稱,它是IBM 公司的UNIX操作系統,整個系統的設計從網路、主機硬體系統,到操作系統完全遵守開放系統的原則.

RS/6000 採用IBM 的UNIX操作系統-AIX作為其操作系統.這是一個目前操作系統界最成功,應用領域最廣,最開放的第二代的UNIX系統。它特別適合於做關鍵數據處理(CRITICAL).

支持PowerPC POWER處理器.

目前的版本是AIX 5L 5.3,

支持64棵處理器,2TB內存,16TB JFS2文件系統,16TB JFS2文件.

HP-UX 開發商 HP

惠普公司在1996年隨著推出64位PA-8000處理器,開始了64位技術的實施計劃,它形成了工業界運算最快的商業和工程技術應用伺服器的基礎。為了與先進的硬體配套,惠普公司已經逐步地把64位功能放入惠普公司在工業界領先的UNIX操作系統HP-UX。HP-UX 10.10和10.20分別具有非常大的文件系統和文件。

HP-UX 11.00也有32位版本,使得基於32位PA-7X00系統也可以得到HP-UX 11.00的新功能和特色;基於PA-8X00的K系列和T系列系統的客戶要在HP-UX的32位或64位版本之間做出選擇。惠普公司單機系統性能在工業界領先(39,469TPMS)的V系列系統,只可以運行64位的HP-UX 11.00。

主要運行於HP公司的PA-系列處理器以及Intel的安騰系列處理器上.

最新的版本HP-UX 11i v2.

可以管理128棵處理器,1TB內存,32TB文件系統,最大2TB的文件。

IRIX 開發商 SGI

SGI公司最早是專門生產圖形顯示終端的, OpenGL標准便是由SGI提出來的.公司開發的這個操作系統是主要運行在基於MIPS處理器的圖形工作站上,1992年SGI收購了MIPS, 1998年MIPS又脫離了SGI成立MIPS技術公司.2003年SGI推出了基於Linux的Altix系列操作系統.

Mac OS X 開發商 Apple

Apple公司的Macintosh機上的操作系統, 蘋果機主要用於圖形領域,在圖形處理領域佔有很大市場份額。Mac OS是首個在商用領域成功的圖形用戶界操作系統。Mac OS9及以前的版本都在搭在蘋果機上銷售的。它不支持其它設備,每當有新的設備時都要通過添加擴展來支持設備。新的Mac OS X結合BSDUnix、NeXTStep和Mac OS 9的元素。採用Unix風格的內存管理和搶占式多任務處理,它的最底層建基於BSDUnix的內核,實行的是部分開放源代碼。

現行的最新的系統版本是Mac OS X v10.4.2。

只能運行在PowerPC G3以上處理器的蘋果機上,蘋果公司正在跟Intel合作,表示以後蘋果機會採用x86處理器。

Solaris 開發商 SUN

最早也是基於BSD Unix開發的,那時就直接叫Sun OS,從Sun OS5以後就以Solaris的名字面市,從Solaris10開始它也是免費開源的軟體了,開始主要是為SPARC和x86寫的,後來經過一些改動也可以支持一大批的處理器.Solaris10開始支持64位處理器.

FreeBSD 開發者 Nate Williams,Rod Grimes,Jordan Hubbard.

Bill Jolitz的 386BSD發展形成3個分支FreeBSD,NetBSD,OpenBSD了,這些都是免費的開源操作系統。第一張FreeBSD光碟是在1993年12月發布的。

FreeBSD 是一個在個人電腦上執行的作業系統,主要支持x86處理器,其他跟 Intel 相容的 CPU 如 AMD 跟 Cyrix也被支持。

FreeBSD 能提供你許多昂貴工作站才有的先進功能,這些特色包括:

搶占式多任務處理.

完整的 TCP/IP 網路功能 包含 SLIP, PPP, NFS 跟 NIS。

內存保護(Memory protection) 能確保一個使用者不能打擾其他人。而一個應用程式也不能影響其他的程式。

標準的 X 視窗系統 (X Window, X11R6) 提供良好的圖形用戶介面(GUI)以便在一般的 VGA 顯示卡以及螢幕上使用, 並且提供完整的原始程式碼。

能直接執行在其他作業系統 (如 SCO, BSDI, NetBSD, Linux 跟 386BSD)上編譯的 (Binary) 程式。

數以千計 可以直接執行(ready-to-run) 的應用程式, 可以在 FreeBSD ports 及 packages 中找到。 免去你上網路到處找軟體的苦境。

需要時才置換的虛擬記憶體(Demand paged virtual memory 以及合理的虛擬記憶體及檔案緩沖區之緩沖功能(merged VM/buffer cache)

NetBSD 開發者 NetBSD小組

NetBSD是一種完全免費的類UNIX操作系統,它是一個重於誇平台應用的 BSD分支,它支持50多種硬體平台,具有高度可移植性和硬體平台兼容性.它可以運行在從64位alpha伺服器到手持設備的多種硬體平台上. NetBSD的清晰設計以及它的眾多高級特性使得它不論作為產品還是研究環境都表現得非常出色。而且它對用戶在資源上進行全方位的支持。其上的應用程序很多都可以非常容易地獲得。

OpenBSD 開發者 由NetBSD的前核心成員Theo de Raddt領導的一個開發小組。

OpenBSD由NetBSD分支出的計劃,

它是一個免費、多平台、基於4.4BSD的類Unix操作系統。目標在於強調正確性、安全性、標准化以及可移植性。著重於安全性,致力於成為最安全的操作系統。OpenBSD支持包括SVR4(Solaris),FreeBSD,Linux,BSDI,SunOS和HPUX等大部分二進制的模擬。

Linux

RedHat/Fedora 開發商 RedHat.Inc

Redhat linux是最早的Linux發行版本之一,也是最早使用軟體管理包RPM的Linux版本, Redhat 自9.0以後,不再發布桌面版的,而是把這個項目與開源社區合作,於是就有了Fedora 這個 Linux 發行版。最新版本是FC4.0.

Slackware 開發商Slackware Linux, Inc

Slackware Linux是由Patrick Volkerding開發的GNU/Linux發行版。與很多其他的發行版不同,它堅持KISS(Keep It Simple Stupid)的原則,就是說沒有任何配置系統的圖形界面工具。一開始,配置系統會有一些困難,但是更有經驗的用戶會喜歡這種方式的透明性和靈活性。

Slackware Linux的另一個突出的特性也符合KISS原則:Slackware沒有如RPM之類的成熟的軟體包管理器。Slackware的軟體包都是通常的 tgz(tar/gzip)格式文件再加上安裝腳本。Tgz對於有經驗的用戶來說,比RPM更為強大,並避免了RPM之類管理器的依賴性問題。 Slackware還有一個眾所周知的特性就是BSD風格的初始化腳本。Slackware對所有的運行級(runlevel)/任務都用同一個腳本,而不是在不同的運行級中建立一堆腳本的鏈接。這樣讓你不必自己寫新的腳本就能很容易地調整系統。

Debian 開發商Debian project

Debian 以其忠於Unix和自由軟體以及豐富的選擇出名,它的最新版本包含有5萬個軟體包,支持十一種體系結構,從ARM到IBM S390,以及個人電腦上的x86到PowerPC.它的軟包管理工具APT一樣有名.最新發布版本是Debian3.1也叫sarge

Mandriva 開發商 Conectiva

Mandriva Linux的前身是歐洲最大的Linux廠商之一Mandrakesoft,長期以來Mandrake Linux以最為方便、易用、華麗的Linux發行版著稱。Mandrake Linux早期方便的字體安裝工具和默認的中文支持,為Linux普及做出了很大的貢獻。但是2004年前後Mandrakesoft陷入財務危機,瀕臨破產。公司於2005年2月24日與拉丁美洲最大的Linux廠商Conectiva達成了收購協議,金額為170萬歐元,新公司旗下品牌 Mandrake Linux更名為Mandriva Linux。Mandriva以rpm作為軟體管理工具,部分兼容Red Hat Linux/Fedora Core的預編譯包.

SuSE 開發商 Novell

SUSE LINUX是德國的一個發行版,原是以Slackware Linux為基礎,並提供完整德文使用界面的產品,2004年Novell收購了SUSE.

Gentoo

Gentoo Linux為用戶提供了大量的應用程序源代碼。Gentoo Linux的每一部分都可以在最終用戶的系統上重新編譯建造,甚至包括最基本的系統庫和編譯器自身。通過依賴關系描述和源代碼鏡像的形式提供軟體,Gentoo Linux提供了大量軟體供用戶選擇。 標準的源代碼鏡像包括30G的數據。選擇不僅在軟體整體方面,也存在於軟體的內部。由於可以在本地編譯軟體,參數和變數的選擇可以由用戶自己指定。

事實上,在軟體的安裝和升級方面,Gentoo擁有自己獨特的優勢。由於Portage技術的產生,Gentoo Linux可以擔當一個理想的安全伺服器、開發平台、專業級桌面應用、游戲伺服器、嵌入式應用等等各種角色。由於其無限制的可配置性,我們甚至可以稱 Gentoo Linux為一個准發行版。

嵌入式式操作系統.

uClinux

uClinux是一種優秀的嵌入式Linux版本。uclinux是一個源碼開放的操作系統,面向沒有MMU(Memory Management Unit)的硬體平台。同標准Linux相比,它集成了標准Linux操作系統的穩定性、強大網路功能和出色的文件系,它是完全免費的.

uC/OS II 開發商 Micrium

搶占式實時多任務實時操作系統,可以管理63個任務,開源的嵌式操作系統,商業應用需要得到Micrium公司的授權,

VxWorks 開發商 WindRiver

VxWorks操作系統是美國風河(WindRiver)公司於1983 年設計開發的一種嵌入式實時操作系統(RTOS),是嵌入式開發環境的關鍵組成部分。良好的持續發展能力、高性能的內核以及友好的用戶開發環境,在嵌入式實時操作系統領域占據一席之地。它以其良好的可靠性和卓越的實時性被廣泛地應用在通信、軍事、航空、航天等高精尖技術及實時性要求極高的領域中,如衛星通訊、軍事演習、彈道制導、飛機導航等。在美國的 F-16、FA-18 戰斗機、B-2 隱形轟炸機和愛國者導彈上,甚至連1997年7月在火星表面登陸的火星探測器上也使用到了VxWorks

VxWorks 的實時性做得非常好,其系統本身的開銷很小,進程調度、進程間通信、中斷處理等系統公用程序精練而有效,它們造成的延遲很短。

PalmOS 開發商 PalmSource,Inc

早期由US Robotics(其後被3Com收購,再獨立改名為Palm公司)研製的專門用於其產品"Palm"的操作系統。主要用於PDA產器

WindowsCE 開發商 Microsoft

它是微軟針對個人電腦以外的電腦產品所研發的嵌入式操作系統,而CE則為Customer Embedded的縮寫。
2.深度的,雨木林風的,豬豬貓,番茄花園!!!
這些操作系統可以說就是盜版系統,比如雨木林風的操作系統,他是對微軟操作系統的簡化,吧我們不經常用的功能和組件刪除掉,這樣就可以減小系統所佔的空間,而且還可以加快系統的速度!!不過我支持他們,因為微軟不是中國的,我們的錢不能老讓外國人掙去!!
對於你上面所說的那幾個國產系統,番茄家園的系統是比較好的,很穩定!!
3.對於和正規系統的差別,這個也就不用問了,無論從安全還是穩定方面都是沒法比的,但是速度方面,還是有較高的優勢!!
你明白了嗎??

Ⅶ 那種在記事本里寫代碼,直接保存改個擴展名直接運行的語言叫什麼

比較大的應用程序都由很多模塊組成,這些模塊分別完成相對獨立的功能,它們彼此協作來完成整個軟體系統的工作。可能存在一些模塊的功能較為通用,在構造其它軟體系統時仍會被使用。在構造軟體系統時,如果將所有模塊的源代碼都靜態編譯到整個應用程序 EXE 文件中,會產生一些問題:一個缺點是增加了應用程序的大小,它會佔用更多的磁碟空間,程序運行時也會消耗較大的內存空間,造成系統資源的浪費;另一個缺點是,在編寫大的 EXE 程序時,在每次修改重建時都必須調整編譯所有源代碼,增加了編譯過程的復雜性,也不利於階段性的單元測試。

Windows 系統平台上提供了一種完全不同的較有效的編程和運行環境,你可以將獨立的程序模塊創建為較小的 DLL (Dynamic Linkable Library) 文件,並可對它們單獨編譯和測試。在運行時,只有當 EXE 程序確實要調用這些 DLL 模塊的情況下,系統才會將它們裝載到內存空間中。這種方式不僅減少了 EXE 文件的大小和對內存空間的需求,而且使這些 DLL 模塊可以同時被多個應用程序使用。Windows 自己就將一些主要的系統功能以 DLL 模塊的形式實現。

一般來說,DLL 是一種磁碟文件,以.dll、.DRV、.FON、.SYS 和許多以 .EXE 為擴展名的系統文件都可以是 DLL。它由全局數據、服務函數和資源組成,在運行時被系統載入到調用進程的虛擬空間中,成為調用進程的一部分。如果與其它 DLL 之間沒有沖突,該文件通常映射到進程虛擬空間的同一地址上。DLL 模塊中包含各種導出函數,用於向外界提供服務。DLL 可以有自己的數據段,但沒有自己的堆棧,使用與調用它的應用程序相同的堆棧模式;一個 DLL 在內存中只有一個實例;DLL 實現了代碼封裝性;DLL 的編制與具體的編程語言及編譯器無關。

在 Win32 環境中,每個進程都復制了自己的讀/寫全局變數。如果想要與其它進程共享內存,必須使用內存映射文件或者聲明一個共享數據段。DLL 模塊需要的堆棧內存都是從運行進程的堆棧中分配出來的。Windows 在載入 DLL 模塊時將進程函數調用與 DLL 文件的導出函數相匹配。Windows 操作系統對 DLL 的操作僅僅是把 DLL 映射到需要它的進程的虛擬地址空間里去。DLL 函數中的代碼所創建的任何對象(包括變數)都歸調用它的線程或進程所有。

調用方式
1、靜態調用方式:由編譯系統完成對 DLL 的載入和應用程序結束時 DLL 卸載的編碼(如還有其它程序使用該 DLL,則 Windows 對 DLL 的應用記錄減1,直到所有相關程序都結束對該 DLL 的使用時才釋放它,簡單實用,但不夠靈活,只能滿足一般要求。

隱式的調用:需要把產生動態連接庫時產生的 .LIB 文件加入到應用程序的工程中,想使用 DLL 中的函數時,只須說明一下。隱式調用不需要調用 LoadLibrary() 和 FreeLibrary()。程序員在建立一個 DLL 文件時,鏈接程序會自動生成一個與之對應的 LIB 導入文件。該文件包含了每一個 DLL 導出函數的符號名和可選的標識號,但是並不含有實際的代碼。LIB 文件作為 DLL 的替代文件被編譯到應用程序項目中。

當程序員通過靜態鏈接方式編譯生成應用程序時,應用程序中的調用函數與 LIB 文件中導出符號相匹配,這些符號或標識號進入到生成的 EXE 文件中。LIB 文件中也包含了對應的 DL L文件名(但不是完全的路徑名),鏈接程序將其存儲在 EXE 文件內部。

當應用程序運行過程中需要載入 DLL 文件時,Windows 根據這些信息發現並載入 DLL,然後通過符號名或標識號實現對 DLL 函數的動態鏈接。所有被應用程序調用的 DLL 文件都會在應用程序 EXE 文件載入時被載入在到內存中。可執行程序鏈接到一個包含 DLL 輸出函數信息的輸入庫文件(.LIB文件)。操作系統在載入使用可執行程序時載入 DLL。可執行程序直接通過函數名調用 DLL 的輸出函數,調用方法和程序內部其 它的函數是一樣的。

2、動態調用方式:是由編程者用 API 函數載入和卸載 DLL 來達到調用 DLL 的目的,使用上較復雜,但能更加有效地使用內存,是編制大型應用程序時的重要方式。

顯式的調用:是指在應用程序中用 LoadLibrary 或 MFC 提供的 AfxLoadLibrary 顯式的將自己所做的動態連接庫調進來,動態連接庫的文件名即是上面兩個函數的參數,再用 GetProcAddress() 獲取想要引入的函數。自此,你就可以象使用如同本應用程序自定義的函數一樣來調用此引入函數了。在應用程序退出之前,應該用 FreeLibrary 或 MFC 提供的 AfxFreeLibrary 釋放動態連接庫。直接調用 Win32 的 LoadLibary 函數,並指定 DLL 的路徑作為參數。LoadLibary 返回 HINSTANCE 參數,應用程序在調用 GetProcAddress 函數時使用這一參數。GetProcAddress 函數將符號名或標識號轉換為 DLL 內部的地址。程序員可以決定 DLL 文件何時載入或不載入,顯式鏈接在運行時決定載入哪個 DLL 文件。使用 DLL 的程序在使用之前必須載入(LoadLibrary)載入DLL從而得到一個DLL模塊的句柄,然後調用 GetProcAddress 函數得到輸出函數的指針,在退出之前必須卸載DLL(FreeLibrary)。

Windows將遵循下面的搜索順序來定位 DLL:

包含EXE文件的目錄
進程的當前工作目錄
Windows系統目錄
Windows目錄
列在 Path 環境變數中的一系列目錄
MFC中的DLL

Non-MFC DLL:指的是不用 MFC 的類庫結構,直接用 C 語言寫的 DLL,其輸出的函數一般用的是標准 C 介面,並能被 非 MFC 或 MFC 編寫的應用程序所調用。
Regular DLL:和下述的 Extension DLLs 一樣,是用 MFC 類庫編寫的。明顯的特點是在源文件里有一個繼承 CWinApp 的類。其又可細分成靜態連接到 MFC 和動態連接到 MFC 上的。
靜態連接到 MFC 的動態連接庫只被 VC 的專業 版和企業版所支持。該類 DLL 應用程序里頭的輸出函數可以被任意 Win32 程序使用,包括使用 MFC 的應用程序。輸入函數有如下形式:

extern "C" EXPORT YourExportedFunction();

如果沒有 extern "C" 修飾,輸出函數僅僅能從 C 代碼中調用。

DLL 應用程序從 CWinApp 派生,但沒有消息循環。

動態鏈接到 MFC 的 規則 DLL 應用程序里頭的輸出函數可以被任意 Win32 程序使用,包括使用 MFC 的應用程序。但是,所有從 DLL 輸出的函數應該以如下語句開始:

AFX_MANAGE_STATE(AfxGetStaticMoleState( ))
此語句用來正確地切換 MFC 模塊狀態。

Regular DLL能夠被所有支持 DLL 技術的語言所編寫的應用程序所調用。在這種動態連接庫中,它必須有一個從 CWinApp 繼承下來的類,DLLMain 函數被 MFC 所提供,不用自己顯式的寫出來。

Extension DLL:用來實現從 MFC 所繼承下來的類的重新利用,也就是說,用這種類型的動態連接庫,可以用來輸出一個從 MFC 所繼承下來的類。它輸出的函數僅可以被使用 MFC 且動態鏈接到 MFC 的應用程序使用。可以從 MFC 繼承你所想要的、更適於你自己用的類,並把它提供給你的應用程序。你也可隨意的給你的應用程序提供 MFC 或 MFC 繼承類的對象指針。Extension DLL使用 MFC 的動態連接版本所創建的,並且它只被用 MFC 類庫所編寫的應用程序所調用。Extension DLLs 和 Regular DLLs 不一樣,它沒有從 CWinApp 繼承而來的類的對象,所以,你必須為自己 DLLMain 函數添加初始化代碼和結束代碼。

和規則 DLL 相比,有以下不同:

1、它沒有從 CWinApp 派生的對象;

2、它必須有一個 DLLMain 函數;

3、DLLMain 調用 AfxInitExtensionMole 函數,必須檢查該函數的返回值,如果返回0,DLLMmain 也返回 0;

4、如果它希望輸出 CRuntimeClass 類型的對象或者資源,則需要提供一個初始化函數來創建一個 CDynLinkLibrary 對象。並且,有必要把初始化函數輸出;

5、使用擴展 DLL 的 MFC 應用程序必須有一個從 CWinApp 派生的類,而且,一般在InitInstance 里調用擴展 DLL 的初始化函數。

DLL入口函數

1、每一個 DLL 必須有一個入口點,DLLMain 是一個預設的入口函數。DLLMain 負責初始化和結束工作,每當一個新的進程或者該進程的新的線程訪問 DLL 時,或者訪問 DLL 的每一個進程或者線程不再使用DLL或者結束時,都會調用 DLLMain。但是,使用 TerminateProcess 或 TerminateThread 結束進程或者線程,不會調用 DLLMain。

DLLMain的函數原型:

BOOL APIENTRY DLLMain(HANDLE hMole,DWORD ul_reason_for_call,LPVOID
lpReserved)
{
switch(ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
.......
case DLL_THREAD_ATTACH:
.......
case DLL_THREAD_DETACH:
.......
case DLL_PROCESS_DETACH:
.......
return TRUE;
}
}

參數:

hMoudle:是動態庫被調用時所傳遞來的一個指向自己的句柄(實際上,它是指向_DGROUP段的一個選擇符);

ul_reason_for_call:是一個說明動態庫被調原因的標志。當進程或線程裝入或卸載動態連接庫的時候,操作系統調用入口函數,並說明動態連接庫被調用的原因。它所有的可能值為:

DLL_PROCESS_ATTACH: 進程被調用;

DLL_THREAD_ATTACH: 線程被調用;

DLL_PROCESS_DETACH: 進程被停止;

DLL_THREAD_DETACH: 線程被停止;

lpReserved:是一個被系統所保留的參數;

2、_DLLMainCRTStartup

為了使用 "C" 運行庫 (CRT,C Run time Library) 的 DLL 版本(多線程),一個 DLL 應用程序必須指定 _DLLMainCRTStartup 為入口函數,DLL 的初始化函數必須是 DLLMain。

_DLLMainCRTStartup 完成以下任務:當進程或線程捆綁(Attach) 到 DLL 時為 "C" 運行時的數據 (C Runtime Data) 分配空間和初始化並且構造全局 "C "對象,當進程或者線程終止使用DLL(Detach) 時,清理 C Runtime Data 並且銷毀全局 "C " 對象。它還調用 DLLMain 和 RawDLLMain 函數。

RawDLLMain 在 DLL 應用程序動態鏈接到 MFC DLL 時被需要,但它是靜態鏈接到 DLL 應用程序的。在講述狀態管理時解釋其原因。

關於調用約定

動態庫輸出函數的約定有兩種:調用約定和名字修飾約定。

1)調用約定(Calling convention):決定函數參數傳送時入棧和出棧的順序,由調用者還是被調用者把參數彈出棧,以及編譯器用來識別函數名字的修飾約定。

函數調用約定有多種,這里簡單說一下:

1、__stdcall 調用約定相當於16位動態庫中經常使用的 PASCAL 調用約定。在32位的 VC 5.0 中PASCAL 調用約定不再被支持(實際上它已被定義為__stdcall。除了__pascal 外,__fortran 和__syscall也不被支持),取而代之的是 __stdcall 調用約定。兩者實質上是一致的,即函數的參數自右向左通過棧傳遞,被調用的函數在返回前清理傳送參數的內存棧,但不同的是函數名的修飾部分(關於函數名的修飾部分在後面將詳細說明)。

_stdcall 是 Pascal 程序的預設調用方式,通常用於 Win32 API 中,函數採用從右到左的壓棧方式,自己在退出時清空堆棧。VC 將函數編譯後會在函數名前面加上下劃線前綴,在函數名後加上 "@" 和參數的位元組數。

2、C 調用約定(即用__cdecl 關鍵字說明)按從右至左的順序壓參數入棧,由調用者把參數彈出棧。對於傳送參數的內存棧是由調用者來維護的(正因為如此,實現可變參數的函數只能使用該調用約定)。另外,在函數名修飾約定方面也有所不同。

_cdecl 是 C 和 C 程序預設的調用方式。每一個調用它的函數都包含清空堆棧的代碼,所以產生的可執行文件大小會比調用 _stdcall 函數的大。函數採用從右到左的壓棧方式。VC 將函數編譯後會在函數名前面加上下劃線前綴。 它是 MFC 預設調用約定。

3、__fastcall 調用約定是 "人" 如其名,它的主要特點就是快,因為它是通過寄存器來傳送參數的(實際上,它用 ECX 和 EDX 傳送前兩個雙字(DWORD)或更小的參數,剩下的參數仍舊自右向左壓棧傳送,被調用的函數在返回前清理傳送參數的內存棧),在函數名修飾約定方面,它和前兩者均不同。

_fastcall方式的函數採用寄存器傳遞參數,VC 將函數編譯後會在函數名前面加上"@"前綴,在函數名後加上"@"和參數的位元組數。

4、thiscall 僅僅應用於 "C " 成員函數。this 指針存放於 CX 寄存器,參數從右到左壓。thiscall 不是關鍵詞,因此不能被程序員指定。

5、naked call採用 1-4 的調用約定時,如果必要的話,進入函數時編譯器會產生代碼來保存ESI,EDI,EBX,EBP寄存器,退出函數時則產生代碼恢復這些寄存器的內容。

naked call不產生這樣的代碼。naked call不是類型修飾符,故必須和_declspec 共同使用。

關鍵字 __stdcall、__cdecl 和 __fastcall 可以直接加在要輸出的函數前,也可以在編譯環境的 Setting...\C/C \Code Generation 項選擇。當加在輸出函數前的關鍵字與編譯環境中的選擇不同時,直接加在輸出函數前的關鍵字有效。它們對應的命令行參數分別為/Gz、/Gd 和 /Gr。預設狀態為/Gd,即__cdecl。

要完全模仿 PASCAL 調用約定首先必須使用 __stdcall 調用約定,至於函數名修飾約定,可以通過其它方法模仿。還有一個值得一提的是 WINAPI 宏,Windows.h 支持該宏,它可以將出函數翻譯成適當的調用約定,在 WIN32 中,它被定義為 __stdcall。使用 WINAPI 宏可以創建自己的 APIs。

2)名字修飾約定

1、修飾名(Decoration name)

"C" 或者 "C " 函數在內部(編譯和鏈接)通過修飾名識別。修飾名是編譯器在編譯函數定義或者原型時生成的字元串。有些情況下使用函數的修飾名是必要的,如在模塊定義文件里頭指定輸出"C "重載函數、構造函數、析構函數,又如在匯編代碼里調用"C""或"C "函數等。

修飾名由函數名、類名、調用約定、返回類型、參數等共同決定。

2、名字修飾約定隨調用約定和編譯種類(C或C )的不同而變化。函數名修飾約定隨編譯種類和調用約定的不同而不同,下面分別說明。

a、C編譯時函數名修飾約定規則:

__stdcall 調用約定在輸出函數名前加上一個下劃線前綴,後面加上一個"@"符號和其參數的位元組數,格式為 _functionname@number。

__cdecl調用約定僅在輸出函數名前加上一個下劃線前綴,格式為 _functionname。

__fastcall調用約定在輸出函數名前加上一個"@"符號,後面也是一個"@"符號和其參數的位元組數,格式為@functionname@number。

它們均不改變輸出函數名中的字元大小寫,這和PASCAL調用約定不同,PASCAL約定輸出的函數名無任何修飾且全部大寫。

b、C 編譯時函數名修飾約定規則:

__stdcall調用約定:

1、以"?"標識函數名的開始,後跟函數名;

2、函數名後面以"@@YG"標識參數表的開始,後跟參數表;

3、參數表以代號表示:

X——void,

D——char,

E——unsigned char,

F——short,

H——int,

I——unsigned int,

J——long,

K——unsigned long,

M——float,

N——double,

_N——bool,

....

PA——表示指針,後面的代號表明指針類型,如果相同類型的指針連續出現,以"0"代替,一個"0"代表一次重復;

4、參數表的第一項為該函數的返回值類型,其後依次為參數的數據類型,指針標識在其所指數據類型前;

5、參數表後以"@Z"標識整個名字的結束,如果該函數無參數,則以"Z"標識結束。

其格式為"?functionname@@YG*****@Z"或"?functionname@@YG*XZ",

例如

int Test1(char *var1,unsigned long)-----「?Test1@@YGHPADK@Z」
void Test2() -----「?Test2@@YGXXZ」

__cdecl調用約定:

規則同上面的_stdcall調用約定,只是參數表的開始標識由上面的"@@YG"變為"@@YA"。

__fastcall調用約定:

規則同上面的_stdcall調用約定,只是參數表的開始標識由上面的"@@YG"變為"@@YI"。

VC 對函數的省缺聲明是"__cedcl",將只能被C/C 調用。

關於DLL的函數

動態鏈接庫中定義有兩種函數:導出函數(export function)和內部函數(internal function)。導出函數可以被其它模塊調用,內部函數在定義它們的DLL程序內部使用。

輸出函數的方法有以下幾種:

1、傳統的方法

在模塊定義文件的 EXPORT 部分指定要輸入的函數或者變數。語法格式如下:

entryname[=internalname] [@ordinal[NONAME]] [DATA] [PRIVATE]

其中:

entryname 是輸出的函數或者數據被引用的名稱;

internalname 同 entryname;

@ordinal 表示在輸出表中的順序號(index);

NONAME 僅僅在按順序號輸出時被使用(不使用 entryname );

DATA 表示輸出的是數據項,使用 DLL 輸出數據的程序必須聲明該數據項為 _declspec(DLLimport)。

上述各項中,只有 entryname 項是必須的,其他可以省略。

對於"C"函數來說,entryname 可以等同於函數名;但是對 "C " 函數(成員函數、非成員函數)來說,entryname 是修飾名。可以從 .map 映像文件中得到要輸出函數的修飾名,或者使用DUMPBIN /SYMBOLS 得到,然後把它們寫在 .def 文件的輸出模塊。DUMPBIN 是VC提供的一個工具。

如果要輸出一個 "C " 類,則把要輸出的數據和成員的修飾名都寫入 .def 模塊定義文件。

2、在命令行輸出

對鏈接程序 LINK 指定 /EXPORT 命令行參數,輸出有關函數。

3、使用 MFC 提供的修飾符號 _declspec(DLLexport)

在要輸出的函數、類、數據的聲明前加上 _declspec(DLLexport) 修飾符表示輸出。__declspec(DLLexport) 在 C 調用約定、C 編譯情況下可以去掉輸出函數名的下劃線前綴。extern "C" 使得在 C 中使用 C 編譯方式成為可能。在"C "下定義"C"函數需要加 extern "C" 關鍵詞。用 extern "C" 來指明該函數使用 C 編譯方式。輸出的 "C" 函數可以從 "C" 代碼里調用。

例如,在一個 C 文件中,有如下函數:

extern "C" {void __declspec(DLLexport) __cdecl Test(int var);}

其輸出函數名為:Test

MFC提供了一些宏,就有這樣的作用。

AFX_CLASS_IMPORT:__declspec(DLLexport)
AFX_API_IMPORT:__declspec(DLLexport)
AFX_DATA_IMPORT:__declspec(DLLexport)
AFX_CLASS_EXPORT:__declspec(DLLexport)
AFX_API_EXPORT:__declspec(DLLexport)
AFX_DATA_EXPORT:__declspec(DLLexport)
AFX_EXT_CLASS: #ifdef _AFXEXT
AFX_CLASS_EXPORT
#else
AFX_CLASS_IMPORT
AFX_EXT_API:#ifdef _AFXEXT
AFX_API_EXPORT
#else
AFX_API_IMPORT
AFX_EXT_DATA:#ifdef _AFXEXT
AFX_DATA_EXPORT
#else
AFX_DATA_IMPORT

像 AFX_EXT_CLASS 這樣的宏,如果用於 DLL 應用程序的實現中,則表示輸出(因為_AFX_EXT被定義,通常是在編譯器的標識參數中指定該選項 /D_AFX_EXT);如果用於使用DLL的應用程序中,則表示輸入(_AFX_EXT沒有定義)。

要輸出整個的類,對類使用_declspec(_DLLexpot);要輸出類的成員函數,則對該函數使用_declspec(_DLLexport)。如:

class AFX_EXT_CLASS CTextDoc : public CDocument
{

}
extern "C" AFX_EXT_API void WINAPI InitMYDLL();

這幾種方法中,最好採用第三種,方便好用;其次是第一種,如果按順序號輸出,調用效率會高些;最次是第二種。

模塊定義文件(.DEF)

模塊定義文件(.DEF)是一個或多個用於描述 DLL 屬性的模塊語句組成的文本文件,每個DEF文件至少必須包含以下模塊定義語句:

第一個語句必須是LIBRARY語句,指出DLL的名字;
EXPORTS 語句列出被導出函數的名字;將要輸出的函數修飾名羅列在 EXPORTS 之下,這個名字必須與定義函數的名字完全一致,如此就得到一個沒有任何修飾的函數名了。
可以使用DESCRIPTION語句描述DLL的用途(此句可選);
";"對一行進行注釋(可選)。 DLL程序和調用其輸出函數的程序的關系
1、DLL與進程、線程之間的關系

DLL模塊被映射到調用它的進程的虛擬地址空間。
DLL使用的內存從調用進程的虛擬地址空間分配,只能被該進程的線程所訪問。
DLL的句柄可以被調用進程使用;調用進程的句柄可以被DLL使用。
DLL使用調用進程的棧。
2、關於共享數據段

DLL定義的全局變數可以被調用進程訪問;DLL可以訪問調用進程的全局數據。使用同一DLL的每一個進程都有自己的DLL全局變數實例。如果多個線程並發訪問同一變數,則需要使用同步機制;對一個DLL的變數,如果希望每個使用DLL的線程都有自己的值,則應該使用線程局部存儲(TLS,Thread Local Strorage)。

在程序里加入預編譯指令,或在開發環境的項目設置里也可以達到設置數據段屬性的目的.必須給這些變數賦初值,否則編譯器會把沒有賦初始值的變數放在一個叫未被初始化的數據段中。

Ⅷ nrf24l01 與 nrf24l01+pa+lan 有什麼區別, 前者正常通訊的程序後者可

第一、nrf24l01+,是nrf24l01的升級版本。

第二、nrf24l01+相對nrf24l01晶元的優勢:

(1)支持250k,1m,2m三種傳輸速率,數據量小則可選擇250k速率,傳輸距離更遠。

(2)支持更多種功率配置,能根據不同應用有效節省功耗。

(3)nordic更新一代2.4g晶元穩定性及可靠性更高。
所以前者正常通訊的程序後者當然可以用

閱讀全文

與PA源碼是什麼相關的資料

熱點內容
web應用安全pdf 瀏覽:47
linuxintel網卡驅動下載 瀏覽:217
資源解壓後怎麼刪除 瀏覽:868
編程之美15種演算法 瀏覽:147
java的圖形用戶界面設計 瀏覽:769
算數游戲源碼 瀏覽:999
壓縮機工作聲音判斷 瀏覽:985
事業單位程序員 瀏覽:506
易語言取相似顏色源碼 瀏覽:773
pyodbclinux 瀏覽:585
vivo為什麼把伺服器沉到深海 瀏覽:460
程序員能為電商做什麼 瀏覽:401
騰訊直充qq號加密碼 瀏覽:140
qt搭建msvc編譯器環境 瀏覽:338
單片機晶振壞了會不會工作不穩定 瀏覽:770
天天影迷APP顯示連接伺服器失敗怎麼回事 瀏覽:961
鋼鐵命令同盟第七關怎麼過 瀏覽:7
android底部控制項彈出 瀏覽:43
為程序員而自豪 瀏覽:583
可以進行c語言編譯的文件名 瀏覽:384