Linux内核分析:实验六
来源:互联网 发布:女友长得像刘亦菲 知乎 编辑:程序博客网 时间:2024/05/22 02:12
分析linux内核创建一个新进程的过程
杨鹏伟原创作品转载请注明出处《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
1. 进程的创建过程
在linux系统中,使i用 fork,vfork 和 clone 都可以创建新的进程,在表面看起来它们是不一样的,但实际上它们都调用do_fork函数来实现这个功能;但仔细分析来它们还是有一点点小区别的:(from:http://blog.csdn.net/abclixu123/article/details/8288292)
vfork和fork创建子进程的区别: fork: 子进程拷贝父进程的数据段 vfork: 子进程与父进程共享数据段<span style="font-family:sans-serif;"> </span> fork: 父、子进程的执行次序不确定 vfork: 子进程先运行,父进程后运行fork与exec函数族的区别就在于:fork创建一个新的进程,产生一个新的PID,exec启动一个新程序,替换原有的进程,因此进程的PID不会改变。
下面我们就详细看看这三个函数的调用方法:
SYSCALL_DEFINE0(fork) { return do_fork(SIGCHLD, 0, 0, NULL, NULL);}SYSCALL_DEFINE0(vfork){ return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, 0, 0, NULL, NULL);} SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp, int __user *, parent_tidptr, int __user *, child_tidptr, int, tls_val){ return do_fork(clone_flags, newsp, 0, parent_tidptr, child_tidptr);}
从用户态的代码看fork();函数返回了两次,即在父子进程中各返回一次,父进程从系统调用中返回比较容易理解,子进程从系统调用中返回,那它在系统调用处理过程中的哪里开始执行的呢?这就涉及子进程的内核堆栈数据状态和task_struct中thread记录的sp和ip的一致性问题,这是 在哪里设定的?copy_thread in copy_process。
*childregs = *current_pt_regs(); //复制内核堆栈childregs->ax = 0; //为什么子进程的fork返回0,这里就是原因! p->thread.sp = (unsigned long) childregs; //调度到子进程时的内核栈顶p->thread.ip = (unsigned long) ret_from_fork; //调度到子进程时的第一条指令地址
二、分析fork函数对应的内核处理过程sys_clone,理解创建一个新进程如何创建和修改task_struct数据结构
fork、vfork和clone三个系统调用都可以创建一个新进程,而且都是通过调用do_fork来实现进程的创建,函数在/linux-3.18.6/kernel/fork.c中,代码如下:
1618 * Ok, this is the main fork-routine.1619 *1620 * It copies the process, and if successful kick-starts1621 * it and waits for it to finish using the VM if required.1622 */1623long do_fork(unsigned long clone_flags,1624 unsigned long stack_start,1625 unsigned long stack_size,1626 int __user *parent_tidptr,1627 int __user *child_tidptr)1628{1629 struct task_struct *p;1630 int trace = 0;1631 long nr;16321633 /*1634 * Determine whether and which event to report to ptracer. When1635 * called from kernel_thread or CLONE_UNTRACED is explicitly1636 * requested, no event is reported; otherwise, report if the event1637 * for the type of forking is enabled.1638 */1639 if (!(clone_flags & CLONE_UNTRACED)) {1640 if (clone_flags & CLONE_VFORK)1641 trace = PTRACE_EVENT_VFORK;1642 else if ((clone_flags & CSIGNAL) != SIGCHLD)1643 trace = PTRACE_EVENT_CLONE;1644 else1645 trace = PTRACE_EVENT_FORK;16461647 if (likely(!ptrace_event_enabled(current, trace)))1648 trace = 0;1649 }16501651 p = copy_process(clone_flags, stack_start, stack_size,1652 child_tidptr, NULL, trace);1653 /*1654 * Do this prior waking up the new thread - the thread pointer1655 * might get invalid after that point, if the thread exits quickly.1656 */1657 if (!IS_ERR(p)) {1658 struct completion vfork;1659 struct pid *pid;16601661 trace_sched_process_fork(current, p);16621663 pid = get_task_pid(p, PIDTYPE_PID);1664 nr = pid_vnr(pid);16651666 if (clone_flags & CLONE_PARENT_SETTID)1667 put_user(nr, parent_tidptr);16681669 if (clone_flags & CLONE_VFORK) {1670 p->vfork_done = &vfork;1671 init_completion(&vfork);1672 get_task_struct(p);1673 }16741675 wake_up_new_task(p);16761677 /* forking complete and child started to run, tell ptracer */1678 if (unlikely(trace))1679 ptrace_event_pid(trace, pid);16801681 if (clone_flags & CLONE_VFORK) {1682 if (!wait_for_vfork_done(p, &vfork))1683 ptrace_event_pid(PTRACE_EVENT_VFORK_DONE, pid);1684 }16851686 put_pid(pid);1687 } else {1688 nr = PTR_ERR(p);1689 }1690 return nr;1691}上段代码先定义一个task_sturct结构指针,再用copy_process函数复制一个进程,故而真正的复制过程在copy_process函数中,该函数主要执行内容为:
struct task_struct *p;retval = security_task_create(clone_flags);p = dup_task_struct(current);retval = sched_fork(clone_flags, p);retval = copy_files(clone_flags, p);retval = copy_fs(clone_flags, p);retval = copy_sighand(clone_flags, p);retval = copy_signal(clone_flags, p);retval = copy_mm(clone_flags, p);retval = copy_io(clone_flags, p);retval = copy_thread(clone_flags, stack_start, stack_size, p);
/linux-3.18.6/arch/x86/kernel/process_32.c的copy_thread函数中
p->thread.sp = (unsigned long) childregs; p->thread.ip = (unsigned long) ret_from_fork; childregs->ax = 0; fork子进程返回为0
决定了子进程从系统调用中返回后的执行.ret_from_fork决定了新进程的第一条指令地址。p->thread.ip = (unsigned long) ret_from_fork;将子进程的ip设置为ret_from_fork的首地址,子进程从ret_from_fork开始。三. 使用gdb调试:
在调试前,我们可以看到如下信息
0 0
- Linux内核分析实验六
- Linux内核分析:实验六
- Linux内核分析:实验六
- linux 内核实验六实现
- Linux内核分析 实验六:分析Linux内核创建一个新进程的过程
- Linux内核|实验六 内核模块编程
- Linux内核分析:实验六--Linux进程的创建过程分析
- Linux内核分析(六)
- Linux内核分析(六)
- (六)Linux内核分析
- Linux内核分析:实验一
- Linux内核分析:实验一
- Linux内核分析实验一
- Linux内核分析:实验一
- Linux内核分析:实验二
- Linux内核分析实验二
- Linux内核分析:实验二
- Linux内核分析:实验二
- LaTeX之代码语法高亮
- android ListView下拉刷新拉取网络数据
- Android 表单验证框架:AValidations
- HD 1159 Common Subsequence (最长公共子序列)
- 【笔记】 《js权威指南》- 第6章 对象 - 6.5 - 6.6 属性操作2
- Linux内核分析:实验六
- boost 正则表达式测试
- 用中国剩余定理解 POJ1006
- 【Java学习-J.160331.0.4】笔记3-Linux基础
- POJ 1700 过河坐船最短时间问题
- Vrituoso安装经验总结
- php获取复选框的值CheckBox的多个值
- Android引导动画库:TourGuide
- 浏览器如何获取网页