Linux进程管理之进程的创建
开发平台:Ubuntu 11.04
内核源码:linux-2.6.38.8.tar.bz2
目标平台:ARM体系结构
在Linux应用程序的开发中,可以通过fork、vfork和clone等系统调用来创建一个子进程,它们在Linux内核中的入口点分别为sys_fork、sys_vfork和sys_clone函数。
/* linux-2.6.38.8/arch/arm/kernel/sys_arm.c */
asmlinkage int sys_fork(struct pt_regs *regs)
{
#ifdef CONFIG_MMU
return do_fork(SIGCHLD, regs->ARM_sp, regs, 0, NULL, NULL);
#else
/* can not support in nommu mode */
return(-EINVAL);
#endif
}
asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp,
int __user *parent_tidptr, int tls_val,
int __user *child_tidptr, struct pt_regs *regs)
{
if (!newsp)
newsp = regs->ARM_sp;
return do_fork(clone_flags, newsp, regs, 0, parent_tidptr, child_tidptr);
}
asmlinkage int sys_vfork(struct pt_regs *regs)
{
return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->ARM_sp, regs, 0, NULL, NULL);
}
在这里,asmlinkage对ARM体系结构来说是忽略的。
只有支持MMU的系统才能使用sys_fork函数。
这三个函数的实现都只是对do_fork函数的简单封装。
1、do_fork函数
(1)、函数参数
/* linux-2.6.38.8/kernel/fork.c */
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)
clone_flags是clone标志,用于控制创建子进程的行为,可能的取值如下:
/* linux-2.6.38.8/include/linux/sched.h */
#define CSIGNAL 0x000000ff /* signal mask to be sent at exit */
#define CLONE_VM 0x00000100 /* set if VM shared between processes */
#define CLONE_FS 0x00000200 /* set if fs info shared between processes */
#define CLONE_FILES 0x00000400 /* set if open files shared between processes */
#define CLONE_SIGHAND 0x00000800 /* set if signal handlers and blocked signals shared */
#define CLONE_PTRACE 0x00002000 /* set if we want to let tracing continue on the child too */
#define CLONE_VFORK 0x00004000 /* set if the parent wants the child to wake it up on mm_release */
#define CLONE_PARENT 0x00008000 /* set if we want to have the same parent as the cloner */
#define CLONE_THREAD 0x00010000 /* Same thread group? */
#define CLONE_NEWNS 0x00020000 /* New namespace group? */
#define CLONE_SYSVSEM 0x00040000 /* share system V SEM_UNDO semantics */
#define CLONE_SETTLS 0x00080000 /* create a new TLS for the child */
#define CLONE_PARENT_SETTID 0x00100000 /* set the TID in the parent */
#define CLONE_CHILD_CLEARTID 0x00200000 /* clear the TID in the child */
#define CLONE_DETACHED 0x00400000 /* Unused, ignored */
#define CLONE_UNTRACED 0x00800000 /* set if the tracing process can't force CLONE_PTRACE on this clone */
#define CLONE_CHILD_SETTID 0x01000000 /* set the TID in the child */
/* 0x02000000 was previously the unused CLONE_STOPPED (Start in stopped state)
and is now available for re-use. */
#define CLONE_NEWUTS 0x04000000 /* New utsname group? */
#define CLONE_NEWIPC 0x08000000 /* New ipcs */
#define CLONE_NEWUSER 0x10000000 /* New user namespace */
#define CLONE_NEWPID 0x20000000 /* New pid namespace */
#define CLONE_NEWNET 0x40000000 /* New network namespace */
#define CLONE_IO 0x80000000 /* Clone io context */
stack_start等于父进程当前的栈指针。
regs指向通用寄存器集合,它以原始的形式保存了调用参数。在ARM体系结构上,pt_regs结构体的定义如下:
/* linux-2.6.38.8/arch/arm/include/asm/ptrace.h */
struct pt_regs {
unsigned long uregs[18];
};
#define ARM_cpsr uregs[16]
#define ARM_pc uregs[15]
#define ARM_lr uregs[14]
#define ARM_sp uregs[13]
#define ARM_ip uregs[12]
#define ARM_fp uregs[11]
#define ARM_r10 uregs[10]
#define ARM_r9 uregs[9]
#define ARM_r8 uregs[8]
#define ARM_r7 uregs[7]
#define ARM_r6 uregs[6]
#define ARM_r5 uregs[5]
#define ARM_r4 uregs[4]
#define ARM_r3 uregs[3]
#define ARM_r2 uregs[2]
#define ARM_r1 uregs[1]
#define ARM_r0 uregs[0]
#define ARM_ORIG_r0 uregs[17]
stack_size总被设置为0。
parent_tidptr和child_tidptr分别表示父子进程的用户态变量地址,只有在CLONE_PARENT_SETTID或CLONE_CHILD_SETTID标志被设置时才有意义。
(2)、检查冲突的clone标志和必要的权限
if (clone_flags & CLONE_NEWUSER) {
if (clone_flags & CLONE_THREAD)
return -EINVAL;
/* hopefully this check will go away when userns support is
* complete
*/
if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SETUID) ||
!capable(CAP_SETGID))
return -EPERM;
}
(3)、检查当前进程(也就是父进程)的ptrace字段(current->ptrace),如果它的值不为0,则表示有另外一个进程正在跟踪父进程
if (likely(user_mode(regs)))
trace = tracehook_prepare_clone(clone_flags);
创建内核线程时不需要检查。
(4)、调用copy_process函数
p = copy_process(clone_flags, stack_start, regs, stack_size,
child_tidptr, NULL, trace);
(5)、若copy_process函数创建子进程失败,则返回PTR_ERR函数转换后的错误码
/* linux-2.6.38.8/include/linux/err.h */
static inline long __must_check PTR_ERR(const void *ptr)
{
return (long) ptr;
}
在一个返回指针的函数(如copy_process)中,应该先使用ERR_PTR函数把所有的错误码都转变为指针,然后在调用函数中通过IS_ERR函数来检查它的返回值,或通过PTR_ERR函数恢复返回值对应的错误码。在实际应用中,IS_ERR或PTR_ERR应该与ERR_PTR配对使用。
static inline void * __must_check ERR_PTR(long error)
{
return (void *) error;
}
#define MAX_ERRNO 4095
#define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO)
static inline long __must_check IS_ERR(const void *ptr)
{
return IS_ERR_VALUE((unsigned long)ptr);
}
(6)、若成功创建子进程,内核还须执行一些收尾操作
1)、追踪点函数,通过宏TRACE_EVENT定义在linux-2.6.38.8/include/trace/events/sched.h文件中,在ARM体系结构上不用考虑
trace_sched_process_fork(current, p);
2)、调用task_pid_vnr函数获取子进程的局部PID
nr = task_pid_vnr(p);
/* linux-2.6.38.8/include/linux/sched.h */
static inline pid_t task_pid_vnr(struct task_struct *tsk)
{
return __task_pid_nr_ns(tsk, PIDTYPE_PID, NULL);
}
/* linux-2.6.38.8/kernel/pid.c */
pid_t __task_pid_nr_ns(struct task_struct *task, enum pid_type type,
struct pid_namespace *ns)
{
pid_t nr = 0;
rcu_read_lock();
if (!ns)
ns = current->nsproxy->pid_ns;
if (likely(pid_alive(task))) {
if (type != PIDTYPE_PID)
task = task->group_leader;
nr = pid_nr_ns(task->pids[type].pid, ns);
}
rcu_read_unlock();
return nr;
}
在这里,子进程共享父进程的PID命名空间。
然后,在CLONE_PARENT_SETTID标志被设置时,把此PID值通过parent_tidptr指针输出到用户空间
if (clone_flags & CLONE_PARENT_SETTID)
put_user(nr, parent_tidptr);
3)、在CLONE_VFORK标志被设置时,初始化completion同步原语
if (clone_flags & CLONE_VFORK) {
p->vfork_done = &vfork;
init_completion(&vfork);
}
completion结构体变量vfork被用于父子进程之间的同步。task_struct结构体中的vfork_done字段就是用于此目的。
4)、系统调用审计
audit_finish_fork(p);
只有在CONFIG_AUDITSYSCALL选项被配置时才有效,在S3C2410的配置中没有这个选项。
5)、如果子进程的ptrace字段不为0,则为子进程增加挂起信号(SIGSTOP),并设置它的thread_info 结构体的flags字段为TIF_SIGPENDING。
tracehook_report_clone(regs, clone_flags, nr, p);
6)、清除PF_STARTING标志
p->flags &= ~PF_STARTING;
7)、唤醒子进程
wake_up_new_task(p, clone_flags);
在CONFIG_SMP选项未配置时,它的实现只是简单地调用activate_task函数把子进程加载到运行队列。
8)、当tracehook_prepare_clone的返回值trace不为0时(即父进程被跟踪),则把子进程的PID存入当前进程的ptrace_message字段,并调用ptrace_notify函数。
tracehook_report_clone_complete(trace, regs,
clone_flags, nr, p);
9)、在CLONE_VFORK标志被设置时,父进程通过调用wait_for_completion函数使其被阻塞,直到子进程退出(或执行新的程序)并接受到信号为止(内核通过complete函数)。
if (clone_flags & CLONE_VFORK) {
freezer_do_not_count();
wait_for_completion(&vfork);
freezer_count();
tracehook_report_vfork_done(p, nr);
}
其中,freezer_do_not_count和freezer_count函数分别用于设置或清除PF_FREEZER_SKIP标志。
tracehook_report_vfork_done函数用于向跟踪程序报告子进程已退出或已执行新的程序。
2、copy_process函数
static struct task_struct *copy_process(unsigned long clone_flags,
unsigned long stack_start,
struct pt_regs *regs,
unsigned long stack_size,
int __user *child_tidptr,
struct pid *pid,
int trace)
1)、检查冲突的clone标志
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);
if ((clone_flags & CLONE_PARENT) &&
current->signal->flags & SIGNAL_UNKILLABLE)
return ERR_PTR(-EINVAL);
2)、附加的安全检查
retval = security_task_create(clone_flags);
if (retval)
goto fork_out;
/* linux-2.6.38.8/security/security.c */
int security_task_create(unsigned long clone_flags)
{
return security_ops->task_create(clone_flags);
}
/* linux-2.6.38.8/security/capability.c */
static int cap_task_create(unsigned long clone_flags)
{
return 0;
}
函数指针task_create指向cap_task_create,返回0表示允许操作。更复杂的安全模型是美国国家安全局开发的SELinux,这时的函数指针task_create指向selinux_task_create函数。
3)、调用dup_task_struct函数为子进程获取进程描述符(即task_struct结构体)
retval = -ENOMEM;
p = dup_task_struct(current);
if (!p)
goto fork_out;
/* linux-2.6.38.8/kernel/fork.c */
static struct task_struct *dup_task_struct(struct task_struct *orig)
a、对ARM体系结构来说,prepare_to_copy函数为空
prepare_to_copy(orig);
b、执行alloc_task_struct宏,为新进程分配进程描述符(即task_struct结构体)
tsk = alloc_task_struct();
if (!tsk)
return NULL;
# define alloc_task_struct() kmem_cache_alloc(task_struct_cachep, GFP_KERNEL)
c、调用alloc_thread_info函数分配一块2THREAD_SIZE_ORDER个页的空闲的内存区,用来存放新进程的thread_info结构体和内核栈。
ti = alloc_thread_info(tsk);
if (!ti) {
free_task_struct(tsk);
return NULL;
}
#define THREAD_SIZE_ORDER 1
static inline struct thread_info *alloc_thread_info(struct task_struct *tsk)
{
#ifdef CONFIG_DEBUG_STACK_USAGE
gfp_t mask = GFP_KERNEL | __GFP_ZERO;
#else
gfp_t mask = GFP_KERNEL;
#endif
return (struct thread_info *)__get_free_pages(mask, THREAD_SIZE_ORDER);
}
进程描述符和thread_info结构体以及内核栈的关系请参考Linux进程管理之task_struct结构体(上)。
d、复制父进程task_struct结构体中的内容,但指向子进程自身的内核栈
err = arch_dup_task_struct(tsk, orig);
if (err)
goto out;
tsk->stack = ti;
int __attribute__((weak)) arch_dup_task_struct(struct task_struct *dst,
struct task_struct *src)
{
*dst = *src;
return 0;
}
e、初始化子进程的dirties字段
err = prop_local_init_single(&tsk->dirties);
if (err)
goto out;
f、复制父进程的thread_info结构体信息,但指向子进程描述符
setup_thread_stack(tsk, orig);
/* linux-2.6.38.8/include/linux/sched.h */
#define task_thread_info(task) ((struct thread_info *)(task)->stack)
static inline void setup_thread_stack(struct task_struct *p, struct task_struct *org)
{
*task_thread_info(p) = *task_thread_info(org);
task_thread_info(p)->task = p;
}
g、清除子进程thread_info结构体的TIF_USER_RETURN_NOTIFY和TIF_NEED_RESCHED标志
clear_user_return_notifier(tsk);
clear_tsk_need_resched(tsk);
TIF_NEED_RESCHED标志表示该进程想要调度器选择另一个进程来替换它,清除该标志则表示不想要这么做。
h、由于内核栈和thread_info结构体共享2个页面的内存,为防止内核栈中的数据破坏thread_info结构体,因此在它们之间设置一个魔数,用来表示内核栈栈底。
stackend = end_of_stack(tsk);
*stackend = STACK_END_MAGIC; /* for overflow detection */
/* linux-2.6.38.8/include/linux/magic.h */
#define STACK_END_MAGIC 0x57AC6E9D
i、初始化子进程描述符的stack_canary字段
#ifdef CONFIG_CC_STACKPROTECTOR
tsk->stack_canary = get_random_int();
#endif
j、设置子进程描述符的usage字段为2,表示进程描述符正在被使用而且其相应的进程处于活动状态。
atomic_set(&tsk->usage,2);
k、设置子进程描述符的fs_excl、btrace_seq和splice_pipe三个字段为0或NULL。
atomic_set(&tsk->fs_excl, 0);
#ifdef CONFIG_BLK_DEV_IO_TRACE
tsk->btrace_seq = 0;
#endif
tsk->splice_pipe = NULL;
l、进程内核栈计数
account_kernel_stack(ti, 1);
4)、初始化子进程描述符中与ftrace功能相关的字段
ftrace_graph_init_task(p);
5)、初始化子进程描述符的pi_lock、pi_waiters和pi_blocked_on三个字段
rt_mutex_init_task(p);
6)、检查子进程描述符的hardirqs_enabled和softirqs_enabled两个字段是否不为0
#ifdef CONFIG_PROVE_LOCKING
DEBUG_LOCKS_WARN_ON(!p->hardirqs_enabled);
DEBUG_LOCKS_WARN_ON(!p->softirqs_enabled);
#endif
7)、检查用户所拥有的进程数是否超过了限制,如果是,则放弃创建进程,除非当前用户是root用户或者分配了特别的权限
retval = -EAGAIN;
if (atomic_read(&p->real_cred->user->processes) >=
task_rlimit(p, RLIMIT_NPROC)) {
if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE) &&
p->real_cred->user != INIT_USER)
goto bad_fork_free;
}
8)、如果设置了CLONE_THREAD标志,则copy_creds函数会使用父进程的real_cred和cred,但更新了其中的一些计数
retval = copy_creds(p, clone_flags);
if (retval < 0)
goto bad_fork_free;
9)、检查系统中的进程数量(存放在nr_threads变量中)是否超过max_threads变量的值(max_threads变量的值取决于系统内存的大小,总的原则是所有进程的thread_info结构体和内核栈所占用的空间不能超过物理内存的八分之一)。
retval = -EAGAIN;
if (nr_threads >= max_threads)
goto bad_fork_cleanup_count;
10)、如果实现新进程的执行域和可执行格式的内核函数都包含在内核模块中,则递增它们的使用计数器。
if (!try_module_get(task_thread_info(p)->exec_domain->module))
goto bad_fork_cleanup_count;
11)、子进程描述符中众多零碎的字段的初始化
p->did_exec = 0; //用于记录调用execve()的次数
delayacct_tsk_init(p); //delays字段:分配struct task_delay_info大小的内存空间,并初始化其中的自旋锁
copy_flags(clone_flags, p); //flags字段:清除PF_SUPERPRIV和PF_WQ_WORKER标志,并设置PF_FORKNOEXEC和PF_STARTING标志
//还要清除thread_info结构体的TIF_FREEZE标志
INIT_LIST_HEAD(&p->children);
INIT_LIST_HEAD(&p->sibling);
rcu_copy_process(p);
/* linux-2.6.38.8/include/linux/sched.h */
static inline void rcu_copy_process(struct task_struct *p)
{
p->rcu_read_lock_nesting = 0;
p->rcu_read_unlock_special = 0;
#ifdef CONFIG_TREE_PREEMPT_RCU
p->rcu_blocked_node = NULL;
#endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */
#ifdef CONFIG_RCU_BOOST
p->rcu_boost_mutex = NULL;
#endif /* #ifdef CONFIG_RCU_BOOST */
INIT_LIST_HEAD(&p->rcu_node_entry);
}
p->vfork_done = NULL; //在do_fork函数中会用到
spin_lock_init(&p->alloc_lock); //初始化自旋锁
init_sigpending(&p->pending); //清除信号集
/* linux-2.6.38.8/include/linux/signal.h */
static inline void init_sigpending(struct sigpending *sig)
{
sigemptyset(&sig->signal);
INIT_LIST_HEAD(&sig->list);
}
//初始化进程的各种时间为0
//#define cputime_zero (0UL) /* linux-2.6.38.8/include/asm-generic/cputime.h */
p->utime = cputime_zero;
p->stime = cputime_zero;
p->gtime = cputime_zero;
p->utimescaled = cputime_zero;
p->stimescaled = cputime_zero;
#ifndef CONFIG_VIRT_CPU_ACCOUNTING
p->prev_utime = cputime_zero;
p->prev_stime = cputime_zero;
#endif
#if defined(SPLIT_RSS_COUNTING)
memset(&p->rss_stat, 0, sizeof(p->rss_stat)); //初始化struct task_rss_stat变量为0
#endif
p->default_timer_slack_ns = current->timer_slack_ns; //初始化为父进程的timer_slack_ns值
task_io_accounting_init(&p->ioac); //初始化struct task_io_accounting变量为0
acct_clear_integrals(p);
/* linux-2.6.38.8/kernel/tsacct.c */
void acct_clear_integrals(struct task_struct *tsk)
{
tsk->acct_timexpd = 0;
tsk->acct_rss_mem1 = 0;
tsk->acct_vm_mem1 = 0;
}
posix_cpu_timers_init(p);
/* linux-2.6.38.8/kernel/fork.c */
static void posix_cpu_timers_init(struct task_struct *tsk)
{
tsk->cputime_expires.prof_exp = cputime_zero;
tsk->cputime_expires.virt_exp = cputime_zero;
tsk->cputime_expires.sched_exp = 0;
INIT_LIST_HEAD(&tsk->cpu_timers[0]);
INIT_LIST_HEAD(&tsk->cpu_timers[1]);
INIT_LIST_HEAD(&tsk->cpu_timers[2]);
}
p->lock_depth = -1; //大内核锁,-1表示不上锁
do_posix_clock_monotonic_gettime(&p->start_time);
p->real_start_time = p->start_time;
monotonic_to_bootbased(&p->real_start_time); //真实启动时间还须要加上总的睡眠时间
p->io_context = NULL;
p->audit_context = NULL;
cgroup_fork(p); //共享父进程的cgroups,并更新其中的引用计数
/* linux-2.6.38.8/kernel/cgroup.c */
void cgroup_fork(struct task_struct *child)
{
task_lock(current);
child->cgroups = current->cgroups;
get_css_set(child->cgroups);
task_unlock(current);
INIT_LIST_HEAD(&child->cg_list);
}
#ifdef CONFIG_NUMA
p->mempolicy = mpol_dup(p->mempolicy); //为子进程分配新的struct mempolicy变量,但拷贝父进程mempolicy变量中的内容
if (IS_ERR(p->mempolicy)) {
retval = PTR_ERR(p->mempolicy);
p->mempolicy = NULL;
goto bad_fork_cleanup_cgroup;
}
mpol_fix_fork_child_flag(p); //flags字段:如果新的struct mempolicy变量分配成功,则增加PF_MEMPOLICY标志
#endif
#ifdef CONFIG_TRACE_IRQFLAGS
p->irq_events = 0;
#ifdef __ARCH_WANT_INTERRUPTS_ON_CTXSW
p->hardirqs_enabled = 1;
#else
p->hardirqs_enabled = 0;
#endif
p->hardirq_enable_ip = 0;
p->hardirq_enable_event = 0;
p->hardirq_disable_ip = _THIS_IP_;
p->hardirq_disable_event = 0;
p->softirqs_enabled = 1;
p->softirq_enable_ip = _THIS_IP_;
p->softirq_enable_event = 0;
p->softirq_disable_ip = 0;
p->softirq_disable_event = 0;
p->hardirq_context = 0;
p->softirq_context = 0;
#endif
#ifdef CONFIG_LOCKDEP
p->lockdep_depth = 0; /* no locks held yet */
p->curr_chain_key = 0;
p->lockdep_recursion = 0;
#endif
#ifdef CONFIG_DEBUG_MUTEXES
p->blocked_on = NULL; /* not blocked yet */
#endif
#ifdef CONFIG_CGROUP_MEM_RES_CTLR
p->memcg_batch.do_batch = 0;
p->memcg_batch.memcg = NULL;
#endif
12)、调用sched_fork函数初始化子进程描述符与调度有关的字段
sched_fork(p, clone_flags);
/* linux-2.6.38.8/kernel/sched.c */
static void __sched_fork(struct task_struct *p)
{
p->se.exec_start = 0;
p->se.sum_exec_runtime = 0;
p->se.prev_sum_exec_runtime = 0;
p->se.nr_migrations = 0;
#ifdef CONFIG_SCHEDSTATS
memset(&p->se.statistics, 0, sizeof(p->se.statistics));
#endif
INIT_LIST_HEAD(&p->rt.run_list);
p->se.on_rq = 0;
INIT_LIST_HEAD(&p->se.group_node);
#ifdef CONFIG_PREEMPT_NOTIFIERS
INIT_HLIST_HEAD(&p->preempt_notifiers);
#endif
}
void sched_fork(struct task_struct *p, int clone_flags)
{
int cpu = get_cpu();
__sched_fork(p);
/*
* We mark the process as running here. This guarantees that
* nobody will actually run it, and a signal or other external
* event cannot wake it up and insert it on the runqueue either.
*/
p->state = TASK_RUNNING;
/*
* Revert to default priority/policy on fork if requested.
*/
if (unlikely(p->sched_reset_on_fork)) {
if (p->policy == SCHED_FIFO || p->policy == SCHED_RR) {
p->policy = SCHED_NORMAL;
p->normal_prio = p->static_prio;
}
if (PRIO_TO_NICE(p->static_prio) < 0) {
p->static_prio = NICE_TO_PRIO(0);
p->normal_prio = p->static_prio;
set_load_weight(p);
}
/*
* We don't need the reset flag anymore after the fork. It has
* fulfilled its duty:
*/
p->sched_reset_on_fork = 0;
}
/*
* Make sure we do not leak PI boosting priority to the child.
*/
p->prio = current->normal_prio;
if (!rt_prio(p->prio))
p->sched_class = &fair_sched_class;
if (p->sched_class->task_fork)
p->sched_class->task_fork(p);
/*
* The child is not yet in the pid-hash so no cgroup attach races,
* and the cgroup is pinned to this child due to cgroup_fork()
* is ran before sched_fork().
*
* Silence PROVE_RCU.
*/
rcu_read_lock();
set_task_cpu(p, cpu);
rcu_read_unlock();
#if defined(CONFIG_SCHEDSTATS) || defined(CONFIG_TASK_DELAY_ACCT)
if (likely(sched_info_on()))
memset(&p->sched_info, 0, sizeof(p->sched_info));
#endif
#if defined(CONFIG_SMP) && defined(__ARCH_WANT_UNLOCKED_CTXSW)
p->oncpu = 0;
#endif
#ifdef CONFIG_PREEMPT
/* Want to start with kernel preemption disabled. */
task_thread_info(p)->preempt_count = 1;
#endif
#ifdef CONFIG_SMP
plist_node_init(&p->pushable_tasks, MAX_PRIO);
#endif
put_cpu();
}
该函数把子进程的状态设置为TASK_RUNNING,并把thread_info结构体的preempt_count字段设置为1,从而禁止内核抢占。
13)、初始化Performance Event功能相关的字段
retval = perf_event_init_task(p);
if (retval)
goto bad_fork_cleanup_policy;
/* linux-2.6.38.8/kernel/perf_event.c */
int perf_event_init_task(struct task_struct *child)
{
int ctxn, ret;
memset(child->perf_event_ctxp, 0, sizeof(child->perf_event_ctxp));
mutex_init(&child->perf_event_mutex);
INIT_LIST_HEAD(&child->perf_event_list);
for_each_task_context_nr(ctxn) {
ret = perf_event_init_context(child, ctxn);
if (ret)
return ret;
}
return 0;
}
14)、分配一个struct audit_context变量,并把它赋给子进程描述符的audit_context字段
if ((retval = audit_alloc(p)))
goto bad_fork_cleanup_policy;
15)、各种数据的拷贝
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_namespaces(clone_flags, p)))
goto bad_fork_cleanup_mm;
if ((retval = copy_io(clone_flags, p)))
goto bad_fork_cleanup_namespaces;
a、如果CLONE_SYSVSEM标志被设置,copy_semundo则使用父进程的System V信号量。
b、如果CLONE_FILES标志被设置,copy_files则使用父进程的文件描述符;否则创建新的struct files_struct变量。
c、如果CLONE_FS标志被设置,copy_fs则使用父进程的struct fs_struct变量。
d、如果CLONE_SIGHAND标志被设置,copy_sighand则使用父进程的信号处理程序。
e、如果CLONE_THREAD标志被设置,copy_signal则使用父进程的struct signal_struct变量。
f、如果CLONE_VM标志被设置,copy_mm则会让父子进程共享同一地址空间,并把相关的min_flt、maj_flt、nvcsw、nivcsw和last_switch_count字段都初始化为0。
g、如果与命名空间相关的标志都未被设置,copy_namespaces则会让子进程共享父进程相应的命名空间。
h、如果CLONE_IO标志被设置,copy_io则使用父进程的struct io_context变量。
16)、初始化thread_info结构体
retval = copy_thread(clone_flags, stack_start, stack_size, p, regs);
if (retval)
goto bad_fork_cleanup_io;
17)、如果传入的pid参数不为init_struct_pid,则会申请一个新的struct pid实例,并且在CLONE_NEWPID标志被设置时,还需要在proc文件系统中输出新的PID命名空间。
if (pid != &init_struct_pid) {
retval = -ENOMEM;
pid = alloc_pid(p->nsproxy->pid_ns);
if (!pid)
goto bad_fork_cleanup_io;
if (clone_flags & CLONE_NEWPID) {
retval = pid_ns_prepare_proc(p->nsproxy->pid_ns);
if (retval < 0)
goto bad_fork_free_pid;
}
}
18)、获取全局PID值,并在CLONE_THREAD标志被设置时,把子进程的tgid的值初始化为父进程的tgid值。
p->pid = pid_nr(pid);
p->tgid = p->pid;
if (clone_flags & CLONE_THREAD)
p->tgid = current->tgid;
19)、当父子进程的nsproxy不相等时,初始化子进程描述符的cgroups和cg_list两个字段
if (current->nsproxy != p->nsproxy) {
retval = ns_cgroup_clone(p, pid);
if (retval)
goto bad_fork_free_pid;
}
20)、另一些零碎字段的初始化
/* 用户空间线程库使用clone系统调用来生成新线程 */
p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? child_tidptr : NULL; //保存clone系统调用的用户空间指针
/*
* Clear TID on mm_release()?
*/
p->clear_child_tid = (clone_flags & CLONE_CHILD_CLEARTID) ? child_tidptr: NULL; //把用户空间指针保存在另一个字段
#ifdef CONFIG_FUTEX
p->robust_list = NULL;
#ifdef CONFIG_COMPAT
p->compat_robust_list = NULL;
#endif
INIT_LIST_HEAD(&p->pi_state_list);
p->pi_state_cache = NULL;
#endif
/*
* sigaltstack should be cleared when sharing the same VM
*/
if ((clone_flags & (CLONE_VM|CLONE_VFORK)) == CLONE_VM)
p->sas_ss_sp = p->sas_ss_size = 0;
/*
* Syscall tracing and stepping should be turned off in the
* child regardless of CLONE_PTRACE.
*/
user_disable_single_step(p); //ptrace字段:清除PT_SINGLESTEP标志
//thread字段:清除断点(breakpoint)
clear_tsk_thread_flag(p, TIF_SYSCALL_TRACE); //清除thread_info结构体的TIF_SYSCALL_TRACE标志
#ifdef TIF_SYSCALL_EMU
clear_tsk_thread_flag(p, TIF_SYSCALL_EMU); //清除thread_info结构体的TIF_SYSCALL_EMU标志
#endif
clear_all_latency_tracing(p); //初始化子进程描述符的latency_record和latency_record_count两个字段为0
/* ok, now we should be set up.. */
p->exit_signal = (clone_flags & CLONE_THREAD) ? -1 : (clone_flags & CSIGNAL);
//CLONE_THREAD标志被设置时,初始化为-1,否则初始化为clone_flags的最后8位
p->pdeath_signal = 0;
p->exit_state = 0;
p->group_leader = p; //子进程是所在线程组的领头进程
INIT_LIST_HEAD(&p->thread_group);
cgroup_fork_callbacks(p); //cgroups字段:共享父进程的struct css_set结构体
/* linux-2.6.38.8/kernel/cgroup.c */
void cgroup_fork(struct task_struct *child)
{
task_lock(current);
child->cgroups = current->cgroups;
get_css_set(child->cgroups);
task_unlock(current);
INIT_LIST_HEAD(&child->cg_list);
}
//对线程来说(当CLONE_THREAD标志被设置时),子进程的父进程是当前进程的父进程
if (clone_flags & (CLONE_PARENT|CLONE_THREAD)) {
p->real_parent = current->real_parent;
p->parent_exec_id = current->parent_exec_id;
} else {
p->real_parent = current;
p->parent_exec_id = current->self_exec_id;
}
//普通进程的线程组组长是进程本身,但对线程来说(即CLONE_THREAD标志被设置时),其组长是当前进程的组长。
if (clone_flags & CLONE_THREAD) {
current->signal->nr_threads++;
atomic_inc(¤t->signal->live);
atomic_inc(¤t->signal->sigcnt);
p->group_leader = current->group_leader;
list_add_tail_rcu(&p->thread_group, &p->group_leader->thread_group);
}
if (likely(p->pid)) {
tracehook_finish_clone(p, clone_flags, trace);
if (thread_group_leader(p)) { //判断该进程是不是线程组的组长,若是,则须进行以下的操作
//#define thread_group_leader(p) (p == p->group_leader)
if (clone_flags & CLONE_NEWPID)
p->nsproxy->pid_ns->child_reaper = p; //在创建新的PID命名空间时,会让其中的child_reaper字段指向自身
p->signal->leader_pid = pid;
p->signal->tty = tty_kref_get(current->signal->tty);
attach_pid(p, PIDTYPE_PGID, task_pgrp(current)); //获取PIDTYPE_PGID类型的struct pid,并插入到相应的链表中
attach_pid(p, PIDTYPE_SID, task_session(current)); //获取PIDTYPE_SID类型的struct pid,并插入到相应的链表中
list_add_tail(&p->sibling, &p->real_parent->children); //插入到兄弟进程链表中,它们都是同一父进程的子进程
list_add_tail_rcu(&p->tasks, &init_task.tasks); //插入到进程链表中,可通过此链表遍历系统中所有的进程
__this_cpu_inc(process_counts);
}
attach_pid(p, PIDTYPE_PID, pid); //把PIDTYPE_PID类型的struct pid插入到相应的链表中
nr_threads++; //递增进程数量的统计
}
total_forks++; //递增total_forks变量,用以记录被创建的进程的数量
proc_fork_connector(p); //初始化struct proc_event结构的fork字段
cgroup_post_fork(p); //构建cg_list链表
/* linux-2.6.38.8/kernel/cgroup.c */
void cgroup_post_fork(struct task_struct *child)
{
if (use_task_css_set_links) {
write_lock(&css_set_lock);
task_lock(child);
if (list_empty(&child->cg_list))
list_add(&child->cg_list, &child->cgroups->tasks);
task_unlock(child);
write_unlock(&css_set_lock);
}
}
perf_event_fork(p); //初始化struct perf_task_event结构的task字段为p
更多推荐
所有评论(0)