导航:首页 > 源码编译 > 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源码是什么相关的资料

热点内容
视频字幕提取APP怎么使用 浏览:57
js通过ip地址连接服务器吗 浏览:846
java数字金额大写金额 浏览:856
人人影视路由器固件编译 浏览:965
照片通讯录短信怎么从安卓到苹果 浏览:456
逻辑开发编译环境 浏览:670
ce自己编译 浏览:896
javaexe进程 浏览:478
电脑wechat是什么文件夹 浏览:956
单片机moc3041 浏览:786
at命令串口助手 浏览:749
吸血app怎么关闭 浏览:35
云服务器地图不见了怎么办 浏览:240
mc服务器应该叫什么名字 浏览:607
推拉门增加密封性 浏览:731
服务器搬家需要什么 浏览:541
普通电脑如何添加服务器 浏览:401
在外包公司如何成为优秀的程序员 浏览:413
无服务器如何开发 浏览:802
怎么改中国移动服务器 浏览:779