Linux实验:计算机是怎么工作的


ID **超 SA1****256

测试环境

Ubuntu 12.10 

Linux-kernel: 3.5.0-17-generic

gcc 版本gcc.real (Ubuntu/Linaro 4.7.2-2ubuntu1) 4.7.2


本次为我的实验作业,并对linux进行了进一步的加深理解。

首先,概述一下编译链接的过程:C语言的编译链接过程是将程序源代码转换成可以运行的可执行文件,需要进行编译和链接。编译就是把文本形式源代码(高级语言指令)转换为功能等效的汇编代码。链接是把目标文件、操作系统的启动代码和用到的库文件链接最终生成二进制可执行代码的过程。完整过程可以描述如下():

1.预处理(.c)
2.编译成汇编代码(.s)
3.汇编成目标代码(.o)
4.链接(可执行文件)

实验代码:test.c源码

步骤一:

int g(int x)
{
	return x + 3;
}
int f(int x)
{
	return g(x);
}
 
int main()
{
	return f(8) + 1;
}


步骤二: gcc -E -o test.cpp test.c

# 1 "test.c"
# 1 "<command-line>"
# 1 "test.c"
int g(int x)
{
    return x + 3;
}

int f(int x)
{
    return g(x);
}

int main()
{
    return f(8) + 1;
}


步骤三:

然后是编译成汇编代码gcc -x cpp-output -S -o test.s test.cpp ,

其中参数-x说明根据指定的步骤进行工作,cpp-output指明从预处理得到的文件开始编译,-S说明生成汇编代码后停止工作。生成的汇编代码如下;

生成 test.s 如下

<span style="font-size:14px;"> .file   "test.c"  
    .text  
    .globl  g  
    .type   g, @function  
g:  
.LFB0:  
    .cfi_startproc  
    pushl   %ebp  
    .cfi_def_cfa_offset 8  
    .cfi_offset 5, -8  
    movl    %esp, %ebp  
    .cfi_def_cfa_register 5  
    movl    8(%ebp), %eax  
    addl    $3, %eax  
    popl    %ebp  
    .cfi_restore 5  
    .cfi_def_cfa 4, 4  
    ret  
    .cfi_endproc  
.LFE0:  
    .size   g, .-g  
    .globl  f  
    .type   f, @function  
.LFB1:  
    .cfi_startproc  
    pushl   %ebp  
    .cfi_def_cfa_offset 8  
    .cfi_offset 5, -8  
    movl    %esp, %ebp  
    .cfi_def_cfa_register 5  
    subl    $4, %esp  
    movl    8(%ebp), %eax  
    movl    %eax, (%esp)  
    call    g  
    leave  
    .cfi_restore 5  
    .cfi_def_cfa 4, 4  
    ret  
    .cfi_endproc  
.LFE1:  
    .size   f, .-f  
    .globl  main  
    .type   main, @function  
main:  
.LFB2:  
    .cfi_startproc  
    pushl   %ebp  
    .cfi_def_cfa_offset 8  
    .cfi_offset 5, -8  
    movl    %esp, %ebp  
    .cfi_def_cfa_register 5  
    subl    $4, %esp  
    movl    $8, (%esp)  
    call    f  
    addl    $1, %eax</span><pre name="code" class="cpp">    subl    $4, %esp  
    movl    $8, (%esp)</pre><br>  
leave .cfi_restore 5 .cfi_def_cfa 4, 4 ret .cfi_endproc.LFE2: .size main, .-main .ident "GCC: (Ubuntu/Linaro 4.7.2-2ubuntu1) 4.7.2" .section .note.GNU-stack,"",@progbits  

接下来我们来分析一下程序运行时栈的变化  (注:栈在内存中是从高地址往低地址的方向!)

程序入口为 main

说明:先是用pushl 命令将ebp 压入栈中,然后再讲esp的地址赋给ebp,即espebp现在都指向了栈顶。如下图所示


pushl   %ebp
movl     %esp, %ebp



在接下来看

   subl    $4, %esp
    movl    $8, (%esp)

因为栈是从高地址往低地址,

 subl    $4, %esp

esp往下移动4个地址,然后将8压入到esp所指的地址当中,见下图



然后是call f ,将eip入栈,即保存返回地址,然后将函数f的地址给eip,然后开始执行函数f



执行函数f的过程,首先仍然是保存旧栈,为了让之后能够顺利返回

pushl %ebp
movl %esp, %ebp

处理过程下图所示:



然后和之前类似

 subl    $4, %esp
 movl    8(%ebp), %eax
 movl    %eax, (%esp)

 过程如下图所示:



然后call g 过程如和上面调用f一样

pushl   %ebp
movl    %esp, %ebp

上面为保护入口地址



接来来的指令为计算,并将结果存入eax寄存器中

popl    %ebp
ret

返回 f



接下来执行

leave

ret

其中

leave等价为

movl %ebp , %esp;popl %ebp;

ret 等价为

popl %eip

所以栈中现在的情况如下图所示



回到main中继续执行以下代码

addl    $1, %eax

leave

ret

至此,结果存于eax寄存器当中,代码执行过程结束了。单任务模式:CPU通过读取EIP寄存器的值得值下一条指令的内存地址,整个过程中,始终由EIP保存将要执行的下一条指令的地址。函数调用过程中,需要保存现场,执行完后要返回,主要是通过EBPESP等寄存器实现的。多任务模式:则需要诸如中断,进程调度等结合分析。







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值