首页 | 资讯动态 | linux基础 | 系统管理 | 网络管理 | 编程开发 | linux数据库 | 服务器技术 | linux相关 | linux认证 | 嵌入式 | 下载中心 | 专题 | linux招聘 | 镜像站
OKLinux中文技术站
·设为首页
·加入收藏
·联系我们
系统管理: 中文环境 系统管理 桌面应用 内核技术 | Linux基础: 基础入门 安装配置 常用命令 经验技巧 软件应用 | Linux数据库: Mysql Postgre Oracle DB2 Sybase other
网络管理: 网络安全 网络应用 Linux服务器 环境配置 黑客安全 | 编程开发: PHP CC++ Python Perl Shell 嵌入式开发 java jsp | PHP技术: PHP基础 PHP技巧 PHP应用 PHP文摘
Linux资讯 Linux招聘 Linux专题 Apache | Linux相关: 硬件相关 Linux解决方案 Linux认证 企业应用 其它Unix | 相关下载: 资料下载 参考手册 开发工具 服务器类 软路由 其它
 技术搜索:
会员中心 注册会员 高级搜索  
  → 当前位置:首页>linux基础>基础入门>正文

应用程序在Linux上的执行过程

http://www.oklinux.cn  2007-12-27  来源:  korn  会员收藏  游客收藏  【 】 

执行文件是如何在shell中被"执行"的。本文中尽可能少用一些源码,免得太过于无聊,主要讲清这个过程,感兴趣的同学可以去查看相应的源码了解更多的信息。

 

1.父进程的行为: 复制,等待

执行应用程序的方式有很多,从shell中执行是一种常见的情况。交互式shell是一个进程(所有的进程都由pid号为1的init进程fork得到,关于这个话题涉及到Linux启动和初始化,以及idle进程等,有空再说),当在用户在shell中敲入./test执行程序时,shell先fork()出一个子进程(这也是很多文章中说的子shell),并且wait()这个子进程结束,所以当test执行结束后,又回到了shell等待用户输入(如果创建的是所谓的后台进程,shell则不会等待子进程结束,而直接继续往下执行)。所以shell进程的主要工作是复制一个新的进程,并等待它的结束。

 

2.子进程的行为: "执行"应用程序

2.1 execve()

另一方面,在子进程中会调用execve()加载test并开始执行。这是test被执行的关键,下面我们详细分析一下。

 

execve()是操作系统提供的非常重要的一个系统调用,在很多文章中被称为exec()系统调用(注意和shell内部exec命令不一样),其实在Linux中并没有exec()这个系统调用,exec只是用来描述一组函数,它们都以exec开头,分别是:

 

  #include

  int execl(const char *path, const char *arg, ...);

  int execlp(const char *file, const char *arg, ...);

  int execle(const char *path, const char *arg, ..., char *const envp[]);

  int execv(const char *path, char *const argv[]);

  int execvp(const char *file, char *const argv[]);

  int execve(const char *path, char *const argv[], char *const envp[]);

 

这几个都是都是libc中经过包装的的库函数,最后通过系统调用execve()实现(#define __NR_evecve 11,编号11的系统调用)。

 

exec函数的作用是在当前进程里执行可执行文件,也就是根据指定的文件名找到可执行文件,用它来取代当前进程的内容,并且这个取代是不可逆的,即被替换掉的内容不再保存,当可执行文件结束,整个进程也随之僵死。因为当前进程的代码段,数据段和堆栈等都已经被新的内容取代,所以exec函数族的函数执行成功后不会返回,失败是返回-1。可执行文件既可以是二进制文件,也可以是可执行的脚本文件,两者在加载时略有差别,这里主要分析二进制文件的运行。

  

2.2 do_execve()

在用户态下调用execve(),引发系统中断后,在内核态执行的相应函数是do_sys_execve(),而do_sys_execve()会调用do_execve()函数。do_execve()首先会读入可执行文件,如果可执行文件不存在,会报错。然后对可执行文件的权限进行检查。如果文件不是当前用户是可执行的,则execve()会返回-1,报permission denied的错误。否则继续读入运行可执行文件时所需的信息(见struct linux_binprm)。

 

2.3 search_binary_handler()

接着系统调用search_binary_handler(),根据可执行文件的类型(如shell,a.out,ELF等),查找到相应的处理函数(系统为每种文件类型创建了一个struct linux_binfmt,并把其串在一个链表上,执行时遍历这个链表,找到相应类型的结构。如果要自己定义一种可执行文件格式,也需要实现这么一个handler)。然后执行相应的load_binary()函数开始加载可执行文件。

 

2.4 load_elf_binary()

加载elf类型文件的handler是load_elf_binary(),它先读入ELF文件的头部,根据ELF文件的头部信息读入各种数据(header information)。再次扫描程序段描述表,找到类型为PT_LOAD的段,将其映射(elf_map())到内存的固定地址上。如果没有动态链接器的描述段,把返回的入口地址设置成应用程序入口。完成这个功能的是start_thread(),start_thread()并不启动一个线程,而只是用来修改了pt_regs中保存的PC等寄存器的值,使其指向加载的应用程序的入口。这样当内核操作结束,返回用户态的时候,接下来执行的就是应用程序了。

 

2.5 load_elf_interp()

如果应用程序中使用了动态链接库,就没有那么简单了,内核除了加载指定的可执行文件,还要把控制权交给动态连接器(program interpreter,ld.so in linux)以处理动态链接的程序。内核搜寻段表,找到标记为PT_INTERP的段中所对应的动态连接器的名称,并使用load_elf_interp()加载其映像,并把返回的入口地址设置成load_elf_interp()的返回值,即动态链接器入口。当execve退出的时候动态链接器接着运行。动态连接器检查应用程序对共享连接库的依赖性,并在需要时对其进行加载,对程序的外部引用进行重定位。然后动态连接器把控制权交给应用程序,从ELF文件头部中定义的程序进入点开始执行。(比如test.c中使用了userlib.so中函数foo(),在编译的时候这个信息被放进了test这个ELF文件中,相应的语句也变成了call fakefoo()。当加载test的时候,知道foo()是一个外部调用,于是求助于动态链接器,加载userlib.so,解析foo()函数地址,然后让fakefoo()重定向到foo(),这样call foo()就成功了。)

 

简短的说,整个在shell中键入./test执行应用程序的过程为:当前shell进程fork出一个子进程(子shell),子进程使用execve来脱离和父进程的关系,加载test文件(ELF格式)到内存中。如果test使用了动态链接库,就需要加载动态链接器(或者叫程序解释器),进一步加载test使用到的动态链接库到内存,并重定位以供test调用。最后从test的入口地址开始执行test。

 

PS: 现代的动态链接器因为性能等原因都采用了延迟加载和延迟解析技术,延迟加载是动态连接库在需要的时候才被加载到内存空间中(通过页面异常机制),延迟解析是指到动态链接库(以加载)中的函数被调用的时候,才会去把这个函数的起始地址解析出来,供调用者使用。动态链接器的实现相当的复杂,为了性能等原因,对堆栈的直接操作被大量使用,感兴趣的可以找相关的代码看看。

 

 


上一篇:Ubuntu系统下Trac+Subversion+Apache安装   下一篇:Linux下的定时器:alarm()与setitimer()


收藏于收藏夹】 【评论】 【推荐】 【打印】 【关闭
相关文档
·Linux操作系统下光盘刻录方法
·Linux系统下硬盘挂载详细说明
·使用Smartmontools工具来检测硬盘的状态
·Linux系统中的RAD工具概括介绍
·如何查看Linux操作系统的版本
·关于Linux 内核中五个主要子系统的介绍
·Linux系统各个目录的一般作用
·Linux系统平台下用Fdisk分区格式化硬盘
·Linux系统下如何查看及修改文件读写权限
·Linux系统的开机程序执行顺序
·关于Linux操作系统的网卡编译
·Linux操作系统的RPM使用详解
·Linux操作系统的错误诊断方法
·概括介绍Linux支持的文件系统
·Linux操作系统默认打开文件数
·Linux操作系统中连接库的生成
发表评论
密码: 匿名评论
评论内容:

(不超过250字,需审核后才会公布,请自觉遵守互联网相关政策法规)
 
  最新文档
·Ubuntu Linux系统的启动过程
·Ubuntu Linux系统下自启动选项设置方法
·Linux系统下文件和文件夹的复制问题
·Linux下如何解包和打包initrd image文
·虚拟机中Linux不能引导的问题
·Linux系统下修改字符集的方法
·Linux操作系统中连接库的生成
·Linux操作系统默认打开文件数
·Linux操作系统的错误诊断方法
·概括介绍Linux支持的文件系统
·Linux操作系统的RPM使用详解
·关于Linux操作系统的网卡编译
  阅读排行
·Linux的GRUB引导程序配置方法大全
·新手从零开始:Linux系统命令的使用
·Linux下tar bz gz等压缩包的压缩和解压
·从硬盘启动Desktop ISO所需的引导文件
·Linux系统面面观 PROC文件系统详细介绍
·Unix Shell程序编写的方法详解
·深入理解软件包的配置、编译与安装方法
·Linux操作系统下关于引导和初始化的问
·Linux操作系统的命令分类介绍
·Linux基础知识普及 理解GNU/Linux含义
·新手看招 Unix和Linux下C语言学习指南
·Linux命令Man解释Lilo:安装引导装载程
·Linux系统的文件属性详细解析(二)
·Linux下挂载硬盘光驱和U盘的方法
·在Redhat AS4中加载NTFS分区的方法详解
网摘收藏: