您的位置:首页 > 运维架构 > Linux

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;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: