fork之后发生了什么(基于3.16-rc4)
在linux c编程中,我们可以使用fork,vfork,clone三个系统调用来创建子进程。下面我们先分析下fork系统调用的实现原理。代码如下(kernel/fork.c):
- #ifdef __ARCH_WANT_SYS_FORK
- SYSCALL_DEFINE0(fork)
- {
- #ifdef CONFIG_MMU
- return do_fork(SIGCHLD, , , NULL, NULL);
- #else
- /* can not support in nommu mode */
- return -EINVAL;
- #endif
- }
- #endif
看见了么,linux中系统调用就是这样定义的。可以看见fork系统调用是do_fork函数的一个封装,它会通过软中断(系统门)的形式跳到内核函数do_fork中。下面我们来分析下do_fork函数(kernel/fork.c):
- long do_fork(unsigned long clone_flags,
- unsigned long stack_start,
- unsigned long stack_size,
- int __user *parent_tidptr,
- int __user *child_tidptr)
- {
- struct task_struct *p;
- int trace = ;
- long nr;
- /*
- * Determine whether and which event to report to ptracer. When
- * called from kernel_thread or CLONE_UNTRACED is explicitly
- * requested, no event is reported; otherwise, report if the event
- * for the type of forking is enabled.
- */
- if (!(clone_flags & CLONE_UNTRACED)) {
- if (clone_flags & CLONE_VFORK)
- trace = PTRACE_EVENT_VFORK;
- else if ((clone_flags & CSIGNAL) != SIGCHLD)
- trace = PTRACE_EVENT_CLONE;
- else
- trace = PTRACE_EVENT_FORK;
- if (likely(!ptrace_event_enabled(current, trace)))
- trace = ;
- }
- p = copy_process(clone_flags, stack_start, stack_size,
- child_tidptr, NULL, trace);
- /*
- * Do this prior waking up the new thread - the thread pointer
- * might get invalid after that point, if the thread exits quickly.
- */
- if (!IS_ERR(p)) {
- struct completion vfork;
- struct pid *pid;
- trace_sched_process_fork(current, p);
- pid = get_task_pid(p, PIDTYPE_PID);
- nr = pid_vnr(pid);
- if (clone_flags & CLONE_PARENT_SETTID)
- put_user(nr, parent_tidptr);
- if (clone_flags & CLONE_VFORK) {
- p->vfork_done = &vfork;
- init_completion(&vfork);
- get_task_struct(p);
- }
- wake_up_new_task(p);
- /* forking complete and child started to run, tell ptracer */
- if (unlikely(trace))
- ptrace_event_pid(trace, pid);
- if (clone_flags & CLONE_VFORK) {
- if (!wait_for_vfork_done(p, &vfork))
- ptrace_event_pid(PTRACE_EVENT_VFORK_DONE, pid);
- }
- put_pid(pid);
- } else {
- nr = PTR_ERR(p);
- }
- return nr;
- }
第17-27行对函数接收到的标志进行判断,如果设置了追踪标志,则依据创建进程所使用的系统调用,在trace变量中放入不同的类型值。第29行创建子进程的描述符以及其他的数据结构,返回值为子进程的描述符指针,保存在变量p中,该函数可以说最核心的函数,待会来分析它。第35行,如果p指针没有出错,则进入if体。第41行获取子进程的pid号。第42行更新子进程的pid命名空间,并获得进程的pid号,存放在nr变量中。第44-45行,如果设置了CLONE_PARENT_SETTID标志,则将子进程的命名空间保存到父进程的一个用户态变量中,该变量的指针就是传进来的参数parent_tidptr。该函数中大部分是跟子进程追踪有关,我们暂且不分析这部分,主要分析下copy_process函数,代码如下(kernel/fork.c):
- static struct task_struct *copy_process(unsigned long clone_flags,
- unsigned long stack_start,
- unsigned long stack_size,
- int __user *child_tidptr,
- struct pid *pid,
- int trace)
- {
- int retval;
- struct task_struct *p;
- if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS))
- return ERR_PTR(-EINVAL);
- if ((clone_flags & (CLONE_NEWUSER|CLONE_FS)) == (CLONE_NEWUSER|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);
- /*
- * Siblings of global init remain as zombies on exit since they are
- * not reaped by their parent (swapper). To solve this and to avoid
- * multi-rooted process trees, prevent global and container-inits
- * from creating siblings.
- */
- if ((clone_flags & CLONE_PARENT) &&
- current->signal->flags & SIGNAL_UNKILLABLE)
- return ERR_PTR(-EINVAL);
- /*
- * If the new process will be in a different pid or user namespace
- * do not allow it to share a thread group or signal handlers or
- * parent with the forking task.
- */
- if (clone_flags & CLONE_SIGHAND) {
- if ((clone_flags & (CLONE_NEWUSER | CLONE_NEWPID)) ||
- (task_active_pid_ns(current) !=
- current->nsproxy->pid_ns_for_children))
- 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;
- ftrace_graph_init_task(p);
- get_seccomp_filter(p);
- 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->real_cred->user->processes) >=
- task_rlimit(p, RLIMIT_NPROC)) {
- if (p->real_cred->user != INIT_USER &&
- !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN))
- goto bad_fork_free;
- }
- current->flags &= ~PF_NPROC_EXCEEDED;
- retval = copy_creds(p, clone_flags);
- if (retval < )
- goto bad_fork_free;
- /*
- * 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.
- */
- retval = -EAGAIN;
- 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;
- delayacct_tsk_init(p); /* Must remain after dup_task_struct() */
- p->flags &= ~(PF_SUPERPRIV | PF_WQ_WORKER);
- p->flags |= PF_FORKNOEXEC;
- INIT_LIST_HEAD(&p->children);
- INIT_LIST_HEAD(&p->sibling);
- rcu_copy_process(p);
- p->vfork_done = NULL;
- spin_lock_init(&p->alloc_lock);
- init_sigpending(&p->pending);
- p->utime = p->stime = p->gtime = ;
- p->utimescaled = p->stimescaled = ;
- #ifndef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
- p->prev_cputime.utime = p->prev_cputime.stime = ;
- #endif
- #ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN
- seqlock_init(&p->vtime_seqlock);
- p->vtime_snap = ;
- p->vtime_snap_whence = VTIME_SLEEPING;
- #endif
- #if defined(SPLIT_RSS_COUNTING)
- memset(&p->rss_stat, , sizeof(p->rss_stat));
- #endif
- p->default_timer_slack_ns = current->timer_slack_ns;
- task_io_accounting_init(&p->ioac);
- acct_clear_integrals(p);
- posix_cpu_timers_init(p);
- 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;
- if (clone_flags & CLONE_THREAD)
- threadgroup_change_begin(current);
- 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_threadgroup_lock;
- }
- #endif
- #ifdef CONFIG_CPUSETS
- p->cpuset_mem_spread_rotor = NUMA_NO_NODE;
- p->cpuset_slab_spread_rotor = NUMA_NO_NODE;
- seqcount_init(&p->mems_allowed_seq);
- #endif
- #ifdef CONFIG_TRACE_IRQFLAGS
- p->irq_events = ;
- p->hardirqs_enabled = ;
- p->hardirq_enable_ip = ;
- p->hardirq_enable_event = ;
- p->hardirq_disable_ip = _THIS_IP_;
- p->hardirq_disable_event = ;
- p->softirqs_enabled = ;
- p->softirq_enable_ip = _THIS_IP_;
- p->softirq_enable_event = ;
- p->softirq_disable_ip = ;
- p->softirq_disable_event = ;
- p->hardirq_context = ;
- p->softirq_context = ;
- #endif
- #ifdef CONFIG_LOCKDEP
- p->lockdep_depth = ; /* no locks held yet */
- p->curr_chain_key = ;
- p->lockdep_recursion = ;
- #endif
- #ifdef CONFIG_DEBUG_MUTEXES
- p->blocked_on = NULL; /* not blocked yet */
- #endif
- #ifdef CONFIG_MEMCG
- p->memcg_batch.do_batch = ;
- p->memcg_batch.memcg = NULL;
- #endif
- #ifdef CONFIG_BCACHE
- p->sequential_io = ;
- p->sequential_io_avg = ;
- #endif
- /* Perform scheduler related setup. Assign this task to a CPU. */
- retval = sched_fork(clone_flags, p);
- if (retval)
- goto bad_fork_cleanup_policy;
- retval = perf_event_init_task(p);
- if (retval)
- goto bad_fork_cleanup_policy;
- retval = audit_alloc(p);
- if (retval)
- goto bad_fork_cleanup_policy;
- /* copy all the process information */
- retval = copy_semundo(clone_flags, p);
- if (retval)
- goto bad_fork_cleanup_audit;
- retval = copy_files(clone_flags, p);
- if (retval)
- goto bad_fork_cleanup_semundo;
- retval = copy_fs(clone_flags, p);
- if (retval)
- goto bad_fork_cleanup_files;
- retval = copy_sighand(clone_flags, p);
- if (retval)
- goto bad_fork_cleanup_fs;
- retval = copy_signal(clone_flags, p);
- if (retval)
- goto bad_fork_cleanup_sighand;
- retval = copy_mm(clone_flags, p);
- if (retval)
- goto bad_fork_cleanup_signal;
- retval = copy_namespaces(clone_flags, p);
- if (retval)
- goto bad_fork_cleanup_mm;
- retval = copy_io(clone_flags, p);
- if (retval)
- goto bad_fork_cleanup_namespaces;
- retval = copy_thread(clone_flags, stack_start, stack_size, p);
- if (retval)
- goto bad_fork_cleanup_io;
- if (pid != &init_struct_pid) {
- retval = -ENOMEM;
- pid = alloc_pid(p->nsproxy->pid_ns_for_children);
- if (!pid)
- goto bad_fork_cleanup_io;
- }
- 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_BLOCK
- p->plug = NULL;
- #endif
- #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 = ;
- /*
- * Syscall tracing and stepping should be turned off in the
- * child regardless of CLONE_PTRACE.
- */
- user_disable_single_step(p);
- clear_tsk_thread_flag(p, TIF_SYSCALL_TRACE);
- #ifdef TIF_SYSCALL_EMU
- clear_tsk_thread_flag(p, TIF_SYSCALL_EMU);
- #endif
- clear_all_latency_tracing(p);
- /* ok, now we should be set up.. */
- p->pid = pid_nr(pid);
- if (clone_flags & CLONE_THREAD) {
- p->exit_signal = -;
- p->group_leader = current->group_leader;
- p->tgid = current->tgid;
- } else {
- if (clone_flags & CLONE_PARENT)
- p->exit_signal = current->group_leader->exit_signal;
- else
- p->exit_signal = (clone_flags & CSIGNAL);
- p->group_leader = p;
- p->tgid = p->pid;
- }
- p->nr_dirtied = ;
- p->nr_dirtied_pause = >> (PAGE_SHIFT - );
- p->dirty_paused_when = ;
- p->pdeath_signal = ;
- INIT_LIST_HEAD(&p->thread_group);
- p->task_works = NULL;
- /*
- * Make it visible to the rest of the system, but dont wake it up yet.
- * Need tasklist lock for parent etc handling!
- */
- write_lock_irq(&tasklist_lock);
- /* CLONE_PARENT re-uses the old parent */
- 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;
- }
- spin_lock(¤t->sighand->siglock);
- /*
- * Process group and session signals need to be delivered to just the
- * parent before the fork or both the parent and the child after the
- * fork. Restart if a signal comes in before we add the new process to
- * it's process group.
- * A fatal signal pending means that current will exit, so the new
- * thread can't slip out of an OOM kill (or normal SIGKILL).
- */
- recalc_sigpending();
- if (signal_pending(current)) {
- spin_unlock(¤t->sighand->siglock);
- write_unlock_irq(&tasklist_lock);
- retval = -ERESTARTNOINTR;
- goto bad_fork_free_pid;
- }
- if (likely(p->pid)) {
- ptrace_init_task(p, (clone_flags & CLONE_PTRACE) || trace);
- init_task_pid(p, PIDTYPE_PID, pid);
- if (thread_group_leader(p)) {
- init_task_pid(p, PIDTYPE_PGID, task_pgrp(current));
- init_task_pid(p, PIDTYPE_SID, task_session(current));
- if (is_child_reaper(pid)) {
- ns_of_pid(pid)->child_reaper = p;
- p->signal->flags |= SIGNAL_UNKILLABLE;
- }
- p->signal->leader_pid = pid;
- p->signal->tty = tty_kref_get(current->signal->tty);
- list_add_tail(&p->sibling, &p->real_parent->children);
- list_add_tail_rcu(&p->tasks, &init_task.tasks);
- attach_pid(p, PIDTYPE_PGID);
- attach_pid(p, PIDTYPE_SID);
- __this_cpu_inc(process_counts);
- } else {
- current->signal->nr_threads++;
- atomic_inc(¤t->signal->live);
- atomic_inc(¤t->signal->sigcnt);
- list_add_tail_rcu(&p->thread_group,
- &p->group_leader->thread_group);
- list_add_tail_rcu(&p->thread_node,
- &p->signal->thread_head);
- }
- attach_pid(p, PIDTYPE_PID);
- nr_threads++;
- }
- total_forks++;
- spin_unlock(¤t->sighand->siglock);
- syscall_tracepoint_update(p);
- write_unlock_irq(&tasklist_lock);
- proc_fork_connector(p);
- cgroup_post_fork(p);
- if (clone_flags & CLONE_THREAD)
- threadgroup_change_end(current);
- perf_event_fork(p);
- trace_task_newtask(p, clone_flags);
- uprobe_copy_process(p, clone_flags);
- return p;
- bad_fork_free_pid:
- if (pid != &init_struct_pid)
- free_pid(pid);
- bad_fork_cleanup_io:
- if (p->io_context)
- exit_io_context(p);
- bad_fork_cleanup_namespaces:
- exit_task_namespaces(p);
- bad_fork_cleanup_mm:
- if (p->mm)
- mmput(p->mm);
- bad_fork_cleanup_signal:
- if (!(clone_flags & CLONE_THREAD))
- free_signal_struct(p->signal);
- bad_fork_cleanup_sighand:
- __cleanup_sighand(p->sighand);
- 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_policy:
- perf_event_free_task(p);
- #ifdef CONFIG_NUMA
- mpol_put(p->mempolicy);
- bad_fork_cleanup_threadgroup_lock:
- #endif
- if (clone_flags & CLONE_THREAD)
- threadgroup_change_end(current);
- delayacct_tsk_free(p);
- module_put(task_thread_info(p)->exec_domain->module);
- bad_fork_cleanup_count:
- atomic_dec(&p->cred->user->processes);
- exit_creds(p);
- bad_fork_free:
- free_task(p);
- fork_out:
- return ERR_PTR(retval);
- }
copy_process
第11-52行对传递进来的clone_flags标志进行一致性检查,如果有错误,返回错误代码。第54行对clone_flags进行附加性安全检查。第59行为子进程创建进程描述符。第63行,这个版本的linux内核源码中该函数为空函数,不研究它。第64行增加子进程p的seccomp.filter结构体成员的引用数量。第66行初始化子进程中的互斥锁。第73-77行比较子进程的拥有者所拥有的进程数量是否超过限制,如果超过了限制并且该拥有者不是root用户,就会跳到错误处理代码。第81行将父进程的拥有者信息拷贝给子进程。第91行检查系统中的进程数量是否超过了限制,nr_threads是内核中的全局变量,存放着进程的总数。第94行检查子进程的执行域(要执行的代码)所在的模块是否已经加载进了内核,如果没有的话就进行出错处理。第98-99行对子进程flags进程初始化。第100-101对子进程的孩子链表和兄弟链表进行初始化。第102行初始化子进程的rcu(一种锁机制)。第104行初始化子进程的alloc_lock自旋锁。第106行初始化进程的挂起信号集。第108-116对进行的各种时间进程初始化。第125行初始化子进程的io计数。第126行对子进程的多个计数域进行清零。第128行初始化子进程的定时器。第185-222行,使用了多个copy_***函数创建了子进程要用的各种数据结构,并将父进程的相应结构体中的内容拷贝进来,或者根据flags指定的内容来初始化子进程的这些数据结构。第224-228行,如果传进来的pid指针和全局结构体变量init_struct_pid的地址不相同的话,就要为子进程分配pid结构体。第265行从pid结构体中获取到子进程的pid号,保存到子进程描述符的pid域。第266-269行,如果设置了CLONE_THREAD标志,说明子进程和父进程在同一个线程组,那么子进程将继承父进程的tgid,否则第275-276行,子进程的组领导者就是它自己,组号tgpid是它自己的pid号。第279-281行设置一些和进程脏页限制有关的成员。下面291-356行的代码需要关掉中断并且获取写锁。第294-296行对进程亲子关系初始化,如果设置了CLONE_PARENT和CLONE_THREAD标志,子进程的真实父进程设置为它的父进程的真实父进程(它的爷爷进程),否则第298-299行,子进程的真实父进程设置为它的父进程。第302行-354行需要获取自旋锁。第321行初始化子进程的ptraced字段(和进程追踪有关)。第323行,对子进程的pid结构体进行初始化。第324-340行,如果子进程是线程组的组长,则对子进程进行相应处理,其中,第325-326行将子进程的进程组和会话组的组长分别设置为父进程的组领导的进程组和会话组组长,第337-338行,将子进程加入它所在组的哈希链表中。否则,子进程不是线程组组长的话,第341-347行不对子进程做pid相关的操作。第349行,同样要将子进程加入到pid哈希表中。第350行全局变量nr_threads自加1,说明系统中的进程又多了一个。如果整个函数内部没有出错的话,第367行返回创建好的子进程描述符指针p。第369行到结束,均为错误处理的代码,如果函数中有出错的地方,就会跳到这些代码中。
下面我们接着分析第59行的dup_task_struct函数,代码如下(kernel/fork.c):
- static struct task_struct *dup_task_struct(struct task_struct *orig)
- {
- struct task_struct *tsk;
- struct thread_info *ti;
- unsigned long *stackend;
- int node = tsk_fork_get_node(orig);
- int err;
- tsk = alloc_task_struct_node(node);
- if (!tsk)
- return NULL;
- ti = alloc_thread_info_node(tsk, node);
- if (!ti)
- goto free_tsk;
- err = arch_dup_task_struct(tsk, orig);
- if (err)
- goto free_ti;
- tsk->stack = ti;
- setup_thread_stack(tsk, orig);
- clear_user_return_notifier(tsk);
- clear_tsk_need_resched(tsk);
- stackend = end_of_stack(tsk);
- *stackend = STACK_END_MAGIC; /* for overflow detection */
- #ifdef CONFIG_CC_STACKPROTECTOR
- tsk->stack_canary = get_random_int();
- #endif
- /*
- * One for us, one for whoever does the "release_task()" (usually
- * parent)
- */
- atomic_set(&tsk->usage, );
- #ifdef CONFIG_BLK_DEV_IO_TRACE
- tsk->btrace_seq = ;
- #endif
- tsk->splice_pipe = NULL;
- tsk->task_frag.page = NULL;
- account_kernel_stack(ti, );
- return tsk;
- free_ti:
- free_thread_info(ti);
- free_tsk:
- free_task_struct(tsk);
- return NULL;
- }
在该函数中创建了子进程的进程描述符,并将描述符指针返回。第6行,如果使用到了NUMA技术的话,函数返回父进程描述符的pref_node_fork字段值,否则,返回-1。第9行为子进程创建进程描述符,将描述符指针保存在tsk变量中。第13行为子进程创建thread_info结构体(也就是创建内核栈),并返回结构体指针(该指针实际上也是子进程堆栈的内核栈指针)。第17行将父进程描述符的各个成员值全部复制给子进程描述符的成员。第21行将子进程内核栈地址ti赋给stack成员。第23行对子进程的thread_info结构体进行初始化,将父进程的该结构体成员值复制给子进程,然后将子进程的描述符指针存入thread_info结构体的task域(从thread_info就可找到进程描述符了)。第25行清除子进程thread_info结构体中的flag标志(具体而言,就是need_resched标志,取消内核抢占)。第26行将自进程内核栈顶指针保存到stackend变量中。第27行向子进程内核堆栈中存入STACK_END_MAGIC,来进行堆栈溢出检测。第23行将子进程描述符使用计数置为2(一个表示父进程的引用,一个表示自己)。第44行将子进程的内核栈所在的zone结构体使用计数置为1。第46行返回子进程描述符指针。接下来,我们回到do_fork函数中,第53行wake_up_new_task函数会将刚创建好的子进程加入到运行队列中并且唤醒它,下面看看该函数(kernel/sched/core.c)。
- void wake_up_new_task(struct task_struct *p)
- {
- unsigned long flags;
- struct rq *rq;
- raw_spin_lock_irqsave(&p->pi_lock, flags);
- #ifdef CONFIG_SMP
- /*
- * Fork balancing, do it here and not earlier because:
- * - cpus_allowed can change in the fork path
- * - any previously selected cpu might disappear through hotplug
- */
- set_task_cpu(p, select_task_rq(p, task_cpu(p), SD_BALANCE_FORK, ));
- #endif
- /* Initialize new task's runnable average */
- init_task_runnable_average(p);
- rq = __task_rq_lock(p);
- activate_task(rq, p, );
- p->on_rq = ;
- trace_sched_wakeup_new(p, true);
- check_preempt_curr(rq, p, WF_FORK);
- #ifdef CONFIG_SMP
- if (p->sched_class->task_woken)
- p->sched_class->task_woken(rq, p);
- #endif
- task_rq_unlock(rq, p, &flags);
- }
该函数暂且放到这里,带我把进程调度这块的博文完成之后,再完善这里。该函数执行完后,第68行父进程返回子进程的pid号,回到do_fork中,子进程创建的工作就完成了。此后父子进程公平的参与进程调度。
fork之后发生了什么(基于3.16-rc4)的更多相关文章
- 执行 $Gulp 时发生了什么 —— 基于 Gulp 的前端集成解决方案(二)
前言 文章 在windows下安装gulp —— 基于 Gulp 的前端集成解决方案(一) 中,已经完成对 gulp 的安装,由于是window环境,文中特意提到了可以通过安装 gitbash 来代替 ...
- 做自己的docker镜像(基于ubuntu:16.04)
基于ubuntu:16.04 apt-get update -y apt-get install sudo -y 换源 sudo apt-get install vim sudo vim /etc/a ...
- fork开源代码后如何基于某个tag建立自己的branch
应用场景: 在github上fork一个自己想看的开源项目,想基于某个tag来写一些测试demo,然后可以做到版本控制. 方法: //克隆 git clone xxxxx.git //查看tag gi ...
- mount过程分析之一(基于3.16.3内核)【转】
转自:https://blog.csdn.net/zr_lang/article/details/39963253 一直想写有些关于文件系统的博文,但是由于近一年来实在太忙,所以没有时间写.前几日赶上 ...
- EJBCA的安装(基于Ubuntu 16.04 LTS + wildfly8 + ejbca6.3.11 + jdk7)
前一段时间折腾了一下PKI,用EJBCA在研究院内网搭建了一个CA,目前是提供给手机端(安卓和IOS)来和服务器端(nginx + Java应用)做安全连接的(客户端和服务器端双向认证) 由于EJBC ...
- Linux Process/Thread Creation、Linux Process Principle、sys_fork、sys_execve、glibc fork/execve api sourcecode
相关学习资料 linux内核设计与实现+原书第3版.pdf(.3章) 深入linux内核架构(中文版).pdf 深入理解linux内核中文第三版.pdf <独辟蹊径品内核Linux内核源代码导读 ...
- 当 Redis 发生高延迟时,到底发生了什么
Redis 是一种内存数据库,将数据保存在内存中,读写效率要比传统的将数据保存在磁盘上的数据库要快很多.但是 Redis 也会发生延迟时,这是就需要我们对其产生原因有深刻的了解,以便于快速排查问题,解 ...
- 数据备份与恢复 半持久化 全持久化 fork aof rdb Backing up Disaster recovery 备份 容灾
Redis数据备份与恢复 - 流年晕开时光 - 博客园 https://www.cnblogs.com/deny/p/11531355.html Redis数据备份与恢复 Redis所有数据都是保存在 ...
- 构建一个基本的前端自动化开发环境 —— 基于 Gulp 的前端集成解决方案(四)
通过前面几节的准备工作,对于 npm / node / gulp 应该已经有了基本的认识,本节主要介绍如何构建一个基本的前端自动化开发环境. 下面将逐步构建一个可以自动编译 sass 文件.压缩 ja ...
随机推荐
- Checked&Unchecked Exception
Java 中定义了两类异常: 1) Checked exception: 这类异常都是Exception的子类 .异常的向上抛出机制进行处理,如果子类可能产生A异常,那么在父类中也必须throws A ...
- Ubuntu安装Apache
在虚拟机上安装了Ubuntu13.10 ,然后使用命令 sudo apt-get install apache2 安装apache总提示“E: 未找到软件包...”,不知所踪,这可能是新手容易的犯 的 ...
- Android权限安全(7)binder,service,zygote安全相关简介
binder 提供服务的service中的binder thread 检查调用者的uid 不是root,system就异常. service 也检查调用者的uid 不是root,system,只能注册 ...
- AndroidApplication Fundamentals(Android应用基础)
AndroidApplication Fundamentals(Android应用基础) Android应用采用Java编程语言来编写,AndroidSDK工具编译我们的代码,连同任何数据和资源文件一 ...
- Sublime Text3中文乱码及tabs中文方块的解决方案
一.文本出现中文乱码问题 方案1 1.打开Sublime Text 3,按Ctrl+-打开控制行,复制粘贴以下python代码,然后回车运行. 2. 复制并粘贴如下代码: import urllib. ...
- [IE编程] 多页面基于IE内核浏览器的代码示例
有不少人发信问这个问题,我把答案贴在这里: 建议参考 WTL (Windows Template Library) 的代码示例工程TabBrowser (在WTL目录/Samples/TabBrow ...
- mybatis源码分析(1)——SqlSessionFactory实例的产生过程
在使用mybatis框架时,第一步就需要产生SqlSessionFactory类的实例(相当于是产生连接池),通过调用SqlSessionFactoryBuilder类的实例的build方法来完成.下 ...
- 解析CSS加密技术之“障眼法”
CSS(Cascading Style Sheet,可译为“层叠样式表”或“级联样式表”)是一组格式设置规则,用于控制Web页面的外观.通过使用CSS样式设置页面的格式,可将页面的内容与表现形式分离. ...
- 【C#学习笔记】Dictionary容器使用
using System; using System.Collections.Generic; namespace ConsoleApplication { class Program { stati ...
- poj 2063 Investment
题意:给定一个初始资金capital,然后给定d种投资方案,每种投资方案中有投资额value[i](是1000的倍数)和利息interest[i],每年的投资就可以拿到全部利息,然后累加起来继续投资利 ...