几点注意:
1.写时复制技术允许父子进程读相同的物理页。只要两者中有一个试图写一个物理页,内核就把这个页的内容拷贝到一个新的物理页,并把这个新的物理页分配给正在写的进程。
2.轻量级进程允许父子进程共享每进程在内核的很多数据结构。
3.vfork()系统调用创建的进程能共享其父进程的内存地址空间。为了防止父进程重写子进程需要的数据,阻塞父进程的执行,一直到子进程退出或执行一个新的程序为止。
轻量级进程是由clone()函数创建的。
#define CLONE_VM 0x00000100 共享内存描述符和所有的页表
#define CLONE_FS 0x00000200 共享根目录和当前工作目录所在的表,以及用于屏蔽新文化初始许可证的位掩码值
#define CLONE_FILES 0x00000400 共享打开文件表
#define CLONE_SIGHAND 0x00000800 共享信号处理程序的表、阻塞信号表和挂起信号表,如果这个标志为true,就必须设置CLONE_VM标志。
#define CLONE_PTRACE 0x00002000 如果父进程被跟踪,则子进程也被跟踪。debugger程序希望以自己作为父进程来跟踪子进程,在这种情况下,内核把该标志强置为1
#define CLONE_VFORK 0x00004000 在发出vfork()系统调用时设置
#define CLONE_PARENT 0x00008000 设置子进程的父进程(进程描述符中的parent和real_parent字段)为调用进程的父进程
#define CLONE_THREAD 0x00010000 把子进程插入到父进程的同一线程组中,并迫使子进程共享父进程的信号描述符,因此也设置子进程的tgid字段和group_leader字段。如果这个标志位为true,就必须设置CLONE_SIGHAND标志
#define CLONE_NEWNS 0x00020000 当clone需要自己的命名空间时(即它自己的已挂载文件系统视图)设置这个标志。不能同时设置CLONE_NEWNS和CLONE_FS.
#define CLONE_SYSVSEM 0x00040000 共享system v ipc取消信号量的操作
#define CLONE_SETTLS 0x00080000 为轻量级进程创建新的线程局部存储段(TLS)该段由参数tls所指向的结构进行描述
#define CLONE_PARENT_SETTID 0x00100000 把子进程的pid写入由ptid参数所指向的父进程的用户态变量
#define CLONE_CHILD_CLEARTID 0x00200000 如果该标志位被设置,则内核建立一种触发机制,用在子进程要退出或要开始执行新程序时。在这些情况下,内核将清除由参数ctid所指向的用户态变量,并唤醒等待这个事件的任何进程
#define CLONE_DETACHED 0x00400000 遗留标志,内核会忽略它
#define CLONE_UNTRACED 0x00800000 内核设置这个标志以使CLONE_PTRACE标志失去作用
#define CLONE_CHILD_SETTID 0x01000000 把子进程的pid写入由ctid参数所指向的子进程的用户态变量中
#define CLONE_STOPPED 0x02000000 强迫子进程开始于TASK_STOPPED状态
do_fork函数负责处理clone(),fork(),vfork()系统调用,执行时使用下列参数:
unsigned long clone_flags各种各样的信息。低字节指定子进程结束时发送到父进程的信号代码,通常选择SIGCHLD信号。剩余的三个字节给一clone标志组用于编码。
unsigned long stack_start表示把用户态堆栈指针赋给子进程的esp寄存器。调用进程应该总是为子进程分配新的堆栈。
struct pt_regs *regs指向通用寄存器值的指针,通用寄存器的数值是在从用户态切换到内核态时被保存到内核态堆栈中的。
unsigned long stack_size未使用(总被设置为0)
int __user *parent_tidptr表示父进程的用户态变量地址,该父进程具有与新轻量级进程相同的pid.只有在CLONE_PARENT_SETTID标志被设置时才有意义。
int __user *child_tidptr表示新轻量级进程的用户态变量地址,该进程具有这一类进程的pid,只有在CLONE_CHILD_SETTID标志被设置时才有意义。
long do_fork(unsigned long clone_flags,unsigned long stack_start,struct pt_regs *regs,unsigned long stack_size,int __user *parent_tidptr,int __user *child_tidptr)
{
struct task_struct *p;
int trace = 0;
long pid = alloc_pidmap();//通过查找pidmap_array位图,为子进程分配新的pid。(由于循环使用pid编号,内核必须通过管理一个pidmap-array位图来表示当前已分配的pid号和闲置的pid号。因为一个页框包含32768个位,所以在32位体系结构中pidmap-array位图存放在一个单独的页中。)
if (pid < 0)
return -EAGAIN;
if (unlikely(current->ptrace)) {检查父进程的ptrace字段,如果不等于0,说明有另外一个进程正在跟踪父进程。
trace = fork_traceflag (clone_flags);此时检查debugger程序是否自己想跟踪子进程(独立于由父进程指定的CLONE_PTRACE标志的数值),如果子进程不是内核线程(CLONE_UNTARCED标志被清0),那么函数设置CLONE_PTRACE标志。
if (trace)
clone_flags |= CLONE_PTRACE;
}
p = copy_process(clone_flags, stack_start, regs, stack_size, parent_tidptr, child_tidptr, pid);//复制进程描述符。如果所有必须的资源都是可用的,该函数返回刚创建的task_struct描述符地址。
if (!IS_ERR(p)) {
struct completion vfork;
if (clone_flags & CLONE_VFORK) {
p->vfork_done = &vfork;
init_completion(&vfork);
}
if ((p->ptrace & PT_PTRACED) || (clone_flags & CLONE_STOPPED)) {
/*We'll start up with an immediate SIGSTOP.*/
sigaddset(&p->pending.signal, SIGSTOP);
set_tsk_thread_flag(p, TIF_SIGPENDING);
}
if (!(clone_flags & CLONE_STOPPED))
wake_up_new_task(p, clone_flags);
else
p->state = TASK_STOPPED;如果CLONE_STOPPEN标志被设置,则把子进程设置为TASK_STOPPED状态。
if (unlikely (trace)) {如果父进程被跟踪,则把子进程的pid存入current的ptrace_message字段并调用ptrace_notify函数
current->ptrace_message = pid;
ptrace_notify ((trace << 8) | SIGTRAP);使得当前进程停止运行,并向当前进程的父进程发送SIGCHLD信号。子进程的祖父进程是跟踪父进程的debugger进程。SIGCHLD信号通知debugger进程:current已经创建了一个子进程,可以通过查找current->ptrace_message字段获得子进程的pid.
}
if (clone_flags & CLONE_VFORK) {如果设置了CLONE_VFORK标志,则把父进程插入等待队列,并挂起父进程直到子进程释放自己的内存地址空间。
wait_for_completion(&vfork);
if (unlikely (current->ptrace & PT_TRACE_VFORK_DONE))
ptrace_notify ((PTRACE_EVENT_VFORK_DONE << 8) | SIGTRAP);
}
} else {
free_pidmap(pid);
pid = PTR_ERR(p);
}
return pid;
}
linux 2.6源代码情景分析笔记之进程10
最新推荐文章于 2024-11-13 22:29:30 发布