内核资料收集
1. unix依赖进程创建满足用户的需求。如,用户输入一条命令,shell进程就创建一个新进程,新进程执行shell的另一个拷贝。
2. 传统的unix操作系统以统一的方式对待所有的进程:子进程复制父进程所拥有的资源。实际上,这种方法使进程创建非常低效:
因为很多情况下,子进程立即调用execve(),并清除父进程拷贝过来的地址空间。
3. 现代unix内核通过引入三种不同的机制解决了这个问题:
a. 写时复制技术允许父子进程读相同的物理页。只要父子进程中的一个试图写一个物理页,内核就把这个物理页拷贝到一个
新的物理页,并把这个新的物理页分配给正在写的进程。
b. 轻量级进程允许父子进程共享每进程在内核的很多数据结构,如页表,打开文件及信号处理。
c. vfork()系统调用创建的进程能共享其父进程的内存地址空间。为了防止父进程重写子进程需要的数据,阻塞父进程的执行,
直到子进程退出或执行一个新的程序为止。
4. clone()/fork()/vfork()
在linux中,轻量级进程是由名为clone()的函数创建的。clone()是在C语言库中定义的一个封装(wrapper)函数,它负责建立新
轻量级进程的堆栈,并且调用对编程者隐藏的clone()系统调用。
传统fork()系统调用在linux中是用clone()实现的。其中clone()的参数...
vfork()系统调用在linux中也是用clone()实现的,其中clone()的参数...
5. do_fork()函数
do_fork()函数负责处理clone()/fork()/vfork()系统调用。do_fork()利用辅助函数copy_process()来处理进程描述符以及子进程执
行所需要的所有其它内核数据结构。do_fork()执行主要步骤如下:
a. 通过pidmap_array位图,为子进程分配新PID
b. 检查父进程的ptrace字段,判断是否要设置do_fork()函数设置CLONE_PTRACE标志。
c. 调用copy_process()复制进程描述符
d. 判断是否设置CLONE_STOPPED,以确定是否要把子进程的状态设置成TASK_STOPPED,并为子进程增加挂起的
SIGSTOP信号
e. 如果没有设置CLONE_STOPPED标志,则调用wake_up_new_task()函数以执行下述操作:
1>高速父子进程的调试参数
2>如果父子进程运行在同一个CPU上,且不能共享同一组页表,就把子进程插入到父进程运行队列,让子进程恰好在父
进程前面。(这样可以产生较好的性能,仔细消化一下,为什么?)
3>如果父子进程运行在不同的CPU上,或父子进程共享同一组页表,就把子进程插入到父进程运行队列的队尾。
f. 如果CLONE_STOPPED标志被设置,则把子进程设置为TASK_STOPPED状态。
g. 如果父进程被跟踪,则把子进程的PID存入current的ptrace_message字段并调用ptrace_notify().
ptrace_notify()使当前进程停止运行,并向当前进程的父进程发送SIGCHLD信号。SIGCHLD信号通知debugger进程:
current已经创建了一个子进程,可以通过查找current->ptrace_message字段获得子进程的PID.
h. 如果设置了CLONE_VFORK标志,则把父进程插入等待队列,并挂起父进程直到子进程释放自己的内存空间
i. 结束并返回了进程的PID.
6. copy_process()函数
copy_process()创建进程描述符以及子进程所执行所需要的所有其他数据结构。其参数与do_fork()的数据相同,外加了进程
的PID。 copy_process()的最重要步骤:
(......)
7. do_fork()完成后
do_fork()结束之后,完整的子进程处于可运行状态,但还没运行。进程切换后,CPU返回到用户态。fork()/vfork()/clone()
系统调用结束后,新进程开始执行。系统调用的返回值放在eax寄存器中。返回给子进程的值是0,返回给父进程的值是了进程
的PID(为什么是这样的? copy_process()对子进程的操作可以解释之)
阅读(537) | 评论(0) | 转发(0) |