linux 2.6源代码情景分析笔记之进程11
2009-12-29 13:13
561 查看
下函数,创建进程描述符以及子进程执行所需要的所有其他数据结构。参数与do_fork()的参数相同,外加子进程的pid。
static task_t *copy_process(unsigned long clone_flags,unsigned long stack_start,struct pt_regs *regs,unsigned long stack_size,int __user *parent_tidptr,int __user *child_tidptr,int pid)
{
int retval;
struct task_struct *p = NULL;
if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS))
return ERR_PTR(-EINVAL);
if ((clone_flags & CLONE_THREAD) && !(clone_flags & CLONE_SIGHAND))
return ERR_PTR(-EINVAL);
if ((clone_flags & CLONE_SIGHAND) && !(clone_flags & CLONE_VM))
return ERR_PTR(-EINVAL);
上面的一组是互相矛盾的,所以必须检查,如第一个。clone不能又需要自己的空间,又屏蔽文件初始许可权的位掩码。
而第二行当CLONE_THREAD被设置,同时CLONE_SIGHAND又被清零的时候是不可以的,因为同一线程组中的轻量级进程必须共享信号
CLONE_SIGHAND标志被设置,CLONE_VM被清零也不可以,因为共享信号处理程序的轻量级进程也必须共享内存描述符
retval = security_task_create(clone_flags);//执行附加的安全检查
if (retval)
goto fork_out;
retval = -ENOMEM;
p = dup_task_struct(current);//为子进程获取进程描述符
if (!p)
goto fork_out;
retval = -EAGAIN;
检查current中的signal->rlim[RLIMIT_NPROC].rlim_cur是否小于或者等于用户所拥有的进程数。如果小于,返回错误码。此函数从每用户数据结构user_struct中获取用户所拥有的进程数。通过进程描述符user字段的指针可以找到这个数据结构
if (atomic_read(&p->user->processes) >= p->signal->rlim[RLIMIT_NPROC].rlim_cur) {
if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE) && p->user != &root_user)
goto bad_fork_free;
}
atomic_inc(&p->user->__count);递增user_struct结构的使用计数器(p->user->__count)
atomic_inc(&p->user->processes);递增用户所拥有的进程计数器(p->user->processes)
get_group_info(p->group_info);
if (nr_threads >= max_threads)检查系统中的进程数量,存放在nr_threads变量中,是否超过max_threads变量的数值。这个变量的缺省数值取决于系统内存容量的大小。总的原则是:所有thread_info描述符和内核栈所占用的空间不能超过物理内存大小的1/8.系统管理员可以通过写/proce/sys/kernel/threads-max文件来改变这个数值。
goto bad_fork_cleanup_count;
if (!try_module_get(p->thread_info->exec_domain->module))
goto bad_fork_cleanup_count;
if (p->binfmt && !try_module_get(p->binfmt->module))
goto bad_fork_cleanup_put_domain;
p->did_exec = 0;初始化为0,记录了进程发出的execve()系统调用的次数
copy_flags(clone_flags, p);设置与进程状态相关的几个关键字段
p->pid = pid;把新进程pid存入task->pid
retval = -EFAULT;
if (clone_flags & CLONE_PARENT_SETTID)如果此标志位被设置,就把子进程的pid复制到参数parent_tidptr指向的用户态变量中
if (put_user(p->pid, parent_tidptr))
goto bad_fork_cleanup;
p->proc_dentry = NULL;
INIT_LIST_HEAD(&p->children);
INIT_LIST_HEAD(&p->sibling);
p->vfork_done = NULL;
spin_lock_init(&p->alloc_lock);
spin_lock_init(&p->proc_lock);
clear_tsk_thread_flag(p, TIF_SIGPENDING);
init_sigpending(&p->pending);
p->it_real_value = 0;
p->it_real_incr = 0;
p->it_virt_value = cputime_zero;
p->it_virt_incr = cputime_zero;
p->it_prof_value = cputime_zero;
p->it_prof_incr = cputime_zero;
init_timer(&p->real_timer);
p->real_timer.data = (unsigned long) p;
p->utime = cputime_zero;
p->stime = cputime_zero;
p->rchar = 0; /* I/O counter: bytes read */
p->wchar = 0; /* I/O counter: bytes written */
p->syscr = 0; /* I/O counter: read syscalls */
p->syscw = 0; /* I/O counter: write syscalls */
acct_clear_integrals(p);
p->lock_depth = -1; /* -1 = no lock */
do_posix_clock_monotonic_gettime(&p->start_time);
p->security = NULL;
p->io_context = NULL;
p->io_wait = NULL;
p->audit_context = NULL;
#ifdef CONFIG_NUMA
p->mempolicy = mpol_copy(p->mempolicy);
if (IS_ERR(p->mempolicy)) {
retval = PTR_ERR(p->mempolicy);
p->mempolicy = NULL;
goto bad_fork_cleanup;
}
#endif
p->tgid = p->pid;
if (clone_flags & CLONE_THREAD)
p->tgid = current->tgid;
if ((retval = security_task_alloc(p)))
goto bad_fork_cleanup_policy;
if ((retval = audit_alloc(p)))
goto bad_fork_cleanup_security;
//调用这些函数来创建新的数据结构,并把父进程相应数据结构的数值复制到新数据结构中,除非clone_flags参数指出它们有不同的数值。
/* copy all the process information */
if ((retval = copy_semundo(clone_flags, p)))
goto bad_fork_cleanup_audit;
if ((retval = copy_files(clone_flags, p)))
goto bad_fork_cleanup_semundo;
if ((retval = copy_fs(clone_flags, p)))
goto bad_fork_cleanup_files;
if ((retval = copy_sighand(clone_flags, p)))
goto bad_fork_cleanup_fs;
if ((retval = copy_signal(clone_flags, p)))
goto bad_fork_cleanup_sighand;
if ((retval = copy_mm(clone_flags, p)))
goto bad_fork_cleanup_signal;
if ((retval = copy_keys(clone_flags, p)))
goto bad_fork_cleanup_mm;
if ((retval = copy_namespace(clone_flags, p)))
goto bad_fork_cleanup_keys;
retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs);
if (retval)
goto bad_fork_cleanup_namespace;
如果clone_flags参数的值被设置为CLONE_CHILD_SETTID或者CLONE_CHILD_CLEARTID,就把child_tidptr参数的数值分别复制到tsk->set_child_tid或者tsk->clear_child_tid字段。
这些标志说明:必须改变子进程用户态地址空间的child_tidptr所指向的变量的数值,不过实际的写操作要稍后再执行。
p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? child_tidptr : NULL;
p->clear_child_tid = (clone_flags & CLONE_CHILD_CLEARTID) ? child_tidptr: NULL;
清除子进程thread_info结构的TIF_SYSCALL_TRACE标志,以使ret_from_fork()函数不会把系统调用结束的消息通知给调试进程。
clear_tsk_thread_flag(p, TIF_SYSCALL_TRACE);
p->parent_exec_id = p->self_exec_id;
用clone_flags参数低位的信号数字编码初始化tsk->exit_signal字段,如果CLONE_THREAD标志被置位,就把tsk->exit_signal字段初始化为-1.只有当线程组的最后一个成员“死亡”,才会产生一个信号,以通知线程组的领头进程的父进程。
p->exit_signal = (clone_flags & CLONE_THREAD) ? -1 : (clone_flags & CSIGNAL);
p->pdeath_signal = 0;
p->exit_state = 0;
调用此函数完成对新进程调度程序数据结构的初始化。该函数把新进程的状态设置为TASK_RUNNING,并把thread_info结构的preempt_count字段设置为1,从而禁止内核抢占,为了保证公平的进程调度,该函数在父子进程间共享父进程的时间片。
sched_fork(p);
p->group_leader = p;
INIT_LIST_HEAD(&p->ptrace_children);
INIT_LIST_HEAD(&p->ptrace_list);
write_lock_irq(&tasklist_lock);
p->cpus_allowed = current->cpus_allowed;
把新进程的thread_info结构的cpu字段设置为由smp_processor_id()所返回的本地cpu号。
set_task_cpu(p, smp_processor_id());
if (sigismember(¤t->pending.signal, SIGKILL)) {
write_unlock_irq(&tasklist_lock);
retval = -EINTR;
goto bad_fork_cleanup_namespace;
}
初始化表示亲子关系的字段。尤其是,如果CLONE_PARENT或者CLONE_THREAD被设置,就用curent->real_parent的值初始化tsk->real_parent和tsk->parent,因此子进程的父进程似乎是当前进程的父进程。否则将tsk->real_parent和tsk->parent置为当前进程。
if (clone_flags & (CLONE_PARENT|CLONE_THREAD))
p->real_parent = current->real_parent;
else
p->real_parent = current;
p->parent = p->real_parent;
if (clone_flags & CLONE_THREAD) {
spin_lock(¤t->sighand->siglock);
if (current->signal->flags & SIGNAL_GROUP_EXIT) {
spin_unlock(¤t->sighand->siglock);
write_unlock_irq(&tasklist_lock);
retval = -EAGAIN;
goto bad_fork_cleanup_namespace;
}
p->group_leader = current->group_leader;
if (current->signal->group_stop_count > 0) {
current->signal->group_stop_count++;
set_tsk_thread_flag(p, TIF_SIGPENDING);
}
spin_unlock(¤t->sighand->siglock);
}
将新进程描述符插入进程链表
SET_LINKS(p);
如果子进程必须被跟踪,p->ptrace被设置成PT_PTRACED,就将current->parent赋给tsk->parent,并将子进程插入调试程序的跟踪链表。
if (unlikely(p->ptrace & PT_PTRACED))
__ptrace_link(p, current->parent);
将新进程描述符插入散列表
attach_pid(p, PIDTYPE_PID, p->pid);
attach_pid(p, PIDTYPE_TGID, p->tgid);
if (thread_group_leader(p)) {
attach_pid(p, PIDTYPE_PGID, process_group(p));
attach_pid(p, PIDTYPE_SID, p->signal->session);
if (p->pid)
__get_cpu_var(process_counts)++;
}
递增此变量。
nr_threads++;
total_forks++;
write_unlock_irq(&tasklist_lock);
retval = 0;
fork_out:
if (retval)
return ERR_PTR(retval);
return p;
bad_fork_cleanup_namespace:
exit_namespace(p);
bad_fork_cleanup_keys:
exit_keys(p);
bad_fork_cleanup_mm:
if (p->mm)
mmput(p->mm);
bad_fork_cleanup_signal:
exit_signal(p);
bad_fork_cleanup_sighand:
exit_sighand(p);
bad_fork_cleanup_fs:
exit_fs(p); /* blocking */
bad_fork_cleanup_files:
exit_files(p); /* blocking */
bad_fork_cleanup_semundo:
exit_sem(p);
bad_fork_cleanup_audit:
audit_free(p);
bad_fork_cleanup_security:
security_task_free(p);
bad_fork_cleanup_policy:
#ifdef CONFIG_NUMA
mpol_free(p->mempolicy);
#endif
bad_fork_cleanup:
if (p->binfmt)
module_put(p->binfmt->module);
bad_fork_cleanup_put_domain:
module_put(p->thread_info->exec_domain->module);
bad_fork_cleanup_count:
put_group_info(p->group_info);
atomic_dec(&p->user->processes);
free_uid(p->user);
bad_fork_free:
free_task(p);
goto fork_out;
}
do_fork结束之后,有了处于可运行状态的完整的子进程。但是还没有实际运行,调度程序要决定何时把cpu交给这个子进程。在以后的进程切换中,调度程序继续完善子进程:把子进程描述符thread字段的数值装入几个cpu寄存器。特别是把thread.esp(即把子进程内核态堆栈的地址)装入esp寄存器,把函数ret_from_fork()的地址装入eip寄存器。用存放在栈中的数值再装载所有的寄存器,并强迫cpu返回到用户态。然后在fork(),vfork(),clone()系统调用结束时,新进程将开始执行。系统调用的返回数值放在eax寄存器中:返回给子进程的数值是0,返回给父进程的数值是子进程的pid。
static struct task_struct *dup_task_struct(struct task_struct *orig)
{
struct task_struct *tsk;
struct thread_info *ti;
prepare_to_copy(orig);
tsk = alloc_task_struct();//为新进程获取进程描述符,并将描述符地址保存在tsk局部变量中
if (!tsk)
return NULL;
ti = alloc_thread_info(tsk);//获取一块空闲内存区,用来存放新进程的thread_info结构和内核栈,并将这块内存区字段的地址存在局部变量ti中。这块内存区字段的大小为8kb或者4kb.
if (!ti) {
free_task_struct(tsk);
return NULL;
}
*ti = *orig->thread_info;将current进程描述符的内容复制到tsk所指向的task_sturct结构中,然后把tsk->thread_info置为ti.
*tsk = *orig;把current进程的thread_info描述符的内容复制到ti所指向的结构中,然后把ti->task置为tsk.
tsk->thread_info = ti;
ti->task = tsk;
/* One for us, one for whoever does the "release_task()" (usually parent) */
atomic_set(&tsk->usage,2);把新进程描述符的使用计数器(tsk->usage)置为2,用来表示进程描述符正在被使用而且其相应的进程处于活动状态(进程状态即不是EXIT_ZOMBIE,也不是EXIT_DEAD)
return tsk;返回新进程的进程描述符指针。
}
# define alloc_task_struct() kmem_cache_alloc(task_struct_cachep, GFP_KERNEL)
#ifdef CONFIG_DEBUG_STACK_USAGE
#define alloc_thread_info(tsk) /
({ /
struct thread_info *ret; /
/
ret = kmalloc(THREAD_SIZE, GFP_KERNEL); /
if (ret) /
memset(ret, 0, THREAD_SIZE); /
ret; /
})
#else
#define alloc_thread_info(tsk) kmalloc(THREAD_SIZE, GFP_KERNEL)
#endif
static inline void copy_flags(unsigned long clone_flags, struct task_struct *p)
{
unsigned long new_flags = p->flags;
new_flags &= ~PF_SUPERPRIV;清除此标志,该标志表示进程是否使用了某种超级用户权限
new_flags |= PF_FORKNOEXEC;然后设置此标志位,表示子进程还没有发出execve()系统调用
if (!(clone_flags & CLONE_PTRACE))
p->ptrace = 0;
p->flags = new_flags;
}
int copy_thread(int nr, unsigned long clone_flags, unsigned long esp,
unsigned long unused,
struct task_struct * p, struct pt_regs * regs)
{
struct pt_regs * childregs;
struct task_struct *tsk;
int err;
childregs = ((struct pt_regs *) (THREAD_SIZE + (unsigned long) p->thread_info)) - 1;
*childregs = *regs;
childregs->eax = 0;
childregs->esp = esp;
p->thread.esp = (unsigned long) childregs;
p->thread.esp0 = (unsigned long) (childregs+1);
p->thread.eip = (unsigned long) ret_from_fork;
savesegment(fs,p->thread.fs);
savesegment(gs,p->thread.gs);
tsk = current;
if (unlikely(NULL != tsk->thread.io_bitmap_ptr)) {
p->thread.io_bitmap_ptr = kmalloc(IO_BITMAP_BYTES, GFP_KERNEL);
if (!p->thread.io_bitmap_ptr) {
p->thread.io_bitmap_max = 0;
return -ENOMEM;
}
memcpy(p->thread.io_bitmap_ptr, tsk->thread.io_bitmap_ptr,
IO_BITMAP_BYTES);
}
/*
* Set a new TLS for the child thread?
*/
if (clone_flags & CLONE_SETTLS) {
struct desc_struct *desc;
struct user_desc info;
int idx;
err = -EFAULT;
if (copy_from_user(&info, (void __user *)childregs->esi, sizeof(info)))
goto out;
err = -EINVAL;
if (LDT_empty(&info))
goto out;
idx = info.entry_number;
if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX)
goto out;
desc = p->thread.tls_array + idx - GDT_ENTRY_TLS_MIN;
desc->a = LDT_entry_a(&info);
desc->b = LDT_entry_b(&info);
}
err = 0;
out:
if (err && p->thread.io_bitmap_ptr) {
kfree(p->thread.io_bitmap_ptr);
p->thread.io_bitmap_max = 0;
}
return err;
}
static task_t *copy_process(unsigned long clone_flags,unsigned long stack_start,struct pt_regs *regs,unsigned long stack_size,int __user *parent_tidptr,int __user *child_tidptr,int pid)
{
int retval;
struct task_struct *p = NULL;
if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS))
return ERR_PTR(-EINVAL);
if ((clone_flags & CLONE_THREAD) && !(clone_flags & CLONE_SIGHAND))
return ERR_PTR(-EINVAL);
if ((clone_flags & CLONE_SIGHAND) && !(clone_flags & CLONE_VM))
return ERR_PTR(-EINVAL);
上面的一组是互相矛盾的,所以必须检查,如第一个。clone不能又需要自己的空间,又屏蔽文件初始许可权的位掩码。
而第二行当CLONE_THREAD被设置,同时CLONE_SIGHAND又被清零的时候是不可以的,因为同一线程组中的轻量级进程必须共享信号
CLONE_SIGHAND标志被设置,CLONE_VM被清零也不可以,因为共享信号处理程序的轻量级进程也必须共享内存描述符
retval = security_task_create(clone_flags);//执行附加的安全检查
if (retval)
goto fork_out;
retval = -ENOMEM;
p = dup_task_struct(current);//为子进程获取进程描述符
if (!p)
goto fork_out;
retval = -EAGAIN;
检查current中的signal->rlim[RLIMIT_NPROC].rlim_cur是否小于或者等于用户所拥有的进程数。如果小于,返回错误码。此函数从每用户数据结构user_struct中获取用户所拥有的进程数。通过进程描述符user字段的指针可以找到这个数据结构
if (atomic_read(&p->user->processes) >= p->signal->rlim[RLIMIT_NPROC].rlim_cur) {
if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE) && p->user != &root_user)
goto bad_fork_free;
}
atomic_inc(&p->user->__count);递增user_struct结构的使用计数器(p->user->__count)
atomic_inc(&p->user->processes);递增用户所拥有的进程计数器(p->user->processes)
get_group_info(p->group_info);
if (nr_threads >= max_threads)检查系统中的进程数量,存放在nr_threads变量中,是否超过max_threads变量的数值。这个变量的缺省数值取决于系统内存容量的大小。总的原则是:所有thread_info描述符和内核栈所占用的空间不能超过物理内存大小的1/8.系统管理员可以通过写/proce/sys/kernel/threads-max文件来改变这个数值。
goto bad_fork_cleanup_count;
if (!try_module_get(p->thread_info->exec_domain->module))
goto bad_fork_cleanup_count;
if (p->binfmt && !try_module_get(p->binfmt->module))
goto bad_fork_cleanup_put_domain;
p->did_exec = 0;初始化为0,记录了进程发出的execve()系统调用的次数
copy_flags(clone_flags, p);设置与进程状态相关的几个关键字段
p->pid = pid;把新进程pid存入task->pid
retval = -EFAULT;
if (clone_flags & CLONE_PARENT_SETTID)如果此标志位被设置,就把子进程的pid复制到参数parent_tidptr指向的用户态变量中
if (put_user(p->pid, parent_tidptr))
goto bad_fork_cleanup;
p->proc_dentry = NULL;
INIT_LIST_HEAD(&p->children);
INIT_LIST_HEAD(&p->sibling);
p->vfork_done = NULL;
spin_lock_init(&p->alloc_lock);
spin_lock_init(&p->proc_lock);
clear_tsk_thread_flag(p, TIF_SIGPENDING);
init_sigpending(&p->pending);
p->it_real_value = 0;
p->it_real_incr = 0;
p->it_virt_value = cputime_zero;
p->it_virt_incr = cputime_zero;
p->it_prof_value = cputime_zero;
p->it_prof_incr = cputime_zero;
init_timer(&p->real_timer);
p->real_timer.data = (unsigned long) p;
p->utime = cputime_zero;
p->stime = cputime_zero;
p->rchar = 0; /* I/O counter: bytes read */
p->wchar = 0; /* I/O counter: bytes written */
p->syscr = 0; /* I/O counter: read syscalls */
p->syscw = 0; /* I/O counter: write syscalls */
acct_clear_integrals(p);
p->lock_depth = -1; /* -1 = no lock */
do_posix_clock_monotonic_gettime(&p->start_time);
p->security = NULL;
p->io_context = NULL;
p->io_wait = NULL;
p->audit_context = NULL;
#ifdef CONFIG_NUMA
p->mempolicy = mpol_copy(p->mempolicy);
if (IS_ERR(p->mempolicy)) {
retval = PTR_ERR(p->mempolicy);
p->mempolicy = NULL;
goto bad_fork_cleanup;
}
#endif
p->tgid = p->pid;
if (clone_flags & CLONE_THREAD)
p->tgid = current->tgid;
if ((retval = security_task_alloc(p)))
goto bad_fork_cleanup_policy;
if ((retval = audit_alloc(p)))
goto bad_fork_cleanup_security;
//调用这些函数来创建新的数据结构,并把父进程相应数据结构的数值复制到新数据结构中,除非clone_flags参数指出它们有不同的数值。
/* copy all the process information */
if ((retval = copy_semundo(clone_flags, p)))
goto bad_fork_cleanup_audit;
if ((retval = copy_files(clone_flags, p)))
goto bad_fork_cleanup_semundo;
if ((retval = copy_fs(clone_flags, p)))
goto bad_fork_cleanup_files;
if ((retval = copy_sighand(clone_flags, p)))
goto bad_fork_cleanup_fs;
if ((retval = copy_signal(clone_flags, p)))
goto bad_fork_cleanup_sighand;
if ((retval = copy_mm(clone_flags, p)))
goto bad_fork_cleanup_signal;
if ((retval = copy_keys(clone_flags, p)))
goto bad_fork_cleanup_mm;
if ((retval = copy_namespace(clone_flags, p)))
goto bad_fork_cleanup_keys;
retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs);
if (retval)
goto bad_fork_cleanup_namespace;
如果clone_flags参数的值被设置为CLONE_CHILD_SETTID或者CLONE_CHILD_CLEARTID,就把child_tidptr参数的数值分别复制到tsk->set_child_tid或者tsk->clear_child_tid字段。
这些标志说明:必须改变子进程用户态地址空间的child_tidptr所指向的变量的数值,不过实际的写操作要稍后再执行。
p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? child_tidptr : NULL;
p->clear_child_tid = (clone_flags & CLONE_CHILD_CLEARTID) ? child_tidptr: NULL;
清除子进程thread_info结构的TIF_SYSCALL_TRACE标志,以使ret_from_fork()函数不会把系统调用结束的消息通知给调试进程。
clear_tsk_thread_flag(p, TIF_SYSCALL_TRACE);
p->parent_exec_id = p->self_exec_id;
用clone_flags参数低位的信号数字编码初始化tsk->exit_signal字段,如果CLONE_THREAD标志被置位,就把tsk->exit_signal字段初始化为-1.只有当线程组的最后一个成员“死亡”,才会产生一个信号,以通知线程组的领头进程的父进程。
p->exit_signal = (clone_flags & CLONE_THREAD) ? -1 : (clone_flags & CSIGNAL);
p->pdeath_signal = 0;
p->exit_state = 0;
调用此函数完成对新进程调度程序数据结构的初始化。该函数把新进程的状态设置为TASK_RUNNING,并把thread_info结构的preempt_count字段设置为1,从而禁止内核抢占,为了保证公平的进程调度,该函数在父子进程间共享父进程的时间片。
sched_fork(p);
p->group_leader = p;
INIT_LIST_HEAD(&p->ptrace_children);
INIT_LIST_HEAD(&p->ptrace_list);
write_lock_irq(&tasklist_lock);
p->cpus_allowed = current->cpus_allowed;
把新进程的thread_info结构的cpu字段设置为由smp_processor_id()所返回的本地cpu号。
set_task_cpu(p, smp_processor_id());
if (sigismember(¤t->pending.signal, SIGKILL)) {
write_unlock_irq(&tasklist_lock);
retval = -EINTR;
goto bad_fork_cleanup_namespace;
}
初始化表示亲子关系的字段。尤其是,如果CLONE_PARENT或者CLONE_THREAD被设置,就用curent->real_parent的值初始化tsk->real_parent和tsk->parent,因此子进程的父进程似乎是当前进程的父进程。否则将tsk->real_parent和tsk->parent置为当前进程。
if (clone_flags & (CLONE_PARENT|CLONE_THREAD))
p->real_parent = current->real_parent;
else
p->real_parent = current;
p->parent = p->real_parent;
if (clone_flags & CLONE_THREAD) {
spin_lock(¤t->sighand->siglock);
if (current->signal->flags & SIGNAL_GROUP_EXIT) {
spin_unlock(¤t->sighand->siglock);
write_unlock_irq(&tasklist_lock);
retval = -EAGAIN;
goto bad_fork_cleanup_namespace;
}
p->group_leader = current->group_leader;
if (current->signal->group_stop_count > 0) {
current->signal->group_stop_count++;
set_tsk_thread_flag(p, TIF_SIGPENDING);
}
spin_unlock(¤t->sighand->siglock);
}
将新进程描述符插入进程链表
SET_LINKS(p);
如果子进程必须被跟踪,p->ptrace被设置成PT_PTRACED,就将current->parent赋给tsk->parent,并将子进程插入调试程序的跟踪链表。
if (unlikely(p->ptrace & PT_PTRACED))
__ptrace_link(p, current->parent);
将新进程描述符插入散列表
attach_pid(p, PIDTYPE_PID, p->pid);
attach_pid(p, PIDTYPE_TGID, p->tgid);
if (thread_group_leader(p)) {
attach_pid(p, PIDTYPE_PGID, process_group(p));
attach_pid(p, PIDTYPE_SID, p->signal->session);
if (p->pid)
__get_cpu_var(process_counts)++;
}
递增此变量。
nr_threads++;
total_forks++;
write_unlock_irq(&tasklist_lock);
retval = 0;
fork_out:
if (retval)
return ERR_PTR(retval);
return p;
bad_fork_cleanup_namespace:
exit_namespace(p);
bad_fork_cleanup_keys:
exit_keys(p);
bad_fork_cleanup_mm:
if (p->mm)
mmput(p->mm);
bad_fork_cleanup_signal:
exit_signal(p);
bad_fork_cleanup_sighand:
exit_sighand(p);
bad_fork_cleanup_fs:
exit_fs(p); /* blocking */
bad_fork_cleanup_files:
exit_files(p); /* blocking */
bad_fork_cleanup_semundo:
exit_sem(p);
bad_fork_cleanup_audit:
audit_free(p);
bad_fork_cleanup_security:
security_task_free(p);
bad_fork_cleanup_policy:
#ifdef CONFIG_NUMA
mpol_free(p->mempolicy);
#endif
bad_fork_cleanup:
if (p->binfmt)
module_put(p->binfmt->module);
bad_fork_cleanup_put_domain:
module_put(p->thread_info->exec_domain->module);
bad_fork_cleanup_count:
put_group_info(p->group_info);
atomic_dec(&p->user->processes);
free_uid(p->user);
bad_fork_free:
free_task(p);
goto fork_out;
}
do_fork结束之后,有了处于可运行状态的完整的子进程。但是还没有实际运行,调度程序要决定何时把cpu交给这个子进程。在以后的进程切换中,调度程序继续完善子进程:把子进程描述符thread字段的数值装入几个cpu寄存器。特别是把thread.esp(即把子进程内核态堆栈的地址)装入esp寄存器,把函数ret_from_fork()的地址装入eip寄存器。用存放在栈中的数值再装载所有的寄存器,并强迫cpu返回到用户态。然后在fork(),vfork(),clone()系统调用结束时,新进程将开始执行。系统调用的返回数值放在eax寄存器中:返回给子进程的数值是0,返回给父进程的数值是子进程的pid。
static struct task_struct *dup_task_struct(struct task_struct *orig)
{
struct task_struct *tsk;
struct thread_info *ti;
prepare_to_copy(orig);
tsk = alloc_task_struct();//为新进程获取进程描述符,并将描述符地址保存在tsk局部变量中
if (!tsk)
return NULL;
ti = alloc_thread_info(tsk);//获取一块空闲内存区,用来存放新进程的thread_info结构和内核栈,并将这块内存区字段的地址存在局部变量ti中。这块内存区字段的大小为8kb或者4kb.
if (!ti) {
free_task_struct(tsk);
return NULL;
}
*ti = *orig->thread_info;将current进程描述符的内容复制到tsk所指向的task_sturct结构中,然后把tsk->thread_info置为ti.
*tsk = *orig;把current进程的thread_info描述符的内容复制到ti所指向的结构中,然后把ti->task置为tsk.
tsk->thread_info = ti;
ti->task = tsk;
/* One for us, one for whoever does the "release_task()" (usually parent) */
atomic_set(&tsk->usage,2);把新进程描述符的使用计数器(tsk->usage)置为2,用来表示进程描述符正在被使用而且其相应的进程处于活动状态(进程状态即不是EXIT_ZOMBIE,也不是EXIT_DEAD)
return tsk;返回新进程的进程描述符指针。
}
# define alloc_task_struct() kmem_cache_alloc(task_struct_cachep, GFP_KERNEL)
#ifdef CONFIG_DEBUG_STACK_USAGE
#define alloc_thread_info(tsk) /
({ /
struct thread_info *ret; /
/
ret = kmalloc(THREAD_SIZE, GFP_KERNEL); /
if (ret) /
memset(ret, 0, THREAD_SIZE); /
ret; /
})
#else
#define alloc_thread_info(tsk) kmalloc(THREAD_SIZE, GFP_KERNEL)
#endif
static inline void copy_flags(unsigned long clone_flags, struct task_struct *p)
{
unsigned long new_flags = p->flags;
new_flags &= ~PF_SUPERPRIV;清除此标志,该标志表示进程是否使用了某种超级用户权限
new_flags |= PF_FORKNOEXEC;然后设置此标志位,表示子进程还没有发出execve()系统调用
if (!(clone_flags & CLONE_PTRACE))
p->ptrace = 0;
p->flags = new_flags;
}
int copy_thread(int nr, unsigned long clone_flags, unsigned long esp,
unsigned long unused,
struct task_struct * p, struct pt_regs * regs)
{
struct pt_regs * childregs;
struct task_struct *tsk;
int err;
childregs = ((struct pt_regs *) (THREAD_SIZE + (unsigned long) p->thread_info)) - 1;
*childregs = *regs;
childregs->eax = 0;
childregs->esp = esp;
p->thread.esp = (unsigned long) childregs;
p->thread.esp0 = (unsigned long) (childregs+1);
p->thread.eip = (unsigned long) ret_from_fork;
savesegment(fs,p->thread.fs);
savesegment(gs,p->thread.gs);
tsk = current;
if (unlikely(NULL != tsk->thread.io_bitmap_ptr)) {
p->thread.io_bitmap_ptr = kmalloc(IO_BITMAP_BYTES, GFP_KERNEL);
if (!p->thread.io_bitmap_ptr) {
p->thread.io_bitmap_max = 0;
return -ENOMEM;
}
memcpy(p->thread.io_bitmap_ptr, tsk->thread.io_bitmap_ptr,
IO_BITMAP_BYTES);
}
/*
* Set a new TLS for the child thread?
*/
if (clone_flags & CLONE_SETTLS) {
struct desc_struct *desc;
struct user_desc info;
int idx;
err = -EFAULT;
if (copy_from_user(&info, (void __user *)childregs->esi, sizeof(info)))
goto out;
err = -EINVAL;
if (LDT_empty(&info))
goto out;
idx = info.entry_number;
if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX)
goto out;
desc = p->thread.tls_array + idx - GDT_ENTRY_TLS_MIN;
desc->a = LDT_entry_a(&info);
desc->b = LDT_entry_b(&info);
}
err = 0;
out:
if (err && p->thread.io_bitmap_ptr) {
kfree(p->thread.io_bitmap_ptr);
p->thread.io_bitmap_max = 0;
}
return err;
}
相关文章推荐
- linux 2.6源代码情景分析笔记之进程4
- linux 2.6源代码情景分析笔记之进程7
- linux 2.6源代码情景分析笔记之进程5
- linux 2.6源代码情景分析笔记之进程10
- linux 2.6源代码情景分析笔记之进程6
- linux 2.6源代码情景分析笔记之进程1
- linux 2.6源代码情景分析笔记之进程8
- linux 2.6源代码情景分析笔记之进程9
- linux 2.6源代码情景分析笔记之进程2
- linux 2.6源代码情景分析笔记之进程3
- linux 2.6源代码情景分析笔记之中断与异常3
- linux 2.6源代码情景分析笔记之内存4
- linux 2.6源代码情景分析笔记之内存1
- linux 2.6源代码情景分析笔记之中断与异常4
- linux 2.6源代码情景分析笔记之中断与异常5
- linux 2.6源代码情景分析笔记之中断与异常6
- linux 2.6源代码情景分析笔记之内存2
- linux 2.6源代码情景分析笔记之内存5
- linux 2.6源代码情景分析笔记之系统启动1
- linux 2.6源代码情景分析笔记之内存6