程序的动态执行过程
来源:互联网 发布:js math 向下取整 编辑:程序博客网 时间:2024/05/01 06:26
1.程序的开始
放在外存上的二进制文件是如何开始运行的,从什么地方开始运行,是一个值得深究的问题。对于编译型的语言,从源代码到可执行文件,一般都会经过预处理,汇编,编译,链接等过程,最后生成可执行文件。对于C语言,逻辑层面上,main函数是程序的入口。在L/Uinux系统下,把二进制可执行文件从外存读入内存,并跳转到main函数的工作则是由内核的exec系统调用来完成。exec函数的作用是用新的程序替换系统当前进程的执行程序,包括代码,数据,堆和栈。简而言之,exec用来执行新的程序。
1.1 exec系统调用
Linux Kernel exec系统调用申明在include/linux/syscalls.h文件中。
三个参数的含义分别是:
新程序的绝对路径(ex: /usr/bin/ls)
新程序用到的参数
环境变量指针
不同体系结构下,kernel_execve的实现也不同,arm体系结构下的代码实现在arch/arm/kernel/sys_arm.c文件中。
kernel_execve处理系统调用从用户态到内核态切换相关工作,并调用do_execve函数处理执行程序的任务。
do_execve函数定义在fs/exec.c文件中,在do_execve函数中又封装了一层,最终调用do_execve_common函数,do_execve_common函数也定义在fs/exec.c文件中。整个过程的实现涉及到很多的细节问题。
后面在写一篇详细分析一下exec涉及到的内容。
粗略概括起来做了如下几件事情:
1. 对新程序执行进程的证书,权限等信息的检查和控制
2. 可执行文件的检索,读取等
3. 可执行文件个格式的检测和匹配,例如shell脚本或者elf文件
4. 更新进程的运行环境,例如堆栈的分配
5. 可执行文件的加载和执行
总结起来,exec函数是操作系统提供给用户态程序的一个门。用户态的程序可以通过这个门执行。当然也可以直接从内核态来运行用户态的程序,Linux内核的khelper就是一个典型的例子。
2. 程序的结束
进程的退出,比较关键的问题是资源的回收。APUE列出了8种进程退出的方式:
a) 从主函数返回
b) 调用 exit
c) 调用 _exit或者_Exit
d) 从最后一个执行线程返回
e) 调用 pthread_exit
f) 调用abort
g) 接收到信号,例如Ctrl+c
h) 最后一个执行线程的cancellation 请求
这八种退出方式可以分成三类,正常返回,线程退出返回,异常返回。进程主动退出唯一的方式就是通过调用exit函数族。
2.1 exit系统调用
exit系统 调用的实现是do_exit函数。do_exit函数定义在kernel/exit.c文件中。do_exit函数主要用于释放进程的代码段和数据段的内存页,向父进程发送子进程退出信号(SIGCHLD),关闭当前进程打开的文件描述符,释放终端设备等资源。
3. 程序的运行环境
系统环境变量是程序运行是可以获取到的系统信息资源,每个系统变量都被保存为一个以NULL结尾的字符串,并且有getenv和setenv函数做存储访问。在windows环境下也有类似的东西。exec系统调用会同时把命令行参数和环境变量写入用户进程的地址空间。
3.1 程序在进程地址空间的内存布局
C程序在编译完成后就划分成了代码段,数据段,BSS段,在运行时又有栈和堆。C程序在进程地址空间的典型布局如图:
3.2 Linux堆栈的形态
3.3 用户态堆栈运行时节奏
代码和数据在C语言中是对立的概念。代码和数据的区别可以理解为编译时和运行时的分界线。编译器的绝大部分工作都跟翻译代码有关,而必要的数据存储管理都是在运行时进行的。一个有经过编译链接后生成的ELF或者a.out文件是没有记录局部变量的值的,也不存在堆栈段,堆栈段是运行时的数据结构。函数是C语言的工作的基本单位,函数的参数和局部变量的操作,函数的调用/返回等行为用符合堆栈这种数据结构的特性。因此理解运行是堆栈的变化过程就是对C语言语境的深刻理解。
一个C函数通常有几个基本要素:参数,auto变量,临时变量,调用子函数,返回到主函数。在进程的地址空间中,堆栈的生长是随着函数调用而生长的。堆栈的生长方向由CPU的体系结构决定。函数的基本要素都保存在堆栈中,每个函数在被调用的过程中都会在堆栈中创建相应的stack frame来保存这些基本要素,返回后,占用的堆栈空间则别释放掉。
活动记录的基本描述如下:
局部变量(local variables)参数(argument)指向主调函数活动记录指针返回地址(return address一个简单递归调用的函数如下:
a(int i){ if(i>0) a(--i); else printf(....);}main(){ a(1);}
对应的堆栈变化如下:
所以,C语言函数的调用,就像夹带着一个stack frame,从程序的一个地方跳到另外一个地方。而在同一个进程中执行多个不同的线程,则只需要为每个线程分配不同的堆栈即可。
- 程序的动态执行过程
- 程序的执行过程
- 程序的执行过程
- 程序的执行过程
- 程序的执行过程
- 程序的执行过程
- 程序的执行过程
- 程序的执行过程
- 程序的执行过程
- 程序的执行过程
- WEB程序的执行过程!
- 单片机执行程序的过程
- python程序的执行过程
- 程序执行的详细过程
- 计算机程序的执行过程
- C++程序的执行过程
- C++程序的执行过程
- php程序的执行过程
- 那些看似惊人但却值得回味的编程观点……
- SQL生成时间临时表
- linux socket编程-server
- 获取spring为注入的service
- ubuntu vim终端编辑命令
- 程序的动态执行过程
- Java基础知识
- ZOJ P1037 HDOJ P1046 Gridland
- Mybatis源码研究序
- HashMap用法
- linux socket编程-client
- vs2010快捷键
- 八旬拾荒婆婆捐万元助学 蜗居8平米地下室
- InstallAnywhere 简介