Linux加载一个可执行程序并启动的过程

原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

作者:严哲璟

以shell下执行ls命令为例介绍Linux通过fork()和execve()类函数的执行程序启动过程:

父进程为shell,命令为ls,目录为/bin/ls

当输入ls时,shell进程通过fork()创建一个新的子进程,fork()进程复制代码,以及新建堆栈等之前已经说明,子进程有机会执行的时候,在ret_from_fork()开始,返回到子进程的用户堆栈中,执行其余的子进程的代码.

在这些子进程需要执行的代码中,有execve(/bin/ls,ls,NULL),ls是列出当前路径的目录的一个可执行文件,同理如./a.out等

为加载此可执行文件到内存中执行,关键的地方在于,execve返回之后,执行的代码变成了需要加载的可执行文件的代码,下面详细说明它是如何做到的.

首先 execve()函数是系统调用,陷入内核,调用do_execve_common()函数,此函数的作用是加载需要执行的可执行文件的ELF头,因为后面需要将可执行文件的信息压入代码段以及将PC指向可执行文件的起点

1430static int do_execve_common(struct filename *filename,
1431				struct user_arg_ptr argv,
1432				struct user_arg_ptr envp)
1433{
1434	struct linux_binprm *bprm;
1435	struct file *file;
1436	struct files_struct *displaced;
1437	int retval;
1438
1439	if (IS_ERR(filename))
1440		return PTR_ERR(filename);
1441
1442	/*
1443	 * We move the actual failure in case of RLIMIT_NPROC excess from
1444	 * set*uid() to execve() because too many poorly written programs
1445	 * don‘t check setuid() return code.  Here we additionally recheck
1446	 * whether NPROC limit is still exceeded.
1447	 */
1448	if ((current->flags & PF_NPROC_EXCEEDED) &&
1449	    atomic_read(&current_user()->processes) > rlimit(RLIMIT_NPROC)) {
1450		retval = -EAGAIN;
1451		goto out_ret;
1452	}
1453
1454	/* We‘re below the limit (still or again), so we don‘t want to make
1455	 * further execve() calls fail. */
1456	current->flags &= ~PF_NPROC_EXCEEDED;
1457
1458	retval = unshare_files(&displaced);
1459	if (retval)
1460		goto out_ret;
1461
1462	retval = -ENOMEM;
1463	bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);
1464	if (!bprm)
1465		goto out_files;
1466
1467	retval = prepare_bprm_creds(bprm);
1468	if (retval)
1469		goto out_free;
1470
1471	check_unsafe_exec(bprm);
1472	current->in_execve = 1;
1473
1474	file = do_open_exec(filename);
1475	retval = PTR_ERR(file);
1476	if (IS_ERR(file))
1477		goto out_unmark;
1478
1479	sched_exec();
1480
1481	bprm->file = file;
1482	bprm->filename = bprm->interp = filename->name;
1483
1484	retval = bprm_mm_init(bprm);
1485	if (retval)
1486		goto out_unmark;
1487
1488	bprm->argc = count(argv, MAX_ARG_STRINGS);
1489	if ((retval = bprm->argc) < 0)
1490		goto out;
1491
1492	bprm->envc = count(envp, MAX_ARG_STRINGS);
1493	if ((retval = bprm->envc) < 0)
1494		goto out;
1495
1496	retval = prepare_binprm(bprm);
1497	if (retval < 0)
1498		goto out;
1499
1500	retval = copy_strings_kernel(1, &bprm->filename, bprm);
1501	if (retval < 0)
1502		goto out;
1503
1504	bprm->exec = bprm->p;
1505	retval = copy_strings(bprm->envc, envp, bprm);
1506	if (retval < 0)
1507		goto out;
1508
1509	retval = copy_strings(bprm->argc, argv, bprm);
1510	if (retval < 0)
1511		goto out;
1512
1513	retval = exec_binprm(bprm)
时间: 2024-10-05 04:34:00

Linux加载一个可执行程序并启动的过程的相关文章

如何在tomcat启动时加载一个类

Tomcat启动时classloader加载顺序 Tomcat的class加载的优先顺序一览   1.最先是$JAVA_HOME/jre/lib/ext/下的jar文件.   2.环境变量CLASSPATH中的jar和class文件.   3.$CATALINA_HOME/common/classes下的class文件.   4.$CATALINA_HOME/commons/endorsed下的jar文件.   5.$CATALINA_HOME/commons/i18n下的jar文件.   6.

JavaWeb 服务启动时,在后台老板启动加载一个线程

avaWeb 服务启动时,在后台启动加载一个线程JavaWeb 服务启动时,在后台启动加载一个线程. 目前,我所掌握的一共有两种方法,第一种是监听(Listener),第二种是配置随项目启动而启动的Servlet. 下面对这两种方法做一简单的介绍,(Mark一下,防止以后急用又忘记了): 监听(Listener) 首先,我们创建一个监听的类,继承ServletContextListener,如下: 源码复制打印    package com.wxp.thread;    import javax

如何在tomcat启动时自动加载一个类

有时候在开发web应用的时候,需要tomcat启动后自动加载一个用户的类,执行一些初始化方法,如从数据库中加载业务字典到内存中,因此需要在tomcat启动时就自动加载一个类,或运行一个类的方法. 可以采用在WEB-INF/web.xml中添加一个监听程序(ServletContextListener配置项),步骤如下:1) 增加一个监听程序 MyServletContextListener.java, 实现javax.servlet.ServletContextListener接口 packag

tomcat启动时自动加载一个类&#160;MyServletContextListener

目的: 我们知道在tomcat启动后,需要页面请求进行驱动来执行操作接而响应.我们希望在tomcat启动的时候能够自动运行一个后台线程,以处理我们需要的一些操作.因此需要tomcat启动时就自动加载一个类,或运行一个类的方法. 可以采用ServletContextListener. 方法: (1)编写一个监听类,实现javax.servlet.ServletContextListener接口. 1 import javax.servlet.ServletContextEvent; 2 impor

JavaWeb服务启动时,在后台启动加载一个线程进行Socket监听端口

最近,做一个项目,需要做一个web服务器,该服务器要与Android端和GPRS模块互相通信.考虑Android端与服务器端用Http通信,GPRS模块与服务器用Tcp通信.因此需要在Web服务器启动的时候启动加载一个线程负责Tcp端口的监听. search了一些方法,从中挑选了两个在此记录一下: 方法一:监听(Listener) 我们创建一个监听类,继承自ServletContextListener,代码如下: 1 package will; 2 3 4 import java.io.IOEx

7. 反射技术:其实就是动态加载一个指定的类

反射技术:其实就是动态加载一个指定的类,并获取该类中的所有的内容.而且将字节码文件封装成对象,并将字节码文件中的内容都封装成对象,这样便于操作这些成员.简单说:反射技术可以对一个类进行解剖. 反射的好处:大大的增强了程序的扩展性. 反射的基本步骤: 1.获得Class对象,就是获取到指定的名称的字节码文件对象. 2.实例化对象,获得类的属性.方法或构造函数. 3.访问属性.调用方法.调用构造函数创建对象. 获取这个Class对象,有三种方式: 1:通过每个对象都具备的方法getClass来获取.

无法加载一个或多个请求的类型。有关更多信息,请检索 LoaderExceptions 属性。

新建一个MVC4的项目,引用DAL后,将DAL的连接字符串考入: <connectionStrings>     <add name="brnmallEntities" connectionString="metadata=res://*/BrnMall.csdl|res://*/BrnMall.ssdl|res://*/BrnMall.msl;provider=System.Data.SqlClient;provider connection string

Unity3d修炼之路:加载一个预制体,然后为该对象添加组件,然后查找对象,得到组件。

#pragma strict function Awake(){ //加载一个预制体 资源必须在 Resources文件夹下 Resources.LoadLoad(); //加载后 必须示例化 GameObject.Instantiate(); //为对象添加组件 AddComponent(); //Find游戏对象 Find(); //Get组件 GetComponent(); var pPrefab : GameObject = Resources.Load("Prefab/Scence&q

Maven传递依赖的时候,同名包不同版本的包均会下载,但是编译的时候,只会加载一个高版本的。

描述,在一个Maven项目中,同时依赖了spring-tomcat-weaver  和  struts-core 包,但是spring-tomcat-weaver 需要commons-digester-1.2 struts-core 需要commons-digester-1.8 Pom文件如下: <dependencies> <dependency> <!-- 需要commons-digester-1.2包 --> <groupId>org.springfr