linux进程创造 - 创建进程API及过程

1. 创建进程函数API


1.1 创建进程fork()

fork的翻译为“叉子,分叉”,其实在unix编程中,我们来创建进程的时候是深有体会的,感觉创建一个进程就像是走到了一个岔路口,父进程和子进程在叉路口分道扬镳,所以我想这就是前辈为什么要用fork来表示创建一个进程的缘由吧。

通过fork(),除了task_struct和堆栈,子进程和父进程共享所有的资源,相当于复制了一个父进程,但是由于linux采用了写时复制技术,复制工作不是立即就执行,提高了效率。在unix编程中,调用fork实际上相当于创建了一个进程。

(kernel/fork.c)

SYSCALL_DEFINE0(fork)
{
#ifdef CONFIG_MMU
    return _do_fork(SIGCHLD, 0, 0, NULL, NULL, 0);
#else
    /* can not support in nommu mode */
    return -EINVAL;
#endif
}

1.2 创建进程vfork()

vfork类似于fork,但并不创建父进程数据的副本,相反,父子进程之间共享数据,这节省了大量CPU时间。

vfork设计用于子进程形成后立即执行execve加载新程序的情形。在子进程退出或开始新程序之前,内核保证父进程处于阻塞状态。但是由于fork使用了写时复制技术,vfork速度方面不再有优势,因此应该避免使用它。

(kernel/fork.c)

SYSCALL_DEFINE0(vfork)
{
    return _do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, 0,
            0, NULL, NULL, 0);
}

1.3 创建线程clone()

clone翻译为“克隆”,从字面理解就是照着父进程的样子重新生成一个子进程,但是子进程是一个新的个体,和父进程已经少了许多关系。

clone产生线程,可以对父子进程之间的共享、复制进行精确控制。

(kernel/fork.c)

#ifdef CONFIG_CLONE_BACKWARDS
SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp,
         int __user *, parent_tidptr,
         unsigned long, tls,
         int __user *, child_tidptr)
#elif defined(CONFIG_CLONE_BACKWARDS2)
SYSCALL_DEFINE5(clone, unsigned long, newsp, unsigned long, clone_flags,
         int __user *, parent_tidptr,
         int __user *, child_tidptr,
         unsigned long, tls)
#elif defined(CONFIG_CLONE_BACKWARDS3)
SYSCALL_DEFINE6(clone, unsigned long, clone_flags, unsigned long, newsp,
        int, stack_size,
        int __user *, parent_tidptr,
        int __user *, child_tidptr,
        unsigned long, tls)
#else
SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp,
         int __user *, parent_tidptr,
         int __user *, child_tidptr,
         unsigned long, tls)
#endif
{
    return _do_fork(clone_flags, newsp, 0, parent_tidptr, child_tidptr, tls);
}

1.4 进程创建部分标志的含义

flags description
CLONE_VM 共享内存描述符和所有的页表
CLONE_FS 共享根目录和当前工作目录所在的表
CLONE_FILES 共享打开文件表
CLONE_SIGHAND 共享信号处理程序的表、阻塞信号表和挂起信号表
CLONE_VFORK vfork使用的标志
CLONE_PARENT 设置子进程的父进程为调用进程的父进程
CLONE_THREAD 创建线程使用的标志
CLONE_PARENT_SETTID 把子进程的PID写入有ptid参数所指向的父进程的用户态变量
CLONE_CHILD_SETTID 把子进程的PID写入有ctid参数所指向的子进程的用户态变量
CLONE_NEWNS 当clone需要自己的命名空间时设置这个标志

2. _do_fork


上述创建进程的API最后调用的函数都是_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,unsigned long tls)
{
    struct task_struct *p;
    int trace = 0;
    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 = 0;
    }

    p = copy_process(clone_flags, stack_start, stack_size,----------------复制父进程的一些数据
             child_tidptr, NULL, trace, tls);
    /*
     * 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) {-----------------------------------如果有标志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;
}

3. copy_process


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,
                    unsigned long tls)

copy_process的流程图如下所示,其中代码细节部分会在文章系列的子部分详细说明:

linux进程创造 - 创建进程API及过程_第1张图片

change log


date content linux kernel
2016.11.20 原始写作 4.6.3

你可能感兴趣的:(Linux内核,linux内核,linux进程,linux线程,unix环境编程)