精华内容
下载资源
问答
  • 下面程序段运行结果是: for(y=1;y<10;) y=((x=3*y,x+1),x-1); printf(“x=%d,y=%d”,x,y); A)x=27,y=27 B)x=12,y=13 C)x=15,y=14 D)x=y=27 逗号表达式注意两个bai基本知识: 1. 逗号表达式的运算i顺序是从左向...

    下面程序段的运行结果是: for(y=1;y<10;) y=((x=3*y,x+1),x-1); printf(“x=%d,y=%d”,x,y); A)x=27,y=27 B)x=12,y=13 C)x=15,y=14 D)x=y=27

    逗号表达式注意两个bai基本知识:
    1.
    逗号表达式的运算i顺序是从左向右运算
    2.
    逗号表达式的值取逗号中最右表达式的值
    第一个循环:(x=0,y=1)
    现在看(x=3y,x+1),x-1这个逗号表达式,
    首先对于(x=3
    y,x+1)和x-1来说,按照从左向右运算先计算(x=3y,x+1)
    (x=3
    y,x+1)
    这个逗号表达式从左向右运算先计算x=3y,这是个赋值语句,处理x=31=3
    再计算x+1,此时x=3,所以x+1=4,但是注意这个表达式并没有对a赋值,仅仅是一个乘法运算
    根据逗号表达式的值取逗号中最右表达式的值的原则,(x=3y,x+1)的值为4
    再看x-1,
    之前运算(x=3
    y,x+1)时,x已经被赋值为3,所以这里3-1=2
    所以x-1这个表达式的值为2
    再根据逗号表达式的值取逗号中最右表达式的值的原则,表达式((x=3*y,x+1),x-1)
    的值即为x-1的值,也就等于2了
    第一次循环结束,得到结果:x=3,y=2
    第二次循环(x=3,y=2)
    得到结果x=6,y=5
    第三次循环(x=6,y=5)
    得到结果x=15,y=14
    循环结束,按整型输出结果

    展开全文
  • 验证PSP(程序段前缀)的作用

    千次阅读 2013-03-29 14:21:47
    程序段前缀是Dos下可执行程序载入内存结构的一部分,位于前0100h部分,它的大致作用有: (1) 子进程通过程序段前缀继承、恢复父进程的信息 (2 ) 使子进程正确地返回到父进程 ( 3) 恢复中断23 H 和24H 的入口...

    程序段前缀是Dos下可执行程序载入内存后结构的一部分,位于前0100h部分,它的大致作用有:

    (1) 子进程通过程序段前缀继承、恢复父进程的信息

    (2 ) 使子进程正确地返回到父进程

    ( 3) 恢复中断23 H 和24H 的入口地址

    (4) 给子进程提供Dos 的入口信息


    更详细的信息请参考:

    http://zhangxunzi.zxq.net/ebook/xxdos/f/dospro/psp.htm


    下面我们来验证一下PSP的作用:

    (借用Orange's中的一个程序,在freedos中运行实验,以下程序如果看不懂,可以只看后面更改的部分)

    ; ==========================================
    ; pmtest2.asm
    ; 编译方法:nasm pmtest2.asm -o pmtest2.com
    ; ==========================================
    
    %include	"pm.inc"	; 常量, 宏, 以及一些说明
    
    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_CODE32: Descriptor    0, SegCode32Len-1, DA_C+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_TEST:   Descriptor 0500000h,     0ffffh, DA_DRW
    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
    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
    SelectorTest		equ	LABEL_DESC_TEST		- LABEL_GDT
    SelectorVideo		equ	LABEL_DESC_VIDEO	- LABEL_GDT
    ; END of [SECTION .gdt]
    
    [SECTION .data1]	 ; 数据段
    ALIGN	32
    [BITS	32]
    LABEL_DATA:
    SPValueInRealMode	dw	0
    ; 字符串
    PMMessage:		db	"In Protect Mode now. ^-^", 0	; 在保护模式中显示
    OffsetPMMessage		equ	PMMessage - $$
    StrTest:		db	"ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0
    OffsetStrTest		equ	StrTest - $$
    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	[SPValueInRealMode], sp
    
    	; 初始化 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, [SPValueInRealMode]
    
    	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	ax, SelectorTest
    	mov	es, ax			; 测试段选择子
    	mov	ax, SelectorVideo
    	mov	gs, ax			; 视频段选择子
    
    	mov	ax, SelectorStack
    	mov	ss, ax			; 堆栈段选择子
    
    	mov	esp, TopOfStack
    
    
    	; 下面显示一个字符串
    	mov	ah, 0Ch			; 0000: 黑底    1100: 红字
    	xor	esi, esi
    	xor	edi, edi
    	mov	esi, OffsetPMMessage	; 源数据偏移
    	mov	edi, (80 * 10 + 0) * 2	; 目的数据偏移。屏幕第 10 行, 第 0 列。
    	cld
    .1:
    	lodsb
    	test	al, al
    	jz	.2
    	mov	[gs:edi], ax
    	add	edi, 2
    	jmp	.1
    .2:	; 显示完毕
    
    	call	DispReturn
    
    	call	TestRead
    	call	TestWrite
    	call	TestRead
    
    	; 到此停止
    	jmp	SelectorCode16:0
    
    ; ------------------------------------------------------------------------
    TestRead:
    	xor	esi, esi
    	mov	ecx, 8
    .loop:
    	mov	al, [es:esi]
    	call	DispAL
    	inc	esi
    	loop	.loop
    
    	call	DispReturn
    
    	ret
    ; TestRead 结束-----------------------------------------------------------
    
    
    ; ------------------------------------------------------------------------
    TestWrite:
    	push	esi
    	push	edi
    	xor	esi, esi
    	xor	edi, edi
    	mov	esi, OffsetStrTest	; 源数据偏移
    	cld
    .1:
    	lodsb
    	test	al, al
    	jz	.2
    	mov	[es:edi], al
    	inc	edi
    	jmp	.1
    .2:
    
    	pop	edi
    	pop	esi
    
    	ret
    ; TestWrite 结束----------------------------------------------------------
    
    
    ; ------------------------------------------------------------------------
    ; 显示 AL 中的数字
    ; 默认地:
    ;	数字已经存在 AL 中
    ;	edi 始终指向要显示的下一个字符的位置
    ; 被改变的寄存器:
    ;	ax, edi
    ; ------------------------------------------------------------------------
    DispAL:
    	push	ecx
    	push	edx
    
    	mov	ah, 0Ch			; 0000: 黑底    1100: 红字
    	mov	dl, al
    	shr	al, 4
    	mov	ecx, 2
    .begin:
    	and	al, 01111b
    	cmp	al, 9
    	ja	.1
    	add	al, '0'
    	jmp	.2
    .1:
    	sub	al, 0Ah
    	add	al, 'A'
    .2:
    	mov	[gs:edi], ax
    	add	edi, 2
    
    	mov	al, dl
    	loop	.begin
    	add	edi, 2
    
    	pop	edx
    	pop	ecx
    
    	ret
    ; DispAL 结束-------------------------------------------------------------
    
    
    ; ------------------------------------------------------------------------
    DispReturn:
    	push	eax
    	push	ebx
    	mov	eax, edi
    	mov	bl, 160
    	div	bl
    	and	eax, 0FFh
    	inc	eax
    	mov	bl, 160
    	mul	bl
    	mov	edi, eax
    	pop	ebx
    	pop	eax
    
    	ret
    ; DispReturn 结束---------------------------------------------------------
    
    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	al, 11111110b
    	mov	cr0, eax
    
    LABEL_GO_BACK_TO_REAL:
    	jmp	0:LABEL_REAL_ENTRY	; 段地址会在程序开始处被设置成正确的值
    
    Code16Len	equ	$ - LABEL_SEG_CODE16
    
    ; END of [SECTION .s16code]
    


    由于只是要验证PSP的作用,我就不详细解释每句代码了,这个程序的主要做的事情就是




    注意在显示完成后,返回了dos,下面我们修改代码


    [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	[SPValueInRealMode], sp
    
    	; 初始化 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
    

    改为

    [SECTION .s16]
    [BITS	16]
    LABEL_BEGIN:
    	mov	ax, cs
    	mov	ds, ax
    	mov	es, ax
    	mov	ss, ax
    	mov	sp, 0100h							;代码A
    
    	mov	[LABEL_GO_BACK_TO_REAL+3], ax
    	mov	[SPValueInRealMode], sp
    
    
          ;Here we give a test of covering the PSP to identify the effect of PSP	 代码B
                    mov     cx, 100							;代码B
    label_loop:     push    1							;代码B
                    loop label_loop							;代码B
    
    
    	; 初始化 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

    即在程序在16位实模式开始时,添加代码B,注意上面代码A句将栈顶指针sp指向了0100h处,即PSP末尾,真正程序的开始处。代码B的作用显而易见,是执行100次循环,向栈中push 1,将PSP覆盖掉。

    下面我们看执行的效果:




    可以看到,程序无法返回dos,而显示是正常的,这说明程序本身的执行是正常的,但是由于PSP被覆盖,程序结束后无法返回dos,这也就证明了PSP的作用。




    展开全文
  • 代码段与程序段的区别

    千次阅读 2012-09-21 23:23:18
    一个程序本质上都是由 bss、data、text三个组成的。这样的概念,不知道最初来源于哪里的规定,但 在当前的计算机程序设计中是很重要的一个基本概念。而且在嵌入式系统的设计中也非常重要,牵涉到嵌入式系统...

           一个程序本质上都是由 bss段、data段、text段三个组成的。这样的概念,不知道最初来源于哪里的规定,但 在当前的计算机程序设计中是很重要的一个基本概念。而且在嵌入式系统的设计中也非常重要,牵涉到嵌入式系统运行时的内存大小分配,存储单元占用空间大小的 问题。

    在采用段式内存管理的架构中(比如intel的80x86系统),bss段(Block Started by Symbol segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域,一般在初始化时bss 段部分将会清零。bss段属于静态内存分配,即程序一开始就将其清零了。

    比如,在C语言之类的程序编译完成之后,已初始化的全局变量保存在.data 段中,未初始化的全局变量保存在.bss 段中。
    在《Programming ground up》里对.bss的解释为:There is another section called the .bss. This section is like the data section, except that it doesn’t take up space in the executable.
           text和data段都在可执行文件中(在嵌入式系统里一般是固化在镜像文件中),由系统从可执行文件中加载;而bss段不在可执行文件中,由系统初始化。

    【例一】< XMLNAMESPACE PREFIX ="O" />

    cl编译两个小程序如下:

    程序1:

    < XMLNAMESPACE PREFIX ="V" />int ar[30000];
    void main()
    {
    ......
    }


    程序2:

    int ar[300000] = {1, 2, 3, 4, 5, 6 };
    void main()
    {
    ......
    }


    发现程序2编译之后所得的.exe文件比程序1的要大得多。当下甚为不解,于是手工编译了一下,并使用了/FAs编译选项来查看了一下其各自的.asm,发现在程序1.asmar的定义如下:

    _BSS SEGMENT
    ?ar@@3PAHA DD 0493e0H DUP (?) ; ar
    _BSS ENDS


    而在程序2.asm中,ar被定义为:

    _DATA SEGMENT
    ?ar@@3PAHA DD 01H ; ar
    DD 02H
    DD 03H
    ORG $+1199988
    _DATA ENDS


    区别很明显,一个位于.bss段,而另一个位于.data段,两者的区别在于:全局的未初始化变量存在于.bss段中,具体体现为一个占位符;全局的已初始化变量存于.data段中;而函数内的自动变量都在栈上分配空间。.bss是不占用.exe文件空间的,其内容由操作系统初始化(清零);而.data却需要占用,其内容由程序初始化,因此造成了上述情况。

    【例二】

    编译如下程序(test.cpp:
    #include <stdio.h>

    #define LEN 1002000

    int inbss[LEN];
    float fA;
    int indata[LEN]={1,2,3,4,5,6,7,8,9};
    double dbB = 100.0;

    const int cst = 100;

    int main(void)
    {
    int run[100] = {1,2,3,4,5,6,7,8,9};
    for(int i=0; i<LEN; ++i)
    printf("%d ", inbss[i]);
    return 0;
    }

    命令:cl /FA test.cpp回车 (/FA:产生汇编代码
    )
    产生的汇编代码
    (test.asm):
    TITLE test.cpp
    .386P
    include listing.inc
    if @Version gt 510
    .model FLAT
    else
    _TEXT SEGMENT PARA USE32 PUBLIC 'CODE'
    _TEXT ENDS
    _DATA SEGMENT DWORD USE32 PUBLIC 'DATA'
    _DATA ENDS
    CONST SEGMENT DWORD USE32 PUBLIC 'CONST'
    CONST ENDS
    _BSS SEGMENT DWORD USE32 PUBLIC 'BSS'
    _BSS ENDS
    _TLS SEGMENT DWORD USE32 PUBLIC 'TLS'
    _TLS ENDS
    FLAT GROUP _DATA, CONST, _BSS
    ASSUME CS: FLAT, DS: FLAT, SS: FLAT
    endif
    PUBLIC ?inbss@@3PAHA ; inbss
    PUBLIC ?fA@@3MA ; fA
    PUBLIC ?indata@@3PAHA ; indata
    PUBLIC ?dbB@@3NA ; dbB
    _BSS SEGMENT

    ?
    inbss@@3PAHA DD < XMLNAMESPACE PREFIX ="ST1" />0f4a10H DUP (?) ; inbss
    ?fA@@3MA DD 01H DUP (?) ; fA
    _BSS ENDS

    _DATA SEGMENT
    ?
    indata@@3PAHA DD 01H ; indata
    DD 02H
    DD 03H
    DD 04H
    DD 05H
    DD 06H
    DD 07H
    DD 08H
    DD 09H
    ORG $+4007964
    ?dbB@@3NA DQ 04059000000000000r ; 100 ; dbB
    _DATA ENDS

    PUBLIC _main
    EXTRN _printf:NEAR
    _DATA SEGMENT
    $SG537 DB '%d ', 00H
    _DATA ENDS
    _TEXT SEGMENT
    _run$ = -400
    _i$ = -404
    _main PROC NEAR
    ; File test.cpp
    ; Line 13
    push ebp
    mov ebp, esp
    sub esp, 404 ; 00000194H
    push edi
    ; Line 14
    mov DWORD PTR _run$[ebp], 1
    mov DWORD PTR _run$[ebp+4], 2
    mov DWORD PTR _run$[ebp+8], 3
    mov DWORD PTR _run$[ebp+12], 4
    mov DWORD PTR _run$[ebp+16], 5
    mov DWORD PTR _run$[ebp+20], 6
    mov DWORD PTR _run$[ebp+24], 7
    mov DWORD PTR _run$[ebp+28], 8
    mov DWORD PTR _run$[ebp+32], 9
    mov ecx, 91 ; 0000005bH
    xor eax, eax
    lea edi, DWORD PTR _run$[ebp+36]
    rep stosd
    ; Line 15
    mov DWORD PTR _i$[ebp], 0
    jmp SHORT $L534
    $L535:
    mov eax, DWORD PTR _i$[ebp]
    add eax, 1
    mov DWORD PTR _i$[ebp], eax
    $L534:
    cmp DWORD PTR _i$[ebp], 1002000 ; 000f4a10H
    jge SHORT $L536
    ; Line 16
    mov ecx, DWORD PTR _i$[ebp]
    mov edx, DWORD PTR ?inbss@@3PAHA[ecx*4]
    push edx
    push OFFSET FLAT:$SG537
    call _printf
    add esp, 8
    jmp SHORT $L535
    $L536:
    ; Line 17
    xor eax, eax
    ; Line 18
    pop edi
    mov esp, ebp
    pop ebp
    ret 0
    _main ENDP
    _TEXT ENDS
    END
    ----------------------------------------
    通过汇编文件可以看到,数组inbssindata位于不同的段(inbss位于bss段,而indata位于data段)
    若把test.cpp中的indata数组拿掉,查看生成的exe文件的大小,可以发现,indata拿掉之后exe文件的大小小了很多。而若拿掉的是inbss数组,exe文件大小跟没拿掉时相差无几。

    说明了:
    bss
    段(未手动初始化的数据)并不给该段的数据分配空间,只是记录数据所需空间的大小。
    data(已手动初始化的数据)段则为数据分配空间,数据保存在目标文件中。

    数据段包含经过初始化的全局变量以及它们的值。BSS段的大小从可执行文件中得到,然后链接器得到这个大小的内存块,紧跟在数据段后面。当这个内存区进入程序的地址空间后全部清零。包含数据段和BSS段的整个区段此时通常称为数据区。

    转自:http://hi.baidu.com/%C6%BF%D6%D0%B5%C4%C5%AE%CE%D7/blog/item/5043d08e741075f3503d922c.html

    ld 时把所有的目标文件的代码段组合成一个代码段,把所有的数据段组合成一个数据段.

    ############################################################################

    BSS段:BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。BSS是英文Block Started by Symbol的简称。BSS段属于静态内存分配。


    数据段:数据段(data segment)通常是指用来存放程序中已初始化的全局变量的一块内存区域。数据段属于静态内存分配。

    代码段:代码段(code segment/text segment)通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读, 某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。代码段是存放了程序代码的数据,假如机器 中有数个进程运行相同的一个程序,那么它们就可以使用同一个代码段。


    堆(heap):堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时, 新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)

    栈(stack):栈又称堆栈, 是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变 量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进先出特点,所以 栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。

    --------------------------------------------------------------------------------------------------------------------------------------

    转自:http://topic.csdn.net/u/20090711/14/1a8ce9fb-05fa-41a9-9980-42e4445b5936.html?73287

    没初始化的全局变量(bss段),只在编译后生成文件的时候会比初始化的全局变量占空间小。
    加载的时候。如果是大型操作系统上,通常加载器帮你把bss段初始化为0,当然已经初始化的全局变量直接从你的可执行文件拷贝对应的值。要注意,bss全 局变量的地址空间不是加载的时候分配的,同样是链接的时候分配的。
    如果在嵌入式里,很可能没加载器,你的程序是直接由bootload程序加载到内存的。这个时候,bss区域是否被清0,要看写bootload代码的人 了。因为这种bootload通常要自己写的。

    --------------------------------------------------------------------------------------------------------------------------------------

    转自:http://baike.baidu.com/view/453125.htm

    BSS是“Block Started by Symbol”的缩写,意为“以符号开始的块”。


      BSS是Unix链接器产生的未初始化数据段。其他的段分别是包含程序代码的“text”段和包含已初始化数据的“data”段。 BSS段的变量只有名称和大小却没有值。此名后来被许多文件格式使用,包括PE。“以符号开始的块”指的是编译器处理未初始化数据的地方。BSS节不包含 任何数据,只是简单的维护开始和结束的地址,以便内存区能在运行时被有效地清零。BSS节在应用程序的二进制映象文件中并不存在。


      在采用段式内存管理的架构中(比如intel的80x86系统),bss段(Block Started by Symbol segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域,一般在初始化时bss 段部分将会清零。bss段属于静态内存分配,即程序一开始就将其清零了。


      比如,在C语言之类的程序编译完成之后,已初始化的全局变量保存在.data 段中,未初始化的全局变量保存在.bss 段中。


      text和data段都在可执行文件中(在嵌入式系统里一般是固化在镜像文件中),由系统从可执行文件中加载;而bss段不在可执行 文件中,由系统初始化。


    以下引自http://www.cnblogs.com/JCSU/articles/1051579.html
    一. 在c中分为这几个存储区
    1.栈 - 由编译器自动分配释放
    2.堆 - 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收
    3.全局区(静态区),全局变量和静态变量的存储是放在一块的,初始化的全局变 量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。- 程序结束释放
    4.另外还有一个专门放常量的地方。- 程序结束释放
    在 函数体中定义的变量通常是在栈上,用malloc, calloc, realloc等分配内存的函数分配得到的就是在堆上。在所有函数体外定义的是全局量,加了static修饰符后不管在哪里都存放在全局区(静态区),在 所有函数体外定义的static变量表示在该文件中有效,不能extern到别的文件用,在函数体内定义的static表示只在该函数体内有效。另外,函 数中的"adgfdf"这样的字符串存放在常量区。比如:
    BSS段,数据段,代码段,堆内存和栈 - 秦人_罗西门 - 罗西门的博客int a = 0; //全局初始化区
    BSS段,数据段,代码段,堆内存和栈 - 秦人_罗西门 - 罗西门的博客char *p1; //全局未初始化区
    BSS段,数据段,代码段,堆内存和栈 - 秦人_罗西门 - 罗西门的博客void main()
    BSS段,数据段,代码段,堆内存和栈 - 秦人_罗西门 - 罗西门的博客BSS段,数据段,代码段,堆内存和栈 - 秦人_罗西门 - 罗西门的博客BSS段,数据段,代码段,堆内存和栈 - 秦人_罗西门 - 罗西门的博客{
    BSS段,数据段,代码段,堆内存和栈 - 秦人_罗西门 - 罗西门的博客 int b; //栈
    BSS段,数据段,代码段,堆内存和栈 - 秦人_罗西门 - 罗西门的博客 char s[] = "abc"; //栈
    BSS段,数据段,代码段,堆内存和栈 - 秦人_罗西门 - 罗西门的博客 char *p2; //栈
    BSS段,数据段,代码段,堆内存和栈 - 秦人_罗西门 - 罗西门的博客 char *p3 = "123456"; //123456{post.content}在常量区,p3在栈上
    BSS段,数据段,代码段,堆内存和栈 - 秦人_罗西门 - 罗西门的博客 static int c = 0; //全局(静态)初始化区
    BSS段,数据段,代码段,堆内存和栈 - 秦人_罗西门 - 罗西门的博客 p1 = (char *)malloc(10); //分配得来得10字节的区域在堆区
    BSS段,数据段,代码段,堆内存和栈 - 秦人_罗西门 - 罗西门的博客 p2 = (char *)malloc(20); //分配得来得20字节的区域在堆区
    BSS段,数据段,代码段,堆内存和栈 - 秦人_罗西门 - 罗西门的博客 strcpy(p1, "123456");
    BSS段,数据段,代码段,堆内存和栈 - 秦人_罗西门 - 罗西门的博客 //123456{post.content}放在常量区,编译器可能会将它与p3所指向 的"123456"优化成一块
    BSS段,数据段,代码段,堆内存和栈 - 秦人_罗西门 - 罗西门的博客}
    BSS段,数据段,代码段,堆内存和栈 - 秦人_罗西门 - 罗西门的博客
    二. 在C++中,内存分成5个区,他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区
    1.栈,就 是那些由编译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区。里面的变量通常是局部变量、函数参数等。
    2.堆,就 是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在 程序结束后,操作系统会自动回收。
    3.自由存储区,就是那些由malloc等分配的内存块,他和堆是十分相似 的,不过它是用free来结束自己的生命的。
    4.全局/静态存储区,全局变量和静态变量被分配到同一块内存 中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。
    5.常量 存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改(当然,你要通过非正当手段也可以修改)
    三. 谈谈堆与栈的关系与区别
    具体地说,现代计算机(串行执行机制),都直接在代 码底层支持栈的数据结构。这体现在,有专门的寄存器指向栈所在的地址,有专门的机器指令完成数据入栈出栈的操作。这种机制的特点是效率高,支持的数据有 限,一般是整数,指针,浮点数等系统直接支持的数据类型,并不直接支持其他的数据结构。因为栈的这种特点,对栈的使用在程序中是非常频繁的。对子程序的调 用就是直接利用栈完成的。机器的call指令里隐含了把返回地址推入栈,然后跳转至子程序地址的操作,而子程序中的ret指令则隐含从堆栈中弹出返回地址 并跳转之的操作。C/C++中的自动变量是直接利用栈的例子,这也就是为什么当函数返回时,该函数的自动变量自动失效的 原因。
    和栈不同,堆的数据结构并不是由系统(无论是机器系统还是操作系统)支持的,而是由函数库提供的。基本的 malloc/realloc/free 函数维护了一套内部的堆数据结构。当程序使用这些函数去获得新的内存空间时,这套函数首先试图从内部堆中寻找可用的内存空间,如果没有可以使用的内存空 间,则试图利用系统调用来动态增加程序数据段的内存大小,新分配得到的空间首先被组织进内部堆中去,然后再以适当的形式返回给调用者。当程序释放分配的内 存空间时,这片内存空间被返回内部堆结构中,可能会被适当的处理(比如和其他空闲空间合并成更大的空闲空间),以更适合下一次内存分配申请。这套复杂的分 配机制实际上相当于一个内存分配的缓冲池(Cache),使用这套机制有如下若干原因:
    1. 系统调用可能不支持任意大小的内存分配。有些系统的系统调用只支持固定大小及其倍数的内存请求(按页分配);这样的话对于大量的小内存分类来说会造成浪 费。
    2. 系统调用申请内存可能是代价昂贵的。系统调用可能涉及用户态和核心态的转换。
    3. 没有管理的内存分配在大量复杂内存的分配释放操作下很容易造成内存碎片。
    堆和栈的对比
    从以上 知识可知,栈是系统提供的功能,特点是快速高效,缺点是有限制,数据不灵活;而栈是函数库提供的功能,特点是灵活方便,数据适应面广泛,但是效率有一定降 低。栈是系统数据结构,对于进程/线程是唯一的;堆是函数库内部数据结构,不一定唯一。不同堆分配的内存无法互相操作。栈空间分静态分配和动态分配两种。 静态分配是编译器完成的,比如自动变量(auto)的分配。动态分配由alloca函数完成。栈的动态分配无需释放(是自动的),也就没有释放函数。为可 移植的程序起见,栈的动态分配操作是不被鼓励的!堆空间的分配总是动态的,虽然程序结束时所有的数据空间都会被释放回系统,但是精确的申请内存/ 释放内存匹配是良好程序的基本要素。
    1.碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间 的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以 至于永远都不可能有一个内存块从栈中间弹出,在他弹出之前,在他上面的后进的栈内容已经被弹出,详细的可以>参考数据结构,这里我们就不再一一讨论 了。
    2.生长方向:对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长。
    3.分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由 alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。
    4.分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈 的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参 考数据结构/操作系统)在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段 的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。
    明确区分堆与栈:
    在bbs上,堆与栈的区分问题,似乎是一个永恒的话题,由此可见,初学者对此往往是混淆不清的,所以我决定拿他第一个开刀。
    首先,我们举一个例子:
    BSS段,数据段,代码段,堆内存和栈 - 秦人_罗西门 - 罗西门的博客void f()
    BSS段,数据段,代码段,堆内存和栈 - 秦人_罗西门 - 罗西门的博客BSS段,数据段,代码段,堆内存和栈 - 秦人_罗西门 - 罗西门的博客BSS段,数据段,代码段,堆内存和栈 - 秦人_罗西门 - 罗西门的博客{
    BSS段,数据段,代码段,堆内存和栈 - 秦人_罗西门 - 罗西门的博客 int* p=new int[5];
    BSS段,数据段,代码段,堆内存和栈 - 秦人_罗西门 - 罗西门的博客}
    BSS段,数据段,代码段,堆内存和栈 - 秦人_罗西门 - 罗西门的博客
    这条短短的一句话就包含了堆与栈,看到new,我们首先就应该想到,我们分配了一块堆内存,那么指针 p呢?他分配的是一块栈内存,所以这句话的意思就是:在栈内存中存放了一个指向一块堆内存的指针p。在程序会先确定在 堆中分配内存的大小,然后调用operator new分配内存,然后返回这块内存的首地址,放入栈中,他在VC6下的汇编代码如下:
    00401028 push 14h
    0040102A call operator new (00401060)
    0040102F add esp,4
    00401032 mov dword ptr [ebp-8],eax
    00401035 mov eax,dword ptr [ebp-8]
    00401038 mov dword ptr [ebp-4],eax
    这里,我们为了简单并没有释放内存,那么该怎么去释放呢?是delete p么?澳,错了,应该是delete []p,这是为了告诉编译器:我删除的是一个数组,VC6就会根据相应的Cookie信息去进行释放内存的工作。
    好了,我们回到我们的主题:堆和栈究竟有什么区别?
    主要的区别由以下几点:
    1、管理方式不同;
    2、空间大小不同;
    3、能否产生碎片不同;
    4、生长方向不同;
    5、分配方式不同;
    6、分配效率不同;
    管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。
    空间大小:一般来讲在32位系统下,堆内存可以达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲,一般都是有一定的空间大小 的,例如,在VC6下面,默认的栈空间大小是1M(好像是,记不清楚了)。当然,我们可以修改:
    打开工程,依次操作菜单如下:Project->Setting->Link,在Category 中选中Output,然后在Reserve中设定堆栈的最大值和commit。
    注意:reserve最小值为4Byte;commit是保留在虚 拟内存的页文件里面,它设置的较大会使栈开辟较大的值,可能增加内存的开销和启动时间。
    堆和栈相比,由于大量new/delete的使用,容易造成大量的内存碎片;由于没有专门的系统支持,效率很低;由于可能引发用户态和核心态的切换,内存 的申请,代价变得更加昂贵。所以栈在程序中是应用最广泛的,就算是函数的调用也利用栈去完成,函数调用过程中的参数,返回地址,EBP和局部变量都采用栈 的方式存放。所以,我们推荐大家尽量用栈,而不是用堆。
    另外对存取效率的比较:
    代码:
    BSS段,数据段,代码段,堆内存和栈 - 秦人_罗西门 - 罗西门的博客char s1[] = "aaaaaaaaaaaaaaa";
    BSS段,数据段,代码段,堆内存和栈 - 秦人_罗西门 - 罗西门的博客char *s2 = "bbbbbbbbbbbbbbbbb";BSS段,数据段,代码段,堆内存和栈 - 秦人_罗西门 - 罗西门的博客
    aaaaaaaaaaa是在运行时刻赋值的;
    而bbbbbbbbbbb是在编译时就确定 的;
    但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。
    比如:
    BSS段,数据段,代码段,堆内存和栈 - 秦人_罗西门 - 罗西门的博客void main()
    BSS段,数据段,代码段,堆内存和栈 - 秦人_罗西门 - 罗西门的博客BSS段,数据段,代码段,堆内存和栈 - 秦人_罗西门 - 罗西门的博客BSS段,数据段,代码段,堆内存和栈 - 秦人_罗西门 - 罗西门的博客{
    BSS段,数据段,代码段,堆内存和栈 - 秦人_罗西门 - 罗西门的博客 char a = 1;
    BSS段,数据段,代码段,堆内存和栈 - 秦人_罗西门 - 罗西门的博客 char c[] = "1234567890";
    BSS段,数据段,代码段,堆内存和栈 - 秦人_罗西门 - 罗西门的博客 char *p ="1234567890";
    BSS段,数据段,代码段,堆内存和栈 - 秦人_罗西门 - 罗西门的博客 a = c[1];
    BSS段,数据段,代码段,堆内存和栈 - 秦人_罗西门 - 罗西门的博客 a = p[1];
    BSS段,数据段,代码段,堆内存和栈 - 秦人_罗西门 - 罗西门的博客 return;
    BSS段,数据段,代码段,堆内存和栈 - 秦人_罗西门 - 罗西门的博客}

    对应的汇编代码
    10: a = c[1];
    00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]
    0040106A 88 4D FC mov byte ptr [ebp-4],cl
    11: a = p[1];
    0040106D 8B 55 EC mov edx,dword ptr [ebp-14h]
    00401070 8A 42 01 mov al,byte ptr [edx+1]
    00401073 88 45 FC mov byte ptr [ebp-4],al
    第一种在读取时直接就把字 符串中的元素读到寄存器cl中,而第二种则要先把指针值读到edx中,在根据edx读取字符,显然慢了.
    无论是堆还是栈,都要防止越界现象的发生(除非你是故意使其越界),因为越界的结果要么是程序崩溃,要么是摧毁程序的堆、栈结构,产生以想不到的结果,就 算是在你的程序运行过程中,没有发生上面的问题,你还是要小心,说不定什么时候就崩掉,编写稳定安全的代码才是最重要的

    展开全文
  •  这个应用报告和相关的代码提供了一种把编译程序段从TMS320F28xxx的flash复制到ram的功能,这样可以提高代码的运行速度。这个解决方案在直接启动之后,进入c_int00 ——C语言代码运行之前实现此功能。  本...

    翻译自TI应用手册SPRAAU8

    摘要

           这个应用报告和相关的代码提供了一种把编译后的程序段从TMS320F28xxx的flash复制到ram的功能,这样可以提高代码的运行速度。这个解决方案在直接启动之后,进入c_int00 ——C语言代码运行之前实现此功能。


           本应用报告中讨论项目内容和代码可以以下网址下载:http://www-s.ti.com/sc/techlit/spraau8.zip


    1.引言:

          在许多应用中代码的执行速度是至关重要的。例如在医疗,监控,电机控制等等一些对时间有严格要求的终端设备。许多应用使用TMS320F28xxx DSCs是因为它的内置flash储存器。内置flash是TMS320F28xxx的一个优势,因为它使得设计者不需要外接flash来储存代码。使用内部flash缺点访问Flash需要等待状态,这使得程序的运行变慢。在大多数应用中,这不是一个问题。其他一些应用中可能会为了获得最高的运行速度要求无等待状态。内部RAM存储器具有零等待状态它是易失性存储器。所以,引导的初始化代码段不可以存储在此存储器

           现在提供的解决方案,使得设计者能够在运行时把被编译器初始化的代码段从flash复制到ram里,获得最大的运行速度。这使代码执行从多达15个等待状态的提升到0等待状态另一种解决方案只将某些函数从Flash复制RAM。详见:《Running an Application from Internal Flash Memory on the TMS320F28xx DSP》
    (SPRA958)。这种方法应该使用在大多数使用C2000™ DSC的应用上,其他要求严格时序和连续的零等待状态应用程序应采用这里提出的解决方案。

           编写汇编程序来完成代码从FlashRAM的复制汇编代码在复位向量后调用c_int00之前执行。这保证了在c_int00调用mian()之前完成复制

           有一些工程比较小,可以把所有初始化了的段都复制到ram。然而,其他一些工程的初始化了的段比所有的内部ram还要大。这些工程可能不可以把所有的初始化了的段都复制到ram,但是用这种方法复制其中一部分段。


    2.编译的代码段:

           编译器生成的包含代码和数据的多个部分,称为段。这下段分为两个不同的组:初始化了的和没被初始化的,初始化部分是由所有的代码,常量和初始化表组成的。下表列出了编译产生初始化段


    初始化段
    段名 内容
    限制
    .cinit 初始化的全局变量和静态变量 代码
    .const 显式初始化全局和静态const变量和字符串常量 不超过64K长度
    .econst 长调用的常量 数据中的任何地方
    .pinit 全局对象的构造函数
    代码
    .switch switch语句产生的 代码或者数据
    .text 可执行代码和常数 代码


          没初始化的段是由未初始化的变量,堆栈和malloc产生的内存下表列出了编译产生的没初始化段


    没初始化段
    段名 内容 限制
    .bss 全局和静态变量 不超过64K长度
    .ebss 长调用的全局或静态变量 数据中的任何地方
    .stack 堆栈空间 不超过64K长度
    .sysmem malloc函数产生的内存 不超过64K长度
    .esysmem far_malloc函数产生的内存 数据中的任何地方



            一旦编译器生成的这些段连接器会从各个源文件中取出这些段,并结合它们来创建一个输出文件。连接器命令文件(.cmd)就是用来告诉连接器去哪里找这些段的。初始化段必须分配到易失性存储器flash/ ROM当电源被撤除时,程序不会消失。未初始化的段可以被分配到RAM中,因为它们是在代码执行期间被初始化的。


             关于更多编译段和连接的信息,请参见:《TMS320C28x Assembly Language
    Tools User’s Guide 》(SPRU513) 和《 the TMS320C28x Optimizing C/C++ Compiler User’s Guide》(SPRU514)。

            德州仪器(TI)提供了多个例子显示如何使用链接器命令文件分配编译段。其中一个就是《Running an Application from Internal Flash Memory on the TMS320F28xx DSP 》(SPRA958)。应用文档提供的例子,演示了使用基于RAM和Flash项目链接器命令文件。


    3.软件:

            本应用文档相关代码文件,包括修改后的版本CodeStartBranch.asm文件和非DSP/BIOS™项目用的文件DSP28xxx_SectionCopy_nonBIOS.asm,由the C/C++ Header Files and Peripheral Examples提供。每个TMS320F28xxx 处理器都提供了现成的连接器命令文件。提供的示例项目演示了如何使用这些文件。本应用文档以TMS320F2808为例。

            该软件独立存放于F28xxx_Flash_to_Ram文件夹中。代码使用的来自the C/C++ Header Files and Peripheral Examples的几个文件经过了Code Composer Studio 3.3和F28xxx代码生成工具5.0.0B3版本的测试。


    3.描述:

    一般的程序流程是这样子的:code_start->wd_disable->copy_sections->c_int00->mian()

    。这个软件流程比标准的软件流程仅仅多了调用复制代码段函数。标准的软件流程:code_start->wd_disable->c_int00->mian()。


    程序开始和关闭看门狗:

            code_start 和wd_disable 的运行代码由DSP28xxx_CodeStartBranch.asm文件提供。上电后,code_start执行,因为它被分配给Flash的引导地址的0x3F7FF6。详见:《Running an Application from Internal Flash Memory on the TMS320F28xx DSP
    》(SPRA958)


    WD_DISABLE .set 1 ;set to 1 to disable WD, else set to 0
          .ref copy_sections
          .global code_start
    ***********************************************************************
    * Function: codestart section
    *
    * Description: Branch to code starting point
    ***********************************************************************
          .sect "codestart"
    
    code_start:
          .if WD_DISABLE == 1
              LB wd_disable ;Branch to watchdog disable code
          .else
              LB copy_sections ;Branch to copy_sections
          .endif



            这个函数从the C/C++ Header Files and Peripheral Examples提供的CodeStartBranch.asm文件修改而来,只是第二个调用用copy_sections代替了_c_int00。这个调用仅仅在WD_DISABLE为0时执行。 上面的代码,WD_DISABLE 被设置为1。这使得wd_disable运行。wd_disable的代码如下:

    ***********************************************************************
    * Function: wd_disable
    *
    * Description: Disables the watchdog timer
    ***********************************************************************
          .if WD_DISABLE == 1
          .sect "wddisable"
    wd_disable:
          SETC OBJMODE ;Set OBJMODE for 28x object code
          EALLOW ;Enable EALLOW protected register access
          MOVZ DP, #7029h>>6 ;Set data page for WDCR register
          MOV @7029h, #0068h ;Set WDDIS bit in WDCR to disable WD
          EDIS ;Disable EALLOW protected register access
          LB copy_sections ;Branch to copy_sections
          .endif

           这要求看门狗在copy_sections和c_int00函数运行期间被除能,否则,看门狗可能会在进入main()之前超时。这个函数也是从the C/C++ Header Files and Peripheral Examples提供的CodeStartBranch.asm文件修改而来,只是用copy_sections代替了_c_int00。


    Copy_sections:

    DSP28xxx_SectionCopy_nonBIOS.asm文件提供了copy_sections的代码,第一次运行到这里,看门狗是关闭的,段已经准备好被复制,段大小被存放在累加器,装载地址放在XAR6中,执行地址放在XAR7中,这个功能例子如下:

         MOVL XAR5,#_text_size ; Store Section Size in XAR5
         MOVL ACC,@XAR5 ; Move Section Size to ACC
         MOVL XAR6,#_text_loadstart ; Store Load Starting Address in XAR6
         MOVL XAR7,#_text_runstart ; Store Run Address in XAR7
         LCR copy ; Branch to Copy

           段的大小,装载开始标志,执行开始标志都由连接器产生,这是在内存分配 -链接器命令文件一节讨论

           在地址和段长度都被存放好之后,copy程序被调用来确定段是否被编译器产生,这由检测累加器是否为0来确定。

    copy:
          B return,EQ ; Return if ACC is Zero (No section to copy)
    
          RPT AL ; Copy Section From Load Address to
             || PWRITE *XAR7, *XAR6++ ; Run Address
    return:
          LRETR ; Return

            如果累加器为0,程序会返回到调用前的地址,如果累加器不为0,有段需要被复制。这用上面所示的PWRITE指令来实现,PWRITE复制XAR6指向的存储器的内容到XAR7指向的内容。在这里,就是复制装载代码的地址的内容到运行代码的地址。这样,一直到累加器为0,完成整个段的复制,当所有段都被复制完,程序就会跳到c_int00,如下:

     LB _c_int00 ; Branch to start of boot.asm in RTS library


    到这里,C语言环境被建立,main()是可进去的。

            完整的copy_sections程序请参见相关文件夹中的DSP28xxx_SectionCopy_nonBIOS.asm。


    内存分配 - 连接命令文件(.cmd)

            如第二节所述,连接命令文件(.cmd)是用来告诉连接器怎么分配编译器产生的段的。The C/C++ Header Files and Peripheral Examples提供了标准的连接命令文件(.cmd)

            相关代码文件提供了三个链接器命令文件用于配置内存分配。

                       · F280xx_nonBIOS_flash.cmd
                       · F281x_nonBIOS_flash.cmd
                       · F2833x_nonBIOS_flash.cmd

            每个文件一般都用相同的方法编写,只是在存储器方面有很小的一些差异(特殊设备)。连接命令文件(.cmd)的Memory部分是根据设备的内存空间来连接编译好的段的。详情参见具体控制器的数据手册。

    下表展示TMS320F2808的存储器映射:



           TMS320F28xxx系列控制器内置RAM,可以被分配为一个单独的段,或者更多的段,因为它是连续存储器映射。如上图所示,F2808有映射到存储器空间的L0,L1和H0 SARAMs,允许生成一个大的内存块,这个块可以被CMD文件的MEMORY部分如下定义:

    RAM_H0L0L1 : origin = 0x008000, length = 0x004000 /* on-chip RAM */

    其余的也可以定义在MEMORY部分,完整的内存分配,请参见相关文件中的CMD文件。

           链接器命令文件的第二部分是SECTIONS。这是实际编译器把段连接到的存储区。所有DSP28xxx_CodeStartBranch.asm 和 DSP28xxx_SectionCopy_nonBIOS.asm的段都被装载到flash中运行,这部分如下所示分配:


    codestart : > BEGIN_FLASH, PAGE = 0 /* Used by file CodeStartBranch.asm */
    wddisable : > FLASH_AB, PAGE = 0 /* Used by file CodeStartBranch.asm */
    copysections : > FLASH_AB, PAGE = 0 /* Used by file SectionCopy.asm */



    其他被初始化的段被下载到flash,但是在ram中运行。这是通过load和run指令来实现下面展示一个例子:

    .text : LOAD = FLASH_AB, PAGE = 0 /* Load section to Flash */
            RUN = RAM_H0L0L1,PAGE = 0 /* Run section from RAM */
            LOAD_START(_text_loadstart),
            RUN_START(_text_runstart),
            SIZE(_text_size)


    为了获得与一个段相关联的特定地址,如上所示,使用了LOAD_START, RUN_START, 和SIZE指令。这些指令的地址和大小DSP28xxx_SectionCopy_nonBIOS.asm文件使用到,用以在复制过程中指向正确的地址。DSP28xxx_SectionCopy_nonBIOS.asm这些值创建为全局变量,如下图所示

    .global _cinit_loadstart, _cinit_runstart, _cinit_size
    .global _const_loadstart, _const_runstart, _const_size
    .global _econst_loadstart, _econst_runstart, _econst_size
    .global _pinit_loadstart, _pinit_runstart, _pinit_size
    .global _switch_loadstart, _switch_runstart, _switch_size
    .global _text_loadstart, _text_runstart, _text_size

    测试例子:

            提供的示例TMS320F2812,TMS320F2808TMS320F28335eZdsp开发上进行了测试。板子上LED的闪烁可以从视觉上证实程序是否正确运行。下面的程序是基于F2808eZdsp评估板设计测试的。同样的,这种方法可以用于其他eZdsp开发



    Code Composer Studio环境:

    1.使用USB线连接F2808eZdsp开发板PC,接上电源线给板子供电

    2.打开Code Composer Studio,设置F2808 eZdsp 仿真器。

    3.打开和编译Example_280xx_Flash_to_RAM_nonBIOS.pjt。

    4.下载.out文件到芯片的flash中。

    5.调试程序(debug)。

    6.运行程序(run)。


    eZdsp电路板上的LED闪烁,表示程序正在运行。


    应用:

    现有的Flash应用程序可以很容易地通过移植相关代码文件来实现此功能。基本的移植步骤如下:

    1.用DSP28xxx_CodeStartBranch.asm替换CodeStartBranch.asm。

    2.在工程中添加DSP28xxx_SectionCopy_nonBIOS.asm文件。

    3.用特殊生成的CMD文件代替现有的CMD文件。


    这个基本步骤不适用于一些特殊情况,比如用户自己定义的段,等


    应用例子:

    为了演示的应用程序集成过程,在C280xC2801x
    C / C ++头文件和外设示例的Example_2808_Flash.pjt中使用下列步骤移植。

    1.下载安装C280x, C2801x C/C++ Header Files and Peripheral Examples。

    2.如上所述连接板,打开项目文件。

    3.删除项目中的DSP280x_CodeStartBranch.asm文件,在项目中添加DSP28xxx_CodeStartBranch.asm文件。

    4.在项目中添加DSP28xxx_SectionCopy_nonBIOS.asm文件。

    5.删除项目中的cmd文件,在项目中添加F280xx_nonBIOS_flash.cmd文件。

    6.把DSP280x_usDelay.asm中的.sect “ramfuncs”改为.text,使DSP28x_usDelay在被分配在.test段中。

    7.删除DSP280x_SysCtrl.c文件中的#pragma CODE_SECTION(InitFlash, “ramfuncs”);。使得InitFlash( )函数被分配到.test而不是ramfuncs。

    8.删除Example_280xFlash.c文件中的#pragma CODE_SECTION(epwm1_timer_isr, “ramfuncs”);和#pragma CODE_SECTION(epwm2_timer_isr, “ramfuncs”);。使得中断服务函数被分配到.test而不是ramfuncs。

    9.删除Example_280xFlash.c文件中的MemCopy(&RamfuncsLoadStart, &RamfuncsLoadEnd, &RamfuncsRunStart);和
    InitFlash( );。由于代码已经被复制到RAM,这些是不需要的了。

    10.如上所述,编译连接程序,把程序下到芯片里运行。


    eZdsp电路板上的LED闪烁,表示程序正在运行。


    存储空间占用:

           因为仅仅在DSP28xxx_SectionCopy_nonBIOS.asm文件中增加了copy_sections的代码。增加的占用的片内flash为0x3C。code_start 和wd_disable函数没有增加额外的代码,他们本来就在C/C++ Header Files and Peripheral Examples的所用项目中被使用。



    测试:

         因为这个功能开机后直接实现,闪存等待状态,锁相环(PLL)都没有配置,因此,它们都运行默认值Flash等待状态15个周期对于F280xx/F281x设备SYSCLKOUTOSCCLK/2,对于F2833x设备SYSCLKOUTOSCCLK/ 4使Code Composer Studio分析功能可以测量运行时间。下表给出了每个F28xxx 控制器启动到main()函数的第一个指令所用的时间,如下所示,由于个平台的代码长度和系统时钟不一样,他们的运行时间也不一样。



    限制:

           此实现的限制因素使用的TMS320F28xxx控制器内部RAM的大小。这限制了那些工程可以使用这种方法,如果工程太大,以至于没法放进RAM里,这种方法是不能用的。


    建议:

           有一些项目需要这种功能,但不是所有被初始化段都要复制到RAM或者没有足够的RAM放下所有的段。仅仅需要复制应用代码本身。这种情况下,仅仅需要复制.text段到RAM。这样子,可以把DSP28xxx_SectionCopy_nonBIOS.asm文件和cmd文件中复制其他段的代码删掉,把其他段放在flash中运行。减少flash的占用空间和缩短了运行到main()的时间。

           应该确定应用程序可以处理复制代码执行时间一点滞后。如果应用程序不能处理这段时间,可以使用Running an Application from Internal Flash Memory on the TMS320F28xx DSP (SPRA958)中的方法复制一部分主要的代码到ram。

           如果使用DSP的引导,建议使用Running an Application from Internal Flash Memory on the TMS320F28xx DSP (SPRA958)中的方法复制一部分主要的代码到ram。一个使用DSP / BIOS的项目,通常是一个较大的项目,不建议使用此方案。


    结语:

            这份应用文档展示,在建立C语言环境之前,通过把flash的代码复制到ram,可以使TMS320F28xxx的控制器实现等待状态运行。这方案给出了代码和存储空间的限制,为设计者提供了实现了这种功能的相关文件。




    展开全文
  •  这个应用报告和相关的代码提供了一种把编译程序段从TMS320F28xxx的flash复制到ram的功能,这样可以提高代码的运行速度。这个解决方案在直接启动之后,进入c_int00 ——C语言代码运行之前实现此功能。  ...
  • 程序运行时内存的各种数据

    千次阅读 2017-11-19 21:12:18
    有趣的是这个中的变量只占用程序运行时的内存空间,而不占用程序文件的储存空间。可以用以下程序来说明这点,文件名为bss.c [cpp] view plain copy #include    int bss_
  • 一个java程序是怎样运行起来的(3)

    万次阅读 2017-11-30 22:36:09
    一个java程序是怎样执行
  • 下面程序运行结果是: main() { int x,i,j; for(i=1;i<=100;i++) { x=i; if(++x%20) if(++x%30) if(++x%7==0) printf("%d ",x); } } A)39 81 B)42 84 C)26 68 D) 28 70 nt x,i; //定义两个整型变量dao for(i=1;i...
  • 程序是怎样运行

    千次阅读 2010-11-10 14:14:00
    此文写给希望了解c程序是如何运行的人。 但是了解一样东西的本来面貌是一个递归的过程,要理解a,就必须先弄懂b,而弄懂b还要知道c……一直要看到最后的东西才能开始向上层返回。所以我十分佩服国内那些编教材...
  • 程序的三的解释

    2013-03-29 11:43:38
    一个程序主要包括下面三个:代码,数据,BSS   代码:指令代码(局部变量也放在代码)。 数据:有初始(并且不为0)的全局变量或静态变量。 BSS:初始化为0或无初始的全局或静态变量。  ...
  • 一个程序在计算机中到底是如何运行的?

    万次阅读 多人点赞 2016-09-22 23:46:09
    在《载入内存,让程序运行起来》一节中讲到,程序是保存在硬盘中的,要载入内存才能运行,CPU也被设计为只能从内存中读取数据和指令。 对于CPU来说,内存仅仅是一个存放指令和数据的地方,并不能在内存中完成计算...
  • C语言小程序--交换两个变量的

    万次阅读 2016-03-16 20:58:35
    例如:a=10,b=20 我们想要交换a与b,可以借助一个第三变量temp,把a的附给temp,把b给a,再把temp的b,就完成了交换。 下面用代码实现: #include int main() {  int a = 10, b = 20;  int temp; ...
  • 程序运行时的内存空间分布

    万次阅读 多人点赞 2014-03-21 01:33:18
    我们在写程序时,既有程序的逻辑代码,也有在程序中定义的变量等数据,那么当我们的程序进行时,我们的代码和数据究竟是存放在哪里的呢?下面就来总结一下。 一、程序运行时的内存空间情况 其实在程序运行时,...
  • C语言程序运行

    千次阅读 2016-12-28 21:46:54
    一、程序运行类型(下面有详细介绍) 在嵌入式系统中,经过编译的C语言程序可以通过操作系统运行,也可以在没有操作系统的情况下运行程序存放的位置和运行的位置通常是不一样的。一般情况下,经过编译程序...
  • python两个数值互换(浅析a,b=b,a原理)

    万次阅读 多人点赞 2017-11-13 17:02:18
    python交换两个值得方法非常简单,即a,b=b,a,一步操作就交换了两个,那么这是为什么呢?...先看下面段程序:import disdef func(a,b): a,b=b,a print(a,b)a=10 b=20 func(a,b) dis.dis(func) 一般来说一个P
  • 在阅读完《深入理解计算机系统》第一章(计算机系统漫游)、第七章(链接)以及第十章(虚拟存储器)和《程序员的自我修养——链接、装载与库》,历时悠久的梦想终于要实现了。开篇之初,首先提出一个迷惑了很久的...
  • Java程序执行流程三

    千次阅读 2019-01-02 10:39:52
    简单说来,一个java程序运行需要编辑源码、编译生成...下面有一简单的java源码,通过它来看一下java程序运行流程: 1 class Person 2 3 { 4 5 private String name; 6 7 private int age; 8 ...
  • 本文总结自己的实际经验,分析了“DSP程序在RAM中正常运行但在Flash烧写无法正常运行”的几种原因或情况。
  • 文件格式 现在PC平台流行的可执行程序格式,主要是Windows下...一般情况下,一个可执行二进制程序(更确切的说,在Linux操作系统下为一个进程单元)在存储(没有调入到内存运行)时拥有3个部分,分别是代码(text)、数...
  • 为了弄清楚这个问题,先看看...文本:文本中存放的是代码,只读数据,字符串常量(我们通常说保存在文字常量中,实际就是在文本)数据:数据用来存放可执行文件中已经初始化的全局变量,全局变量又可细分为全
  • 从编写源代码到程序在内存中运行的全过程解析

    万次阅读 多人点赞 2018-04-21 17:49:51
    作为一个C/C++程序员,搞清楚从编写源代码到程序运行过程中发生的细节是很有必要的。这在之前也是困扰我的一个很大问题,因为最近在忙着找实习,一直没有下定决心来写这篇博客,最近才抽时间写。下面的代码除了明显...
  • php执行linux命令、shell脚本。以获取服务器运行状态举栗子
  • 编写一个控制台应用程序,完成下列功能,并写出运行程序后输出的结果。1) 创建一个类A,在A中编写一个可以被重写的带int类型参数的方法MyMethod,并在该方法中输出传递的整型加10的结果。2) 再创建一个类B,使其...
  • 我们接下来从安全性,性能,功能,可移植性的角度分别分析Android系统为应用程序提供的支撑。 回顾前情: Android应用程序开发以及背后的设计思想深度剖析(1) Android应用程序开发以及背后的设计
  • 提高程序运行效率的10个简单方法

    万次阅读 2018-06-13 21:18:20
    但是这并不等于我们可以忽略程序运行效率,下面就介绍一下本人积累的一些简单实用的提高程序运行效率的方法,希望对大家有所帮助。注:以C/C++程序为例一、尽量减少传递,多用引用来传递...
  • 计算机程序执行和计算

    千次阅读 2018-04-19 09:55:15
    本文通过一个简单的加法的程序,从软硬件层面阐述计算机是如何进行程序执行调用、数值计算的。1 概述 现代程序员学习的计算机程序语言一般是高级语言,是人类可以理解但计算机无法理解的,计算机只能识别高电平和低...
  • 测试程序运行时间

    万次阅读 2011-06-14 15:26:00
    测试程序运行时间-time.h1.计时 C/C++中的计时函数是clock(),而与其相关的数据类型是clock_t。在MSDN中,查得对clock函数定义如下: clock_t clock( void ); 这个函数返回从“开启这个程序进程”到“程序中调用...
  • 关于ios程序的后台运行

    万次阅读 2013-03-14 16:21:21
    我从苹果文档中得知,一般的应用在进入后台的时候可以获取一定时间来运行相关任务,也就是说可以在后台运行一小时间。  还有三种类型的可以运行以,  1.音乐  2.location  3.voip  文二  在...
  • c语言测试程序执行时间

    千次阅读 2017-06-04 10:55:28
    测试程序运行时间-time.h 1.计时 C/C++中的计时函数是clock(),而与其相关的数据类型是clock_t。在MSDN中,查得对clock函数定义如下: clock_t clock( void ); 这个函数返回从“开启这个程序进程”
  • 深入理解程序从编译到运行

    万次阅读 多人点赞 2017-10-17 10:47:18
    From:... 从Hello World说程序运行机制:http://www.sohu.com/a/132798003_505868 C/C++中如何在main()函数之前执行一条语句?:https://www.zhihu.com/question/26031933 (深入理解计算机系统...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 435,703
精华内容 174,281
关键字:

下面程序段执行后b的值是