linux下execve实现的过程

来源:互联网 发布:网络媒介推广 编辑:程序博客网 时间:2024/05/23 00:45

linux下execve实现的过程

    博客分类: 
  • linux
Linux数据结构thread 

实现的最终效果:改变进程执行的上下文。

1.使用execve就是一次系统调用,首先要做的将新的可执行文件的绝对路径从调用者(用户空间)拷贝到系统空间中。

2.在得到可执行文件路径后,就找到可执行文件打开,由于操作系统已经为可执行文件设置了一个数据结构,就初始化这个数据结构,保存一个可执行文件必要的信息。

3.可执行文件不是真正上能够自己运行的,需要有代理人来代理。在系统内核中有一个formats队列,循环遍历这个队列,看看现在被初始化的这个数据结构是哪个代理人可以代理的。如果没有就继续查看数据结构中的信息。按照系统配置了是否可以动态加载模块,加载一次模块,再循环遍历看是否有代理人前来认领。

4.找到正确的代理人后,代理人首先要做的就是放弃以前从父进程继承来的资源(虽然代理程序各不相同,但这是一个共性的操作)。主要是对信号处理表,用户空间和文件3大资源的处理。

  a.信号处理表:将父进程的信号处理表复制过来(可能已经复制了,也可能没有复制,通过检查信号处理表的共享参数就可以知道)。信号处理分3种情况:一对该信号不理睬,二对信号采取默认动作,三对信号采取指定动作。前面2种可以直接复制,最后一种,所谓的默认动作就是用户指定了程序处理,这段程序的代码必然是父进程自己拥有的,他的位置就存在用户空间中,下面我们会放弃继承到的父进程用户空间,对第3种情况的处理就是将其改成默认处理。所以,在新建的子进程中,对信号如果子进程没有采取指定处理,那么一律都会是默认处理,当然如果父进程对某个信号采取了不理睬,子进程也会不理睬,除非子进程后来又做了修改。

  b.用户空间,放弃原来的用户空间(子进程可能有自己的页面,或者就是通过指针共享了父进程的页面)这些一律放弃,将进程控制块task_struct中对用户空间的描述的数据结构mm_struct的下属结构vma全部置0.简而言之就是现在子进程的用户空间是个空架子,一个页面也没有,父进程空间被放弃。

   c.进程控制块task_struct中有file的指针记录了进程打开的文件信息,子进程对继承到的文件采取关闭应当关闭的信息。file的数据结构中有位图记录了应当关闭的文件,子进程放弃这些文件。一般来说,执行的效果是除了标准输入文件,标准输出文件,标准错误输出文件。其它的文件都不会被子进程继承。(标准输入一般就是键盘,标准输出就是显示器。因此如果子进程有打印语句的话,那么他的打印出来的字符会打印到父进程打印的地方,前面写文章有点错误,我已经改掉了)。

5.至此我们已经做了的实际动作就是信号处理表,用户空间和文件。但用户空间是个空架子,真正的程序代码没载入,数据段也没载入,堆栈没有开辟,执行参数和环境变量也没有被印射。但可以知道,每个可执行文件的载入是不同的,比如linux下shell文件和a.out文件2个有很明显的不同,你可以对他们采用同样的载入办法吗。下面就是各个代理人自己开始为自己的代理方申请空间,准备用户内存。最后调用 start_thread 开始启动进程。

 

这里是大致的流程,很多细节没有提到,比如在用户空间释放过程中,我们可以看到很多问题。例如原来的是vfork创建的是线程,你放了空间,为新进程申请了空间,虽然是个空架子,但他已经不是线程了,你要从父进程的线程组中拿掉他。如果是内核线程启动了新的进程,内核线程是没有自己的用户空间的。这是原来用户空间的共享计数会为0,该如何处理,这些问题在这里都没提到。如果有兴趣,大家可以自己看linux源代码。

0 0