要求
· 参考进程初探 编程实现fork(创建一个进程实体) -> exec(将ELF可执行文件内容加载到进程实体) -> running program
· 参照C代码中嵌入汇编代码示例及用汇编代码使用系统调用time示例分析fork和exec系统调用在内核中的执行过程
· 注意task_struct进程控制块,ELF文件格式与进程地址空间的联系,注意Exec系统调用返回到用户态时EIP指向的位置。
· 动态链接库在ELF文件格式中与进程地址空间中的表现形式
· 通过300-500字总结以上实验和分析所得,实验情况和分析的关键代码可以作为总结后面的附录以提供详细信息。
一.编程实现fork(创建一个进程实体) -> exec
程序一:
结果:
程序2:
结果:
程序3:
结果:
由图可知,third.c程序并没有执行perror(“execl”)语句,总结: fork( )函数通过拷贝当前进程创建一个子进程,子进程与父进程的区别仅仅在在于PID、PPID和某些资源和统计量。故fork()函数调用一次返回一次,而exec()调用一次不返回。
二.参照C代码中嵌入汇编代码示例及用汇编代码使用系统调用time示例分析fork和exec系统调用在内核中的执行过程
1.fork()在内核中的执行过程
fork系统调用所对应的系统调用服务例程调用了do_fork(),do_fork()分析如下:
(1)在一开始,该函数定义了一个task_struct类型的指针p,用来接收即将为新进程(子进程)所分配的进程描述符。紧接着使用alloc_pidmap函数为这个新进程分配一个pid。
(2)接下来检查当前进程(父进程)的ptrace字段。ptrace是用来标示一个进程是否被另外一个进程所跟踪。
(3)通过copy_process()创建子进程的描述符,并创建子进程执行时所需的其他数据结构,最终则会返回这个创建好的进程描述符。用task_struct结构体保存进程的描述符
(4)如果copy_process函数执行成功,首先定义了一个完成量vfork,如果clone_flags包含CLONE_VFORK标志,那么将进程描述符中的vfork_done字段指向这个完成量,之后再对vfork完成量进行初始化。
(5)接下来是对父进程,子进程的一些标志位的判断,以及对于copy_process()函数执行失败的处理。
2.execl()在内核中的执行过程
Linux提供了六个exec()函数,这些函数的第一个参数都是要被执行的程序的路径,第二个参数则向程序传递了命令行参数,第三个参数则向程序传递环境变量。
函数定义:int execl(const char * path,const char * arg,....,(char*)0);
函数说明:execl()用来执行参数path字符串所代表的文件路径,接下来的参数代表执行该文件时传递过去的argv(0)、argv[1]……,最后一个参数必须用空指针(NULL)作结束。
返回值:如果执行成功则函数不会返回,执行失败则直接返回-1。
当进程调用一种exec函数时,该进程完全由新程序代换,而新程序则从其main函数开始执行。因为调用exec并不创建新进程,所以前后的进程ID并未改变。exec只是用另一个新程序替换了当前进程的正文、数据、堆和栈段。
三.注意task_struct进程控制块,ELF文件格式与进程地址空间的联系,注意Exec系统调用返回到用户态时EIP指向的位置。
task_struct进程控制块中的mm字段所指向的mm_struct结构描述了进程地址空间的信息,包括代码段、数据段、堆段、栈段所在地址空间里的起始和结束地址等信息。
ELF文件格式中的 ELF头部、段头部表对应进程地址空间中的代码段,在加载可执行文件时,会把它们映射到进程地址空间中的代码段区域。ELF文件格式中的 .data对应进程地址空间中的数据段,在加载可执行文件时,会把它们映射到进程地址空间的数据段区域。
四.动态链接库在ELF文件格式中与进程地址空间中的表现形式
动态链接库在ELF文件中对应着.dynamic段所包含的信息:包括动态链接器所需要的相关信息,比如依赖于哪些共享对象(例如libc.so)、动态链接符号表位置。
动态链接库最后被映射到进程地址空间的共享库区域段。