lab6:分析Linux内核创建一个新进程的过程
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了lab6:分析Linux内核创建一个新进程的过程相关的知识,希望对你有一定的参考价值。
李俊锋 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
一.实验原理
1.进程的定义
进程是操作系统的概念,每当我们执行一个程序时,对于操作系统来讲就创建了一个进程,在这个过程中,伴随着资源的分配和释放。可以认为进程是一个程序的一次执行过程。
2.进程与程序的区别
程序时静态的,它是一些保存 在磁盘上得指令的有序集合,没有任何执行的概念。
二.实验步骤
1.在test.c中添加如下代码:
1 pid_t fpid; 2 printf("going to create a process.....\\n"); 3 asm volatile( 4 "mov $0x2,%%eax\\n\\t" 5 "int $0x80\\n\\t" 6 "mov %%eax,%0\\n\\t" 7 :"=m"(fpid) 8 ); 9 printf("have created a process\\n"); 10 if(fpid < 0) 11 { 12 printf("error in fork!\\n"); 13 } 14 else if(fpid == 0) 15 { 16 printf("i am child,process id :%d.\\n",getpid()); 17 } 18 else 19 { 20 printf("i am parent,process id :%d.\\n",getpid()); 21 } 22 return 0;
2.重新编译运行,结果如下图所示:
3.运行fork,结果如下图所示:
4.使用gdb调试运行系统,如下图所示:
5.在如下位置下断点,如下图所示:
6.调试运行程序,观察程序运行过程:
三.实验总结
实验作业题目的理解如下所示:
-
阅读理解task_struct数据结构http://codelab.shiyanlou.com/xref/linux-3.18.6/include/linux/sched.h#1235;
- state:运行状态
- stack:内核堆栈
- tasks:进程链表
- mm:内存管理
- task_state:任务的状态
- pid:进程PID
- real_parent children:进程的父子关系
- files:文件描述符列表
- signal:信号处理相关
- splice_pipe:管道相关
-
分析fork函数对应的内核处理过程sys_clone,理解创建一个新进程如何创建和修改task_struct数据结构;
- fork、vfork和clone三个系统调用都可以创建一个新进程,都是通过调用do_fork来实现进程的创建
- 创建新进程需要先复制一个PCB:task_struct
- 再给新进程分配一个新的内核堆栈
ti = alloc_thread_info_node(tsk, node); tsk->stack = ti; setup_thread_stack(tsk, orig); //这里只是复制thread_info,而非复制内核堆栈
- 再修改复制过来的进程数据,比如pid、进程链表等等,见copy_process内部
- 从用户态的代码看fork();函数返回了两次,即在父子进程中各返回一次
1 *childregs = *current_pt_regs(); //复制内核堆栈 2 childregs->ax = 0; //为什么子进程的fork返回0,这里就是原因! 3 4 p->thread.sp = (unsigned long) childregs; //调度到子进程时的内核栈顶 5 p->thread.ip = (unsigned long) ret_from_fork; //调度到子进程时的第一条指令地址
-
do_fork完成了创建中的大部分工作,该函数调用copy_process()函数,然后让进程开始运行。copy_process()函数工作如下:
- 1、调用dup_task_struct()为新进程创建一个内核栈、thread_info结构和task_struct,这些值与当前进程的值相同
- 2、检查
- 3、子进程着手使自己与父进程区别开来。进程描述符内的许多成员被清0或设为初始值。
- 4、子进程状态被设为TASK_UNINTERRUPTIBLE,以保证它不会投入运行
- 5、copy_process()调用copy_flags()以更新task_struct的flags成员。表明进程是否拥有超级用户权限的PF_SUPERPRIV标志被清0。表明进程还没有调用exec()函数的PF_FORKNOEXEC标志被设置
- 6、调用alloc_pid()为新进程分配一个有效的PID
- 7、根据传递给clone()的参数标志,copy_process()拷贝或共享打开的文件、文件系统信息、信号处理函数、进程地址空间和命名空间等
- 8、最后,copy_process()做扫尾工作并返回一个指向子进程的指针
-
使用gdb跟踪分析一个fork系统调用内核处理函数sys_clone ,验证您对Linux系统创建一个新进程的理解
- 下断点的函数:sys_clone do_fork dup_task_struct copy_struct copy_process copy_thread ret_from_fork
- dup_task_struct()为新进程创建一个内核栈
copy_process()主要完成进程数据结构,各种资源的初始化
-
特别关注新进程是从哪里开始执行的?为什么从哪里能顺利执行下去?即执行起点与内核堆栈如何保证一致。
ret_from_fork;
决定了新进程的第一条指令地址- copy_thread()函数中的语句
p->thread.ip = (unsigned long) ret_from_fork;
决定了新进程的第一条指令地址 - 在ret_from_fork之前,也就是在copy_thread()函数中
*childregs = *current_pt_regs();
该句将父进程的regs参数赋值到子进程的内核堆栈 - *childregs的类型为pt_regs,里面存放了SAVE ALL中压入栈的参数 故在之后的RESTORE ALL中能顺利执行下去
本次实验主要是对fork系统调用的调试,难度并不是很大,但是需要啊记住的东西很多,希望自己能够全部理解,加油!(*^__^*)
以上是关于lab6:分析Linux内核创建一个新进程的过程的主要内容,如果未能解决你的问题,请参考以下文章
linux内核分析第六周-分析Linux内核创建一个新进程的过程
《Linux内核--分析Linux内核创建一个新进程的过程 》 20135311傅冬菁