试分析Linux的clone,20169217 《Linux内核原理与分析》 第八周作业(示例代码)

此次作业仍然分为两个部分,第一部分为实验。

实验要求:

1. 阅读理解task_struct数据结构。

2. 分析fork函数对应的内核处理过程sys_clone,理解创建一个新进程如何创建和修改task_struct数据结构。

3.使用gdb跟踪分析一个fork系统调用内核处理函数sys_clone ,验证对Linux系统创建一个新进程的理解。

实验内容:

1.理解task_struct数据结构。

操作系统内核里面有操作系统所谓的三大功能,就是进程管理,内存管理和文件系统。但这里面最核心的就是进程管理,也就是在linux里面我们有一个数据结构task_struct。为了管理进程,内核要描述进程的这个东西,我们也称它为进程描述符。它提供了进程所需要了解的信息。

bd7e26d6b2e94aa2831cafd6da1bb4bb.jpg

进程状态变化图为:

1fc80d42c12448b1b1f78ecf0aa635e3.jpg

我们来分析一下进程的变化,操作系统中的进程状态有就绪态、运行态、阻塞态三大状态,实际的,内核管理的进程状态是不一样的。我们来看一下为什么不一样,你看我们在linux进程状态的转换图,我们使用fork来创建一个新进程的时候,实际它的状态是就绪,但是没有在运行。当调度器选择到新创建的进程的时候它就切换到运行态,也是TASK_RUNNING。两个状态都是相同的,也就是说,当进程是TASK_RUNNING状态的时候,它是可运行的,有没有在运行呢,这个取决于它有没有获得CPU的控制权。也就是说这个进程有没有在CPU上实际的执行。如果在CPU上实际的执行就是运行态,如果被调度出去了,在那等待着就是就绪态。所以这个和在操作系统原理中有点不一样。一个正在运行的进程,我们调用do_exit,也就是终止,就会进入TASK_ZOMBIE,也就是进程的终止状态,那么系统会把进程给处理掉。这是就绪态和运行态。我们看一个正在运行的进程,它在等待特定的事件或资源的时候就会进入阻塞态。

2.分析fork函数对应的内核处理过程sys_clone,理解创建一个新进程如何创建和修改task_struct数据结构。

fork、vfork和clone三个系统调用都可以创建一个新进程,而且都是通过调用do_fork来实现进程的创建,函数在/linux-3.18.6/kernel/fork.c中,代码如下:

1623long do_fork(unsigned longclone_flags,1624 unsigned longstack_start,1625 unsigned longstack_size,1626 int __user *parent_tidptr,1627 int __user *child_tidptr)1628{1629 struct task_struct *p;1630 int trace = 0;1631 longnr;1632

1633 /*1634 * Determine whether and which event to report to ptracer. When

1635 * called from kernel_thread or CLONE_UNTRACED is explicitly

1636 * requested, no event is reported; otherwise, report if the event

1637 * 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 else

1645 trace =PTRACE_EVENT_FORK;1646

1647 if (likely(!ptrace_event_enabled(current, trace)))1648 trace = 0;1649}1650

1651 p =copy_process(clone_flags, stack_start, stack_size,1652child_tidptr, NULL, trace);1653 /*1654 * Do this prior waking up the new thread - the thread pointer

1655 * might get invalid after that point, if the thread exits quickly.

1656*/

1657 if (!IS_ERR(p)) {1658 structcompletion vfork;1659 struct pid *pid;1660

1661trace_sched_process_fork(current, p);1662

1663 pid =get_task_pid(p, PIDTYPE_PID);1664 nr =pid_vnr(pid);1665

1666 if (clone_flags &CLONE_PARENT_SETTID)1667put_user(nr, parent_tidptr);1668

1669 if (clone_flags &CLONE_VFORK) {1670 p->vfork_done = &vfork;1671 init_completion(&vfork);1672get_task_struct(p);1673}1674

1675wake_up_new_task(p);1676

1677 /*forking complete and child started to run, tell ptracer*/

1678 if(unlikely(trace))1679ptrace_event_pid(trace, pid);1680

1681 if (clone_flags &CLONE_VFORK) {1682 if (!wait_for_vfork_done(p, &vfork))1683ptrace_event_pid(PTRACE_EVENT_VFORK_DONE, pid);1684}1685

1686put_pid(pid);1687 } else{1688 nr =PTR_ERR(p);1689}1690 returnnr;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跟踪分析一个fork系统调用内核处理函数sys_clone ,验证您对Linux系统创建一个新进程的理解

rm menu -rf

git clone https://github.com/mengning/menu.git

cd menu

mv test_fork.c test.c

make rootfs

qemu-kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S

gdb

file linux-3.18.6/vmlinux 加载调试用的符号表

target remote:1234

设置断点:

b sys_clone

b do_fork

b dup_task_struct

b copy_process

b copy_thread

b ret_from_fork

52c20bef90be4ea4bffa9d7917e62a36.jpg

ac00c1920d2f40a9b0558a9527fea13d.jpg

8b14d22065dc424088f9a053a6a2a8f2.jpg

总结:

Linux系统创建一个新进程:fork、vfork和clone三个系统调用都可以创建一个新进程,而且都是通过调用do_fork来实现进程的创建。do_fork函数主要是调用copy_process函数来创建进程。copy_process函数调用 dup_task_struct 复制当前的 task_struct;调用sched_fork 初始化进程数据结构,并把进程状态设置为 TASK_RUNNING;复制父进程的所有信息,包括copy_files,copy_fs,copy_mm,copy_io等;调用 copy_thread 初始化子进程内核栈;为新进程分配并设置新的 pid,修改进程链表等。dup_task_struct 函数主要是复制一个PCB——task_struct和给新进程分配一个新的内核堆栈。copy_thread函数主要是调度到子进程时的内核栈顶;将子进程的ip设置为ret_from_fork的首地址,子进程从ret_from_fork开始执行;复制内核堆栈;子进程的fork返回0。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值