1:程序的运行
程序设计语言----------->编译器,链接器---------->机器语言----------->操作系统加载到内存中(文件系统,内存管理)---------->操作系统任务管理与任务调度------------>操作系统输入输出------------------->结果
看到程序运行的结构图,不由得想到C语言编译过程
C语言过程如下:
.c.h文件-——>预处理——>.i文件——>编译器——>.s文件——>汇编器——>.o文件——>链接器对.so.a文件链接——>执行文件。
以linux gcc为例:(system :Ubuntu 14.04, gcc version:4.8.2 )
如程序代码mynum.c
#include <stdio.h>
#define mynum 123456789
int main(void)
{
printf("%d\n",mynum);
return 0;
}
经过gcc -E mynum.c -o mynum.i可得如下内容
# 2 "mynum.c" 2
int main(void)
{
printf("%d\n",123456789);
return 0;
}实现了预处理
在此基础上进行编译,gcc -S mynum.i -o mynum.s,得到汇编文件如下
.file "mynum.c"
.section .rodata
.LC0:
.string "%d\n"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $123456789, %esi
movl $.LC0, %edi
movl $0, %eax
call printf
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
.section .note.GNU-stack,"",@progbits
再对其生成目标文件 gcc -c mynum.s -o mynum.o,此时利用vim打开或者用cat显示mynum.o为乱码
gcc mynum.o -o mynum 即生成执行文件 此时文件格式为elf 如果使用交叉编译,生成的执行文件格式一般为二进制文件
链接过程在哪呢?利用命令readelf -d mynum 我们可知我们动态链接库位libc.so.6,其实gcc 默认使用的就是童泰链接
2:程序的内存布局
===================================一下内容根据国嵌Linux应用程序地址布局
小端模式下
代码段 数据段 BSS段 堆 栈 堆先上增长,栈向下增长
我们可以通过编写代码查看数据是如何在内存分配的
其中memory.c代码如下
#include <stdio.h>
#include <stdlib.h>
int global_init_a = 1;
int global_uinit_a;
static int static_global_init_a = 1;
static int static_global_uinit_a;
const int const_global_a = 1;
int global_init_b = 1;
int global_uinit_b;
static int static_global_init_b = 1;
static int static_global_uinit_b;
const int const_global_b = 1;
int main(void)
{
int local_init_a = 1;
int local_uinit_a;
static int static_local_init_a = 1;
static int static_local_uinit_a;
const int const_local_a = 1;
int local_init_b = 1;
int local_uinit_b;
static int static_local_init_b = 1;
static int static_local_uinit_b;
const int const_local_b = 1;
int* malloc_p_a;
malloc_p_a = malloc(sizeof(int));
printf("&global_init_a = %p, global_init_a = %d\n",&global_init_a,global_init_a);
printf("&global_uinit_a = %p, global_uinit_a = %d\n",&global_uinit_a,global_uinit_a);
printf("&static_global_init_a = %p, static_global_init_a = %d\n",&static_global_init_a,static_global_init_a);
printf("&static_global_uinit_a = %p, static_global_uinit_a = %d\n",&static_global_uinit_a,static_global_uinit_a);
printf("&const_global_a = %p, const_global_a = %d\n",&const_global_a,const_global_a);
printf("&local_init_a = %p, local_init_a = %d\n",&local_init_a,local_init_a);
printf("&local_uinit_a = %p, local_uinit_a = %d\n",&local_uinit_a,local_uinit_a);
printf("&static_local_init_a = %p, static_local_init_a = %d\n",&static_local_init_a,static_local_init_a);
printf("&static_local_uinit_a = %p, static_local_uinit_a = %d\n",&static_local_uinit_a,static_local_uinit_a);
printf("&const_local_a = %p, const_local_a = %d\n",&const_local_a,const_local_a);
while(1);
return 0;
}
在linux下利用 cat /proc/memory进程号/maps查看代码段 数据段 BSS段 堆 栈的地址范围,再由memory各种类型打印的地址对应存储区域
08048000-08049000 r-xp 00000000 08:01 1052508 /home/liuyanhit/memory//代码段
08049000-0804a000 r--p 00000000 08:01 1052508 /home/liuyanhit/memory
0804a000-0804b000 rw-p 00001000 08:01 1052508 /home/liuyanhit/memory//数据段
08a7f000-08aa0000 rw-p 00000000 00:00 0 [heap]//堆
b7519000-b751a000 rw-p 00000000 00:00 0
b751a000-b76c3000 r-xp 00000000 08:01 394206 /lib/i386-linux-gnu/libc-2.19.so
b76c3000-b76c5000 r--p 001a9000 08:01 394206 /lib/i386-linux-gnu/libc-2.19.so
b76c5000-b76c6000 rw-p 001ab000 08:01 394206 /lib/i386-linux-gnu/libc-2.19.so
b76c6000-b76c9000 rw-p 00000000 00:00 0
b76dd000-b76e0000 rw-p 00000000 00:00 0
b76e0000-b76e1000 r-xp 00000000 00:00 0 [vdso]
b76e1000-b7701000 r-xp 00000000 08:01 394182 /lib/i386-linux-gnu/ld-2.19.so
b7701000-b7702000 r--p 0001f000 08:01 394182 /lib/i386-linux-gnu/ld-2.19.so
b7702000-b7703000 rw-p 00020000 08:01 394182 /lib/i386-linux-gnu/ld-2.19.so
bfadc000-bfafd000 rw-p 00000000 00:00 0 [stack]//栈
&global_init_a = 0x804a024, global_init_a = 1//数据段
&global_uinit_a = 0x804a03c, global_uinit_a = 0//数据段
&static_global_init_a = 0x804a028, static_global_init_a = 1//数据段
&static_global_uinit_a = 0x804a034, static_global_uinit_a = 0//数据段
&const_global_a = 0x8048630, const_global_a = 1//代码段
&local_init_a = 0xbfafbdf0, local_init_a = 1//栈
&local_uinit_a = 0xbfafbdf4, local_uinit_a = -1217388544//栈
&static_local_init_a = 0x804a02c, static_local_init_a = 1//数据段
&static_local_uinit_a = 0x804a038, static_local_uinit_a = 0//数据段
&const_local_a = 0xbfafbdf8, const_local_a = 1//栈
最后发现
1.代码段:代码,全局常量(const)、字符串常量
2.数据段:全局变量(初始化以及未初始化的)、静态变量(全局的和局部的、初始化的以及未初始化的)
3.堆:动态分配的区域
4.栈:局部变量(初始化以及未初始化的,但不包含静态变量)、局部只读变量(const)
程序设计语言----------->编译器,链接器---------->机器语言----------->操作系统加载到内存中(文件系统,内存管理)---------->操作系统任务管理与任务调度------------>操作系统输入输出------------------->结果
看到程序运行的结构图,不由得想到C语言编译过程
C语言过程如下:
.c.h文件-——>预处理——>.i文件——>编译器——>.s文件——>汇编器——>.o文件——>链接器对.so.a文件链接——>执行文件。
以linux gcc为例:(system :Ubuntu 14.04, gcc version:4.8.2 )
如程序代码mynum.c
#include <stdio.h>
#define mynum 123456789
int main(void)
{
printf("%d\n",mynum);
return 0;
}
经过gcc -E mynum.c -o mynum.i可得如下内容
# 2 "mynum.c" 2
int main(void)
{
printf("%d\n",123456789);
return 0;
}实现了预处理
在此基础上进行编译,gcc -S mynum.i -o mynum.s,得到汇编文件如下
.file "mynum.c"
.section .rodata
.LC0:
.string "%d\n"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $123456789, %esi
movl $.LC0, %edi
movl $0, %eax
call printf
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
.section .note.GNU-stack,"",@progbits
再对其生成目标文件 gcc -c mynum.s -o mynum.o,此时利用vim打开或者用cat显示mynum.o为乱码
gcc mynum.o -o mynum 即生成执行文件 此时文件格式为elf 如果使用交叉编译,生成的执行文件格式一般为二进制文件
链接过程在哪呢?利用命令readelf -d mynum 我们可知我们动态链接库位libc.so.6,其实gcc 默认使用的就是童泰链接
2:程序的内存布局
===================================一下内容根据国嵌Linux应用程序地址布局
小端模式下
代码段 数据段 BSS段 堆 栈 堆先上增长,栈向下增长
我们可以通过编写代码查看数据是如何在内存分配的
其中memory.c代码如下
#include <stdio.h>
#include <stdlib.h>
int global_init_a = 1;
int global_uinit_a;
static int static_global_init_a = 1;
static int static_global_uinit_a;
const int const_global_a = 1;
int global_init_b = 1;
int global_uinit_b;
static int static_global_init_b = 1;
static int static_global_uinit_b;
const int const_global_b = 1;
int main(void)
{
int local_init_a = 1;
int local_uinit_a;
static int static_local_init_a = 1;
static int static_local_uinit_a;
const int const_local_a = 1;
int local_init_b = 1;
int local_uinit_b;
static int static_local_init_b = 1;
static int static_local_uinit_b;
const int const_local_b = 1;
int* malloc_p_a;
malloc_p_a = malloc(sizeof(int));
printf("&global_init_a = %p, global_init_a = %d\n",&global_init_a,global_init_a);
printf("&global_uinit_a = %p, global_uinit_a = %d\n",&global_uinit_a,global_uinit_a);
printf("&static_global_init_a = %p, static_global_init_a = %d\n",&static_global_init_a,static_global_init_a);
printf("&static_global_uinit_a = %p, static_global_uinit_a = %d\n",&static_global_uinit_a,static_global_uinit_a);
printf("&const_global_a = %p, const_global_a = %d\n",&const_global_a,const_global_a);
printf("&local_init_a = %p, local_init_a = %d\n",&local_init_a,local_init_a);
printf("&local_uinit_a = %p, local_uinit_a = %d\n",&local_uinit_a,local_uinit_a);
printf("&static_local_init_a = %p, static_local_init_a = %d\n",&static_local_init_a,static_local_init_a);
printf("&static_local_uinit_a = %p, static_local_uinit_a = %d\n",&static_local_uinit_a,static_local_uinit_a);
printf("&const_local_a = %p, const_local_a = %d\n",&const_local_a,const_local_a);
while(1);
return 0;
}
在linux下利用 cat /proc/memory进程号/maps查看代码段 数据段 BSS段 堆 栈的地址范围,再由memory各种类型打印的地址对应存储区域
08048000-08049000 r-xp 00000000 08:01 1052508 /home/liuyanhit/memory//代码段
08049000-0804a000 r--p 00000000 08:01 1052508 /home/liuyanhit/memory
0804a000-0804b000 rw-p 00001000 08:01 1052508 /home/liuyanhit/memory//数据段
08a7f000-08aa0000 rw-p 00000000 00:00 0 [heap]//堆
b7519000-b751a000 rw-p 00000000 00:00 0
b751a000-b76c3000 r-xp 00000000 08:01 394206 /lib/i386-linux-gnu/libc-2.19.so
b76c3000-b76c5000 r--p 001a9000 08:01 394206 /lib/i386-linux-gnu/libc-2.19.so
b76c5000-b76c6000 rw-p 001ab000 08:01 394206 /lib/i386-linux-gnu/libc-2.19.so
b76c6000-b76c9000 rw-p 00000000 00:00 0
b76dd000-b76e0000 rw-p 00000000 00:00 0
b76e0000-b76e1000 r-xp 00000000 00:00 0 [vdso]
b76e1000-b7701000 r-xp 00000000 08:01 394182 /lib/i386-linux-gnu/ld-2.19.so
b7701000-b7702000 r--p 0001f000 08:01 394182 /lib/i386-linux-gnu/ld-2.19.so
b7702000-b7703000 rw-p 00020000 08:01 394182 /lib/i386-linux-gnu/ld-2.19.so
bfadc000-bfafd000 rw-p 00000000 00:00 0 [stack]//栈
&global_init_a = 0x804a024, global_init_a = 1//数据段
&global_uinit_a = 0x804a03c, global_uinit_a = 0//数据段
&static_global_init_a = 0x804a028, static_global_init_a = 1//数据段
&static_global_uinit_a = 0x804a034, static_global_uinit_a = 0//数据段
&const_global_a = 0x8048630, const_global_a = 1//代码段
&local_init_a = 0xbfafbdf0, local_init_a = 1//栈
&local_uinit_a = 0xbfafbdf4, local_uinit_a = -1217388544//栈
&static_local_init_a = 0x804a02c, static_local_init_a = 1//数据段
&static_local_uinit_a = 0x804a038, static_local_uinit_a = 0//数据段
&const_local_a = 0xbfafbdf8, const_local_a = 1//栈
最后发现
1.代码段:代码,全局常量(const)、字符串常量
2.数据段:全局变量(初始化以及未初始化的)、静态变量(全局的和局部的、初始化的以及未初始化的)
3.堆:动态分配的区域
4.栈:局部变量(初始化以及未初始化的,但不包含静态变量)、局部只读变量(const)