在某些时候也许你会想要把模块和文章联系起来。我们知道模块的位置是由module positions定义的,而module positions在页面上的显示是由模板定义的。如何才能将一个模块真正的插入到一篇文章中呢?步骤
把模块插入到文章中需要使用【loadposition xx】这个命令,如下:
注意:要使用loadposition的功能,需要在“Plug-in Manager”中启用“Content - Load Modules”,如果没有打开这个选项,在文章中会直接显示“{loadposition myposition}”文字
loadmodule也可以使用"{loadmodule yyy}“来代替”{loadposition xx}“,他们是由同一个plugin来处理的。 这种情况下plugin会查找第一个类型是yyy的模块。你可以使用{loadmodule login} 来插入一个名称为"mod_login"的模块。
在模块中插入模块Joomla2.5及以上的版本可以在“Custom HTML"中包含另一个模块,与上述方法一样,都是由同一个plugin来处理的。
当使用这种方法时,请记住你可能会遇到格式上的问题。由于你自定义的HTML模块会自动围绕你插入的模块,所以可能在显示格式和布局的时候会有意想不到的效果。
翻译自Joomla官方文档
一、分段机制
逻辑地址---分段机制---线性地址---分页机制---物理地址,分页机制示意图如图1。
图 1 页映射表结构
页目录表PDE,及页表PTE如图2。
图 2 页目录表或者页表的表格选项
AVL:供软件使用
P:存在属性位,P=1表项有效,P=0表项无效,即是否在内存中。
R/W位指示该表项所指定的页是否可读、写或执行。如R/W=1,对表项所指定页可进行读、写后者执行;如R/W=0,对表项所指定页可读或者执行,但不能对该指定页写。但是R/W位对页的写保护只有处理器处于用户特权级时发挥作用;当处理器处于系统特权级时,R/W位被忽略,也即总可以读、写或者执行。
U/S位指示该表项所指定的页是否似乎用户级页。如U/S=1,表项所指定页是用户级页,可由任何特权级下执行的程序访问;如U/S=0,表项所指定页是系统页,只能由在系统特权级下执行的程序访问。
A=1,表示已访问过对应的物理页,A=0,表示未访问过对应的物理页。
D=1,表示已写过对应的物理页,D=0,表示未写过对应的物理页。
pm.inc增加的几行代码:
PG_P EQU 1 ; 页存在属性位 0001 PG_RWR EQU0; R/W 属性位值, 读/执行 0000 PG_RWW EQU2; R/W 属性位值, 读/写/执行 0010 PG_USS EQU0; U/S 属性位值, 系统级 0000 PG_USU EQU4; U/S 属性位值, 用户级 0100
PDE,PTE具体形象的图形如图3,线性地址到物理地址转换过程如图4:
图3 PDE,PTE具体形象
图4 线性地址到物理地址的转换过程页表DDDDD001改为DDDDD000 由于abc为12位,正好每个PTE对应4KB
每个PDE和PTE都是4个字节,左侧PDE一共4个,占16字节,右侧PTE每个框里面是1024个,所以总共占1024*4*4字节=8KB,所以总共可以指向的内存大小是1024*4*4KB=16MB。此图中CR0的内容为00211000,在形成地址的时候我们只关心高20位,用高20位加上后面12位全0形成基地址,再由页目录索引表或者页表索引找到PDE和PTE。最后高20位补上偏移地址形成物理地址。
二、pmtest8.asm代码详解
; ========================================== ; pmtest8.asm ; 编译方法:nasm pmtest8.asm -o pmtest8.com ; ========================================== %include "pm.inc" ; 常量, 宏, 以及一些说明 PageDirBase0 equ 200000h ; 页目录开始地址: 2M PageTblBase0 equ 201000h ; 页表开始地址: 2M + 4K 这样的大小最多能寻址4GB PageDirBase1 equ 210000h ; 页目录开始地址: 2M + 64K PageTblBase1 equ 211000h ; 页表开始地址: 2M + 64K + 4K LinearAddrDemo equ 00401000h ProcFoo equ 00401000h ProcBar equ 00501000h ProcPagingDemo equ 00301000h org 0100h jmp LABEL_BEGIN [SECTION .gdt] ; GDT ; 段基址, 段界限, 属性 LABEL_GDT: Descriptor 0, 0, 0 ; 空描述符 LABEL_DESC_NORMAL: Descriptor 0, 0ffffh, DA_DRW ; Normal 描述符 LABEL_DESC_FLAT_C: Descriptor 0, 0fffffh, DA_CR|DA_32|DA_LIMIT_4K; 0~4G LIMIT=limit*4k+0FFFh 左移12位再加上FFF正好是FFFFFFFF,4G LABEL_DESC_FLAT_RW: Descriptor 0, 0fffffh, DA_DRW|DA_LIMIT_4K ; 0~4G LABEL_DESC_CODE32: Descriptor 0, SegCode32Len-1, DA_CR|DA_32 ; 非一致代码段, 32 LABEL_DESC_CODE16: Descriptor 0, 0ffffh, DA_C ; 非一致代码段, 16 LABEL_DESC_DATA: Descriptor 0, DataLen-1, DA_DRW ; Data LABEL_DESC_STACK: Descriptor 0, TopOfStack, DA_DRWA|DA_32 ; Stack, 32 位 LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW ; 显存首地址 ; GDT 结束 GdtLen equ $ - LABEL_GDT ; GDT长度 GdtPtr dw GdtLen - 1 ; GDT界限 dd 0 ; GDT基地址 ; GDT 选择子 SelectorNormal equ LABEL_DESC_NORMAL - LABEL_GDT SelectorFlatC equ LABEL_DESC_FLAT_C - LABEL_GDT SelectorFlatRW equ LABEL_DESC_FLAT_RW - LABEL_GDT SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT SelectorCode16 equ LABEL_DESC_CODE16 - LABEL_GDT SelectorData equ LABEL_DESC_DATA - LABEL_GDT SelectorStack equ LABEL_DESC_STACK - LABEL_GDT SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT ; END of [SECTION .gdt] [SECTION .data1] ; 数据段 ALIGN 32 [BITS 32] LABEL_DATA: ; 实模式下使用这些符号 ; 字符串 _szPMMessage: db "In Protect Mode now. ^-^", 0Ah, 0Ah, 0 ; 进入保护模式后显示此字符串 _szMemChkTitle: db "BaseAddrL BaseAddrH LengthLow LengthHigh Type", 0Ah, 0 ; 进入保护模式后显示此字符串 _szRAMSize db "RAM size:", 0 _szReturn db 0Ah, 0 ; 变量 _wSPValueInRealMode dw 0 _dwMCRNumber: dd 0 ; Memory Check Result _dwDispPos: dd (80 * 6 + 0) * 2 ; 屏幕第 6 行, 第 0 列。 _dwMemSize: dd 0 _ARDStruct: ; Address Range Descriptor Structure _dwBaseAddrLow: dd 0 _dwBaseAddrHigh: dd 0 _dwLengthLow: dd 0 _dwLengthHigh: dd 0 _dwType: dd 0 _PageTableNumber dd 0 _MemChkBuf: times 256 db 0 ; 保护模式下使用这些符号 szPMMessage equ _szPMMessage - $$ ;相对于首地址的偏移 szMemChkTitle equ _szMemChkTitle - $$ szRAMSize equ _szRAMSize - $$ szReturn equ _szReturn - $$ dwDispPos equ _dwDispPos - $$ dwMemSize equ _dwMemSize - $$ dwMCRNumber equ _dwMCRNumber - $$ ARDStruct equ _ARDStruct - $$ dwBaseAddrLow equ _dwBaseAddrLow - $$ dwBaseAddrHigh equ _dwBaseAddrHigh - $$ dwLengthLow equ _dwLengthLow - $$ dwLengthHigh equ _dwLengthHigh - $$ dwType equ _dwType - $$ MemChkBuf equ _MemChkBuf - $$ PageTableNumber equ _PageTableNumber- $$ DataLen equ $ - LABEL_DATA ; END of [SECTION .data1] ; 全局堆栈段 [SECTION .gs] ALIGN 32 [BITS 32] LABEL_STACK: times 512 db 0 TopOfStack equ $ - LABEL_STACK - 1 ; END of [SECTION .gs] [SECTION .s16] [BITS 16] LABEL_BEGIN: mov ax, cs mov ds, ax mov es, ax mov ss, ax mov sp, 0100h mov [LABEL_GO_BACK_TO_REAL+3], ax mov [_wSPValueInRealMode], sp ; 得到内存数 mov ebx, 0 mov di, _MemChkBuf .loop: mov eax, 0E820h mov ecx, 20 mov edx, 0534D4150h int 15h ;往es:di中一次存放20个字节 jc LABEL_MEM_CHK_FAIL add di, 20 inc dword [_dwMCRNumber] ;ds:_dwMCRNumber,记录一共有几个20个字节 cmp ebx, 0 jne .loop jmp LABEL_MEM_CHK_OK LABEL_MEM_CHK_FAIL: mov dword [_dwMCRNumber], 0 LABEL_MEM_CHK_OK: ; 初始化 16 位代码段描述符 mov ax, cs movzx eax, ax shl eax, 4 add eax, LABEL_SEG_CODE16 mov word [LABEL_DESC_CODE16 + 2], ax shr eax, 16 mov byte [LABEL_DESC_CODE16 + 4], al mov byte [LABEL_DESC_CODE16 + 7], ah ; 初始化 32 位代码段描述符 xor eax, eax mov ax, cs shl eax, 4 add eax, LABEL_SEG_CODE32 mov word [LABEL_DESC_CODE32 + 2], ax shr eax, 16 mov byte [LABEL_DESC_CODE32 + 4], al mov byte [LABEL_DESC_CODE32 + 7], ah ; 初始化数据段描述符 xor eax, eax mov ax, ds shl eax, 4 add eax, LABEL_DATA mov word [LABEL_DESC_DATA + 2], ax shr eax, 16 mov byte [LABEL_DESC_DATA + 4], al mov byte [LABEL_DESC_DATA + 7], ah ; 初始化堆栈段描述符 xor eax, eax mov ax, ds shl eax, 4 add eax, LABEL_STACK mov word [LABEL_DESC_STACK + 2], ax shr eax, 16 mov byte [LABEL_DESC_STACK + 4], al mov byte [LABEL_DESC_STACK + 7], ah ; 为加载 GDTR 作准备 xor eax, eax mov ax, ds shl eax, 4 add eax, LABEL_GDT ; eax <- gdt 基地址 mov dword [GdtPtr + 2], eax ; [GdtPtr + 2] <- gdt 基地址 ; 加载 GDTR lgdt [GdtPtr] ; 关中断 cli ; 打开地址线A20 in al, 92h or al, 00000010b out 92h, al ; 准备切换到保护模式 mov eax, cr0 or eax, 1 mov cr0, eax ; 真正进入保护模式 jmp dword SelectorCode32:0 ; 执行这一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector:0 处 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; LABEL_REAL_ENTRY: ; 从保护模式跳回到实模式就到了这里 mov ax, cs mov ds, ax mov es, ax mov ss, ax mov sp, [_wSPValueInRealMode] in al, 92h ; ┓ and al, 11111101b ; ┣ 关闭 A20 地址线 out 92h, al ; ┛ sti ; 开中断 mov ax, 4c00h ; ┓ int 21h ; ┛回到 DOS ; END of [SECTION .s16] [SECTION .s32]; 32 位代码段. 由实模式跳入. [BITS 32] LABEL_SEG_CODE32: mov ax, SelectorData mov ds, ax ; 数据段选择子 mov es, ax mov ax, SelectorVideo mov gs, ax ; 视频段选择子 mov ax, SelectorStack mov ss, ax ; 堆栈段选择子 mov esp, TopOfStack call DispMemSize ; 获取内存信息 call PagingDemo ; 演示改变页目录的效果 ; 到此停止 jmp SelectorCode16:0 ; 显示内存信息 -------------------------- DispMemSize: push esi push edi push ecx mov esi, MemChkBuf ;256字节开始位置 mov ecx, [dwMCRNumber] ;for(int i=0;i<[MCRNumber];i++) // 每次得到一个ARDS(Address Range Descriptor Structure)结构 .loop: ;{ mov edx, 5 ; for(int j=0;j<5;j++) // 每次得到一个ARDS中的成员,共5个成员 mov edi, ARDStruct ; { // 依次显示:BaseAddrLow,BaseAddrHigh,LengthLow,LengthHigh,Type .1: ; mov eax dword [esi] ; ds:esi-->eax stosd ; eax-->es:edi ARDStruct[j*4] = MemChkBuf[j*4]; EI自动加4 add esi, 4 ; dec edx ; cmp edx, 0 ; jnz .1 ; 如果不为0} cmp dword [dwType], 1 ; if(Type == AddressRangeMemory) // AddressRangeMemory : 1, AddressRangeReserved : 2 jne .2 ; 如果不相等{ mov eax, [dwBaseAddrLow] ; ds:dwBaseAddrLow add eax, [dwLengthLow] ;ds:dwLengthLow cmp eax, [dwMemSize] ; if(BaseAddrLow + LengthLow > MemSize) ds:dwMemSize jb .2 ;如果小于 mov [dwMemSize], eax ; MemSize = BaseAddrLow + LengthLow; .2: ; } loop .loop ;} 从156字节不断取数据放到es:ARDStruct pop ecx pop edi pop esi ret ; --------------------------------------- ; 测试分页机制 -------------------------- PagingDemo: mov ax, cs mov ds, ax ;因为后面要复制的内容都是相对于32位代码段的偏移 mov ax, SelectorFlatRW mov es, ax push LenFoo push OffsetFoo ;ds:esi-->es:edi push ProcFoo ;es call MemCpy ;此段代码为于lib.inc中,由于以后会用C语言写,暂时不考虑 add esp, 12 push LenBar push OffsetBar push ProcBar call MemCpy; add esp, 12 push LenPagingDemoAll push OffsetPagingDemoProc push ProcPagingDemo call MemCpy add esp, 12 ;至此PagingDemoProc放在内存00301000h,foo放在内存的00401000h,bar放在内存的00501000h mov ax, SelectorData mov ds, ax ; 恢复原来的数据段选择子 mov es, ax call SetupPaging ; 启动分页,相对距离 call SelectorFlatC:ProcPagingDemo ;执行PagingDemoProc,00301000h,分页后形成的地址还是00301000h call PSwitch ; 切换页目录,改变地址映射关系 call SelectorFlatC:ProcPagingDemo;执行PagingDemoProc,00301000h,分页后形成的地址还是00301000h ret ; --------------------------------------- ; 启动分页机制 -------------------------- SetupPaging: ; 根据内存大小计算应初始化多少PDE以及多少页表 xor edx, edx mov eax, [dwMemSize] mov ebx, 400000h ; 400000h = 4M = 4096 * 1024, 一个页表对应的内存大小 div ebx mov ecx, eax ; 此时 ecx 为页表的个数,也即 PDE 应该的个数 test edx, edx jz .no_remainder ;如果为0 inc ecx ; 如果余数不为 0 就需增加一个页表 .no_remainder: mov [PageTableNumber], ecx ; 暂存页表个数,ds:PageTableNumber ; 为简化处理, 所有线性地址对应相等的物理地址. 并且不考虑内存空洞. ; 首先初始化页目录 mov ax, SelectorFlatRW mov es, ax mov edi, PageDirBase0 ; 此段首地址为 PageDirBase0 xor eax, eax mov eax, PageTblBase0 | PG_P | PG_USU | PG_RWW .1: stosd ;eax-->es:edi add eax, 4096 ; 为了简化, 所有页表在内存中是连续的. loop .1 ;共循环ecx次 ; 再初始化所有页表 mov eax, [PageTableNumber] ; 页表个数 mov ebx, 1024 ; 每个页表 1024 个 PTE mul ebx mov ecx, eax ; PTE个数 = 页表个数 * 1024 mov edi, PageTblBase0 ; 此段首地址为 PageTblBase0 xor eax, eax mov eax, PG_P | PG_USU | PG_RWW .2: stosd add eax, 4096 ; 每一页指向 4K 的空间 loop .2 mov eax, PageDirBase0 mov cr3, eax ;修改cr3 mov eax, cr0 or eax, 80000000h mov cr0, eax;启动分页 jmp short .3 .3: nop ret ; 分页机制启动完毕 ---------------------- ; 切换页表 ------------------------------ PSwitch: ; 初始化页目录 mov ax, SelectorFlatRW mov es, ax mov edi, PageDirBase1 ; 此段首地址为 PageDirBase1 xor eax, eax mov eax, PageTblBase1 | PG_P | PG_USU | PG_RWW mov ecx, [PageTableNumber] .1: stosd add eax, 4096 ; 为了简化, 所有页表在内存中是连续的. loop .1 ; 再初始化所有页表 mov eax, [PageTableNumber] ; 页表个数 mov ebx, 1024 ; 每个页表 1024 个 PTE mul ebx mov ecx, eax ; PTE个数 = 页表个数 * 1024 mov edi, PageTblBase1 ; 此段首地址为 PageTblBase1 xor eax, eax mov eax, PG_P | PG_USU | PG_RWW .2: stosd add eax, 4096 ; 每一页指向 4K 的空间 loop .2 ; 在此假设内存是大于 8M 的 mov eax, LinearAddrDemo shr eax, 22 mov ebx, 4096 mul ebx mov ecx, eax mov eax, LinearAddrDemo shr eax, 12 and eax, 03FFh ; 1111111111b (10 bits) mov ebx, 4 mul ebx add eax, ecx add eax, PageTblBase1 ;此时eax为0x00212004,这个参考图3,你就会明白的 mov dword [es:eax], ProcBar | PG_P | PG_USU | PG_RWW mov eax, PageDirBase1 mov cr3, eax jmp short .3 .3: nop ret ; --------------------------------------- PagingDemoProc: ;为了算出LenPagingDemoAll OffsetPagingDemoProc equ PagingDemoProc - $$ ;相对于当前代码段的偏移 mov eax, LinearAddrDemo ;00401000h call eax ;call cs:eax retf ;因为上面把CS和EIP都压入了栈 LenPagingDemoAll equ $ - PagingDemoProc foo: ;00401000h OffsetFoo equ foo - $$ ;相对于当前代码段的偏移 mov ah, 0Ch ; 0000: 黑底 1100: 红字 mov al, 'F' mov [gs:((80 * 17 + 0) * 2)], ax ; 屏幕第 17 行, 第 0 列。 mov al, 'o' mov [gs:((80 * 17 + 1) * 2)], ax ; 屏幕第 17 行, 第 1 列。 mov [gs:((80 * 17 + 2) * 2)], ax ; 屏幕第 17 行, 第 2 列。 ret ;因为上面只改变了EIP,call eax LenFoo equ $ - foo bar: ;00501000h OffsetBar equ bar - $$ ;相对于当前代码段的偏移 mov ah, 0Ch ; 0000: 黑底 1100: 红字 mov al, 'B' mov [gs:((80 * 18 + 0) * 2)], ax ; 屏幕第 18 行, 第 0 列。 mov al, 'a' mov [gs:((80 * 18 + 1) * 2)], ax ; 屏幕第 18 行, 第 1 列。 mov al, 'r' mov [gs:((80 * 18 + 2) * 2)], ax ; 屏幕第 18 行, 第 2 列。 ret LenBar equ $ - bar %include "lib.inc" ; 库函数 SegCode32Len equ $ - LABEL_SEG_CODE32 ; END of [SECTION .s32] ; 16 位代码段. 由 32 位代码段跳入, 跳出后到实模式 [SECTION .s16code] ALIGN 32 [BITS 16] LABEL_SEG_CODE16: ; 跳回实模式: mov ax, SelectorNormal mov ds, ax mov es, ax mov fs, ax mov gs, ax mov ss, ax mov eax, cr0 and eax, 7FFFFFFEh ; PE=0, PG=0 mov cr0, eax LABEL_GO_BACK_TO_REAL: jmp 0:LABEL_REAL_ENTRY ; 段地址会在程序开始处被设置成正确的值 Code16Len equ $ - LABEL_SEG_CODE16 ; END of [SECTION .s16code]
QQ 543415188
MSN feitianhanxue@hotmail.com
Email feitianhanxue@126.com