fork.c

来自「Kernel code of linux kernel」· C语言 代码 · 共 1,689 行 · 第 1/3 页

C
1,689
字号
	if (!mm)		goto fail_nomem;	memcpy(mm, oldmm, sizeof(*mm));	/* Initializing for Swap token stuff */	mm->token_priority = 0;	mm->last_interval = 0;	if (!mm_init(mm, tsk))		goto fail_nomem;	if (init_new_context(tsk, mm))		goto fail_nocontext;	dup_mm_exe_file(oldmm, mm);	err = dup_mmap(mm, oldmm);	if (err)		goto free_pt;	mm->hiwater_rss = get_mm_rss(mm);	mm->hiwater_vm = mm->total_vm;	return mm;free_pt:	mmput(mm);fail_nomem:	return NULL;fail_nocontext:	/*	 * If init_new_context() failed, we cannot use mmput() to free the mm	 * because it calls destroy_context()	 */	mm_free_pgd(mm);	free_mm(mm);	return NULL;}static int copy_mm(unsigned long clone_flags, struct task_struct * tsk){	struct mm_struct * mm, *oldmm;	int retval;	tsk->min_flt = tsk->maj_flt = 0;	tsk->nvcsw = tsk->nivcsw = 0;	tsk->mm = NULL;	tsk->active_mm = NULL;	/*	 * Are we cloning a kernel thread?	 *	 * We need to steal a active VM for that..	 */	oldmm = current->mm;	if (!oldmm)		return 0;	if (clone_flags & CLONE_VM) {		atomic_inc(&oldmm->mm_users);		mm = oldmm;		goto good_mm;	}	retval = -ENOMEM;	mm = dup_mm(tsk);	if (!mm)		goto fail_nomem;good_mm:	/* Initializing for Swap token stuff */	mm->token_priority = 0;	mm->last_interval = 0;	tsk->mm = mm;	tsk->active_mm = mm;	return 0;fail_nomem:	return retval;}static struct fs_struct *__copy_fs_struct(struct fs_struct *old){	struct fs_struct *fs = kmem_cache_alloc(fs_cachep, GFP_KERNEL);	/* We don't need to lock fs - think why ;-) */	if (fs) {		atomic_set(&fs->count, 1);		rwlock_init(&fs->lock);		fs->umask = old->umask;		read_lock(&old->lock);		fs->root = old->root;		path_get(&old->root);		fs->pwd = old->pwd;		path_get(&old->pwd);		read_unlock(&old->lock);	}	return fs;}struct fs_struct *copy_fs_struct(struct fs_struct *old){	return __copy_fs_struct(old);}EXPORT_SYMBOL_GPL(copy_fs_struct);static int copy_fs(unsigned long clone_flags, struct task_struct *tsk){	if (clone_flags & CLONE_FS) {		atomic_inc(&current->fs->count);		return 0;	}	tsk->fs = __copy_fs_struct(current->fs);	if (!tsk->fs)		return -ENOMEM;	return 0;}static int copy_files(unsigned long clone_flags, struct task_struct * tsk){	struct files_struct *oldf, *newf;	int error = 0;	/*	 * A background process may not have any files ...	 */	oldf = current->files;	if (!oldf)		goto out;	if (clone_flags & CLONE_FILES) {		atomic_inc(&oldf->count);		goto out;	}	newf = dup_fd(oldf, &error);	if (!newf)		goto out;	tsk->files = newf;	error = 0;out:	return error;}static int copy_io(unsigned long clone_flags, struct task_struct *tsk){#ifdef CONFIG_BLOCK	struct io_context *ioc = current->io_context;	if (!ioc)		return 0;	/*	 * Share io context with parent, if CLONE_IO is set	 */	if (clone_flags & CLONE_IO) {		tsk->io_context = ioc_task_link(ioc);		if (unlikely(!tsk->io_context))			return -ENOMEM;	} else if (ioprio_valid(ioc->ioprio)) {		tsk->io_context = alloc_io_context(GFP_KERNEL, -1);		if (unlikely(!tsk->io_context))			return -ENOMEM;		tsk->io_context->ioprio = ioc->ioprio;	}#endif	return 0;}static int copy_sighand(unsigned long clone_flags, struct task_struct *tsk){	struct sighand_struct *sig;	if (clone_flags & (CLONE_SIGHAND | CLONE_THREAD)) {		atomic_inc(&current->sighand->count);		return 0;	}	sig = kmem_cache_alloc(sighand_cachep, GFP_KERNEL);	rcu_assign_pointer(tsk->sighand, sig);	if (!sig)		return -ENOMEM;	atomic_set(&sig->count, 1);	memcpy(sig->action, current->sighand->action, sizeof(sig->action));	return 0;}void __cleanup_sighand(struct sighand_struct *sighand){	if (atomic_dec_and_test(&sighand->count))		kmem_cache_free(sighand_cachep, sighand);}static int copy_signal(unsigned long clone_flags, struct task_struct *tsk){	struct signal_struct *sig;	int ret;	if (clone_flags & CLONE_THREAD) {		atomic_inc(&current->signal->count);		atomic_inc(&current->signal->live);		return 0;	}	sig = kmem_cache_alloc(signal_cachep, GFP_KERNEL);	tsk->signal = sig;	if (!sig)		return -ENOMEM;	ret = copy_thread_group_keys(tsk);	if (ret < 0) {		kmem_cache_free(signal_cachep, sig);		return ret;	}	atomic_set(&sig->count, 1);	atomic_set(&sig->live, 1);	init_waitqueue_head(&sig->wait_chldexit);	sig->flags = 0;	sig->group_exit_code = 0;	sig->group_exit_task = NULL;	sig->group_stop_count = 0;	sig->curr_target = tsk;	init_sigpending(&sig->shared_pending);	INIT_LIST_HEAD(&sig->posix_timers);	hrtimer_init(&sig->real_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);	sig->it_real_incr.tv64 = 0;	sig->real_timer.function = it_real_fn;	sig->it_virt_expires = cputime_zero;	sig->it_virt_incr = cputime_zero;	sig->it_prof_expires = cputime_zero;	sig->it_prof_incr = cputime_zero;	sig->leader = 0;	/* session leadership doesn't inherit */	sig->tty_old_pgrp = NULL;	sig->utime = sig->stime = sig->cutime = sig->cstime = cputime_zero;	sig->gtime = cputime_zero;	sig->cgtime = cputime_zero;	sig->nvcsw = sig->nivcsw = sig->cnvcsw = sig->cnivcsw = 0;	sig->min_flt = sig->maj_flt = sig->cmin_flt = sig->cmaj_flt = 0;	sig->inblock = sig->oublock = sig->cinblock = sig->coublock = 0;	task_io_accounting_init(&sig->ioac);	sig->sum_sched_runtime = 0;	INIT_LIST_HEAD(&sig->cpu_timers[0]);	INIT_LIST_HEAD(&sig->cpu_timers[1]);	INIT_LIST_HEAD(&sig->cpu_timers[2]);	taskstats_tgid_init(sig);	task_lock(current->group_leader);	memcpy(sig->rlim, current->signal->rlim, sizeof sig->rlim);	task_unlock(current->group_leader);	if (sig->rlim[RLIMIT_CPU].rlim_cur != RLIM_INFINITY) {		/*		 * New sole thread in the process gets an expiry time		 * of the whole CPU time limit.		 */		tsk->it_prof_expires =			secs_to_cputime(sig->rlim[RLIMIT_CPU].rlim_cur);	}	acct_init_pacct(&sig->pacct);	tty_audit_fork(sig);	return 0;}void __cleanup_signal(struct signal_struct *sig){	exit_thread_group_keys(sig);	kmem_cache_free(signal_cachep, sig);}static void cleanup_signal(struct task_struct *tsk){	struct signal_struct *sig = tsk->signal;	atomic_dec(&sig->live);	if (atomic_dec_and_test(&sig->count))		__cleanup_signal(sig);}static 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;	new_flags |= PF_STARTING;	p->flags = new_flags;	clear_freeze_flag(p);}asmlinkage long sys_set_tid_address(int __user *tidptr){	current->clear_child_tid = tidptr;	return task_pid_vnr(current);}static void rt_mutex_init_task(struct task_struct *p){	spin_lock_init(&p->pi_lock);#ifdef CONFIG_RT_MUTEXES	plist_head_init(&p->pi_waiters, &p->pi_lock);	p->pi_blocked_on = NULL;#endif}#ifdef CONFIG_MM_OWNERvoid mm_init_owner(struct mm_struct *mm, struct task_struct *p){	mm->owner = p;}#endif /* CONFIG_MM_OWNER *//* * This creates a new process as a copy of the old one, * but does not actually start it yet. * * It copies the registers, and all the appropriate * parts of the process environment (as per the clone * flags). The actual kick-off is left to the caller. */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){	int retval;	struct task_struct *p;	int cgroup_callbacks_done = 0;	if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS))		return ERR_PTR(-EINVAL);	/*	 * Thread groups must share signals as well, and detached threads	 * can only be started up within the thread group.	 */	if ((clone_flags & CLONE_THREAD) && !(clone_flags & CLONE_SIGHAND))		return ERR_PTR(-EINVAL);	/*	 * Shared signal handlers imply shared VM. By way of the above,	 * thread groups also imply shared VM. Blocking this case allows	 * for various simplifications in other code.	 */	if ((clone_flags & CLONE_SIGHAND) && !(clone_flags & CLONE_VM))		return ERR_PTR(-EINVAL);	retval = security_task_create(clone_flags);	if (retval)		goto fork_out;	retval = -ENOMEM;	p = dup_task_struct(current);	if (!p)		goto fork_out;	rt_mutex_init_task(p);#ifdef CONFIG_PROVE_LOCKING	DEBUG_LOCKS_WARN_ON(!p->hardirqs_enabled);	DEBUG_LOCKS_WARN_ON(!p->softirqs_enabled);#endif	retval = -EAGAIN;	if (atomic_read(&p->user->processes) >=			p->signal->rlim[RLIMIT_NPROC].rlim_cur) {		if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE) &&		    p->user != current->nsproxy->user_ns->root_user)			goto bad_fork_free;	}	atomic_inc(&p->user->__count);	atomic_inc(&p->user->processes);	get_group_info(p->group_info);	/*	 * If multiple threads are within copy_process(), then this check	 * triggers too late. This doesn't hurt, the check is only there	 * to stop root fork bombs.	 */	if (nr_threads >= max_threads)		goto bad_fork_cleanup_count;	if (!try_module_get(task_thread_info(p)->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;	delayacct_tsk_init(p);	/* Must remain after dup_task_struct() */	copy_flags(clone_flags, p);	INIT_LIST_HEAD(&p->children);	INIT_LIST_HEAD(&p->sibling);#ifdef CONFIG_PREEMPT_RCU	p->rcu_read_lock_nesting = 0;	p->rcu_flipctr_idx = 0;#endif /* #ifdef CONFIG_PREEMPT_RCU */	p->vfork_done = NULL;	spin_lock_init(&p->alloc_lock);	clear_tsk_thread_flag(p, TIF_SIGPENDING);	init_sigpending(&p->pending);	p->utime = cputime_zero;	p->stime = cputime_zero;	p->gtime = cputime_zero;	p->utimescaled = cputime_zero;	p->stimescaled = cputime_zero;	p->prev_utime = cputime_zero;	p->prev_stime = cputime_zero;#ifdef CONFIG_DETECT_SOFTLOCKUP	p->last_switch_count = 0;	p->last_switch_timestamp = 0;#endif	task_io_accounting_init(&p->ioac);	acct_clear_integrals(p);	p->it_virt_expires = cputime_zero;	p->it_prof_expires = cputime_zero;	p->it_sched_expires = 0;	INIT_LIST_HEAD(&p->cpu_timers[0]);	INIT_LIST_HEAD(&p->cpu_timers[1]);	INIT_LIST_HEAD(&p->cpu_timers[2]);	p->lock_depth = -1;		/* -1 = no lock */	do_posix_clock_monotonic_gettime(&p->start_time);	p->real_start_time = p->start_time;	monotonic_to_bootbased(&p->real_start_time);#ifdef CONFIG_SECURITY	p->security = NULL;#endif	p->cap_bset = current->cap_bset;	p->io_context = NULL;	p->audit_context = NULL;	cgroup_fork(p);#ifdef CONFIG_NUMA	p->mempolicy = mpol_dup(p->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);#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	/* Perform scheduler related setup. Assign this task to a CPU. */	sched_fork(p, clone_flags);	if ((retval = security_task_alloc(p)))		goto bad_fork_cleanup_policy;	if ((retval = audit_alloc(p)))		goto bad_fork_cleanup_security;	/* 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_namespaces(clone_flags, p)))		goto bad_fork_cleanup_keys;	if ((retval = copy_io(clone_flags, p)))		goto bad_fork_cleanup_namespaces;	retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs);	if (retval)		goto bad_fork_cleanup_io;	if (pid != &init_struct_pid) {		retval = -ENOMEM;		pid = alloc_pid(task_active_pid_ns(p));		if (!pid)			goto bad_fork_cleanup_io;		if (clone_flags & CLONE_NEWPID) {			retval = pid_ns_prepare_proc(task_active_pid_ns(p));			if (retval < 0)				goto bad_fork_free_pid;		}	}	p->pid = pid_nr(pid);	p->tgid = p->pid;	if (clone_flags & CLONE_THREAD)		p->tgid = current->tgid;	if (current->nsproxy != p->nsproxy) {		retval = ns_cgroup_clone(p, pid);		if (retval)			goto bad_fork_free_pid;	}	p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? child_tidptr : NULL;	/*	 * 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;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?