码迷,mamicode.com
首页 > 系统相关 > 详细

Linux进程创建过程

时间:2014-07-19 09:18:15      阅读:412      评论:0      收藏:0      [点我收藏+]

标签:style   blog   http   color   使用   strong   

相关学习资料

linux内核设计与实现+原书第3版.pdf(3.3章)
深入linux内核架构(中文版).pdf
深入理解linux内核中文第三版.pdf
《独辟蹊径品内核Linux内核源代码导读》
http://www.yanyulin.info/pages/2013/11/linux0.html
http://blog.csdn.net/ddna/article/details/4958058
http://www.cnblogs.com/coolgestar02/archive/2010/12/31/1922629.html
http://blog.sina.com.cn/s/blog_4ba5b45e0102e3to.html
http://www.kernel.org/

 

目录

1. Linux/Unix进程创建简介
2. fork()函数
3. exec()函数
4. 查看进程的启动过程工具

 

1. Linux/Unix进程创建简介

0x1: linux和windows在进程创建上的区别

unix/linux的进程创建和Windows有很大不一样

1. windows
windows采用了createProcess()来进行新进程的创建,大致流程如下:
    1) 申请一块全新的内存(包括内核空间和用户空间)
    2) 打开新进程对应的磁盘文件,将文件内容复制到新申请的内存中
    3) 启动主线程从新进程的函数入口点(默认是main)开始顺序执行
在windows的哲学中,每一个新进程都是一个新的、独立的内存空间,进程之间彼此相对独立。
虽然在内核对象中也有父进程和子进程这些字段,但是这只是一个弱关系,windows中的父子进程并没有强制性的依赖关系。
关于windows的进程创建过程,请参阅另一篇文章
http://www.cnblogs.com/LittleHann/p/3458736.html

2. linux/unix
对于linu/unix的操作系统来说,它并不像windows那样采用"产生(spawn)"进程的机制。
而是将创建进程的步骤分解到两个单独的函数中去执行:
    1) fork()
    fork()通过"拷贝"当前进程,创建一个子进程。这个时候的子进程和父进程的区别仅仅在于PID(进程号)、PPID(父进程号)、和某些资源和统计量
    2) exec()
    exec()函数则负责读取可执行文件并将其载入地址空间开始运行
把这两个函数(fork、exec)组合起来的最终效果就等同于windows中的createProcess

0x2: linux中的0号、1号进程

1. 进程0
Linux引导中创建的第一个进程,完成加载系统后,演变为进程调度、交换及存储管理进程(也就是说0号进程自从创建完1号进程后就不会再次去创建其他进程了,之后由1号进程负责新子进程的创建)
Linux中1号进程是由0号进程来创建的,由于在创建进程时,程序一直运行在内核态,而进程运行在用户态,因此创建0号进程涉及到特权级的变化,即从特权级0变到特权级3,Linux是通过模拟中断返回来实现特权级的变化以及创建0号
进程,通过将0号进程的代码段选择子以及程序计数器EIP直接压入内核态堆栈,然后利用iret汇编指令中断返回跳转到0号进程运行。
2. 进程1 init 进程,由0进程创建,完成系统的初始化。是系统中所有其它用户进程的祖先进程。

0x3: exec函数

我们知道,linux使用fork和exec进行新进程的创建,这里的exec并不是指某一个特定的函数。Linux提供了:

#include <unistd.h>
1. int execl(const char *path, const char *arg, ...);
2. int execlp(const char *file, const char *arg, ...);
3. int execle(const char *path, const char *arg, ..., char *const envp[]);
4. int execv(const char *path, char *const argv[]);
5. int execvp(const char *file, char *const argv[]);
6. int execve(const char *path, char *const argv[], char *const envp[]);

这6个都是用以执行一个可执行文件的函数,它们统称为"exec函数",它们的差异在于对命令行参数和环境变量参数的传递方式不同
其中只有execve是真正意义上的系统调用,其它都是在此基础上经过包装的库函数。
以上函数的本质都是调用\arch\x86\kernel\process_32.c文件中实现的系统调用sys_execve()来执行一个可执行文件。

了解了linux中进程创建的基本知识之后,我们接下来可以继续学习一下linux中创建进程涉及到的2个核心函数fork、exec了

 

 

2. fork()函数

Fork的系统调用代码在\linux-2.6.32.63\arch\x86\kernel\process.c中

/*
Sys_fork系统调用通过 do_fork()函数实现,通过对do_fork()函数传递不同的clone_flags来实现:
1. fork
2. clone
3. vfork
*/
int sys_fork(struct pt_regs *regs)
{
    return do_fork(SIGCHLD, regs->sp, regs, 0, NULL, NULL);
}

我们继续跟踪do_fork()的代码

\linux-2.6.32.63\kernel\fork.c

/*
 *  Ok, this is the main fork-routine.
 *
 * It copies the process, and if successful kick-starts
 * it and waits for it to finish using the VM if required.
 */
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)
{
    struct task_struct *p;
    int trace = 0;
    long nr;

    /*
     * Do some preliminary argument and permissions checking before we
     * actually start allocating stuff
     */
    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;
    }

    /*
     * We hope to recycle these flags after 2.6.26
     */
    if (unlikely(clone_flags & CLONE_STOPPED)) {
        static int __read_mostly count = 100;

        if (count > 0 && printk_ratelimit()) {
            char comm[TASK_COMM_LEN];

            count--;
            printk(KERN_INFO "fork(): process `%s‘ used deprecated "
                    "clone flags 0x%lx\n",
                get_task_comm(comm, current),
                clone_flags & CLONE_STOPPED);
        }
    }

    /*
     * When called from kernel_thread, don‘t do user tracing stuff.
     */
    if (likely(user_mode(regs)))
        trace = tracehook_prepare_clone(clone_flags);
    
    /*
    Do_fork()函数的核心是copy_process()函数,该函数完成了进程创建的绝大部分工作
    */
    p = copy_process(clone_flags, stack_start, regs, 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;

        trace_sched_process_fork(current, p);

        nr = task_pid_vnr(p);

        if (clone_flags & CLONE_PARENT_SETTID)
            put_user(nr, parent_tidptr);

        if (clone_flags & CLONE_VFORK) {
            p->vfork_done = &vfork;
            init_completion(&vfork);
        }

        audit_finish_fork(p);
        tracehook_report_clone(regs, clone_flags, nr, p);

        /*
         * We set PF_STARTING at creation in case tracing wants to
         * use this to distinguish a fully live task from one that
         * hasn‘t gotten to tracehook_report_clone() yet.  Now we
         * clear it and set the child going.
         */
        p->flags &= ~PF_STARTING;

        if (unlikely(clone_flags & CLONE_STOPPED)) {
            /*
             * We‘ll start up with an immediate SIGSTOP.
             */
            sigaddset(&p->pending.signal, SIGSTOP);
            set_tsk_thread_flag(p, TIF_SIGPENDING);
            __set_task_state(p, TASK_STOPPED);
        } else {
            wake_up_new_task(p, clone_flags);
        }

        tracehook_report_clone_complete(trace, regs,
                        clone_flags, nr, p);

        if (clone_flags & CLONE_VFORK) {
            freezer_do_not_count();
            wait_for_completion(&vfork);
            freezer_count();
            tracehook_report_vfork_done(p, nr);
        }
    } else {
        nr = PTR_ERR(p);
    }
    return nr;
}

Do_fork()函数的核心是copy_process()函数,该函数完成了进程创建的绝大部分工作
继续跟踪copy_process()
\linux-2.6.32.63\kernel\fork.c

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;

    /*
    1. 对传入的clone_flag进行检查
    */
    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);

    retval = security_task_create(clone_flags);
    if (retval)
        goto fork_out;

    retval = -ENOMEM;
    /*
    2. 调用了dup_task_struct()函数,该函数的主要作用是:为子进程创建一个新的内核栈,复制task_struct结构和thread_info结构,这里只是对结构完整的复制,所以子进程的进程描述符跟父进程完全一样
    */
    p = dup_task_struct(current);
    if (!p)
        goto fork_out;

    ftrace_graph_init_task(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) >=
            p->signal->rlim[RLIMIT_NPROC].rlim_cur) {
        if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE) &&
            p->real_cred->user != INIT_USER)
            goto bad_fork_free;
    }

    retval = copy_creds(p, clone_flags);
    if (retval < 0)
        goto bad_fork_free;
 
    retval = -EAGAIN;
    /*
    3. 检查创建的进程是否超过了系统进程总量
    */
    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;

    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);
    rcu_copy_process(p);
    p->vfork_done = NULL;
    spin_lock_init(&p->alloc_lock);

    init_sigpending(&p->pending);

    /*
    4. 开始对子进程task_struct结构的初始化过程
    */
    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;

    p->default_timer_slack_ns = current->timer_slack_ns;

    task_io_accounting_init(&p->ioac);
    acct_clear_integrals(p);

    posix_cpu_timers_init(p);

    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);
    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

    p->bts = NULL;

    /* Perform scheduler related setup. Assign this task to a CPU. */
    sched_fork(p, clone_flags);

    retval = perf_event_init_task(p);
    if (retval)
        goto bad_fork_cleanup_policy;

    if ((retval = audit_alloc(p)))
        goto bad_fork_cleanup_policy;
    /* 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_namespaces(clone_flags, p)))
        goto bad_fork_cleanup_mm;
    if ((retval = copy_io(clone_flags, p)))
        goto bad_fork_cleanup_namespaces;
    retval = copy_thread(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(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;
        }
    }

    p->pid = pid_nr(pid);
    /*
    5. 如果设置了同在一个线程组则继承TGID。对于普通进程来说TGID和PID相等,对于线程来说,同一线程组内的所有线程的TGID都相等,这使得这些多线程可以通过调用getpid()获得相同的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 

    if ((clone_flags & (CLONE_VM|CLONE_VFORK)) == CLONE_VM)
        p->sas_ss_sp = p->sas_ss_size = 0;
 
    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->exit_signal = (clone_flags & CLONE_THREAD) ? -1 : (clone_flags & CSIGNAL);
    p->pdeath_signal = 0;
    p->exit_state = 0;
 
    p->group_leader = p;
    INIT_LIST_HEAD(&p->thread_group);
 
    cgroup_fork_callbacks(p);
    cgroup_callbacks_done = 1;

    /* 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(&current->sighand->siglock);
 
    recalc_sigpending();
    if (signal_pending(current)) {
        spin_unlock(&current->sighand->siglock);
        write_unlock_irq(&tasklist_lock);
        retval = -ERESTARTNOINTR;
        goto bad_fork_free_pid;
    }

    if (clone_flags & CLONE_THREAD) {
        atomic_inc(&current->signal->count);
        atomic_inc(&current->signal->live);
        p->group_leader = current->group_leader;
        list_add_tail_rcu(&p->thread_group, &p->group_leader->thread_group);
    }

    if (likely(p->pid)) {
        list_add_tail(&p->sibling, &p->real_parent->children);
        tracehook_finish_clone(p, clone_flags, trace);

        if (thread_group_leader(p)) {
            if (clone_flags & CLONE_NEWPID)
                p->nsproxy->pid_ns->child_reaper = p;

            p->signal->leader_pid = pid;
            tty_kref_put(p->signal->tty);
            p->signal->tty = tty_kref_get(current->signal->tty);
            attach_pid(p, PIDTYPE_PGID, task_pgrp(current));
            attach_pid(p, PIDTYPE_SID, task_session(current));
            list_add_tail_rcu(&p->tasks, &init_task.tasks);
            __get_cpu_var(process_counts)++;
        }
        attach_pid(p, PIDTYPE_PID, pid);
        nr_threads++;
    }

    total_forks++;
    spin_unlock(&current->sighand->siglock);
    write_unlock_irq(&tasklist_lock);
    proc_fork_connector(p);
    cgroup_post_fork(p);
    perf_event_fork(p);
    return p;
/*
6. 对task_struct结构的初始化完了就该继续copy其他的资源了,例如:
    1) 文件
    2) 句柄
    3) 内核资源
*/
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))
        __cleanup_signal(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_cgroup:
#endif
    cgroup_exit(p, cgroup_callbacks_done);
    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);
}

继续跟踪dup_task_struct()
\linux-2.6.32.63\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 err;

    prepare_to_copy(orig);

    /*
    1. 通过alloc_task_struct()函数创建内核栈和task_struct结构空间
    */
    tsk = alloc_task_struct();
    if (!tsk)
        return NULL;
    /*
    2. 分配thread_info结构空间
    */
    ti = alloc_thread_info(tsk);
    if (!ti) {
        free_task_struct(tsk);
        return NULL;
    }
    /*
    3. 为整个task_struct结构复制
    */
     err = arch_dup_task_struct(tsk, orig);
    if (err)
        goto out;

    tsk->stack = ti;

    err = prop_local_init_single(&tsk->dirties);
    if (err)
        goto out;
    /*
    4. 调用setup_thread_stack()函数为thread_info结构复制
    */
    setup_thread_stack(tsk, orig);
    stackend = end_of_stack(tsk);
    *stackend = STACK_END_MAGIC;    /* for overflow detection */

#ifdef CONFIG_CC_STACKPROTECTOR
    tsk->stack_canary = get_random_int();
#endif

    /*
    更新该用户的user_struct结构,累加相应的计数器,由atomic_inc()函数完成
    */
    atomic_set(&tsk->usage,2);
    atomic_set(&tsk->fs_excl, 0);
#ifdef CONFIG_BLK_DEV_IO_TRACE
    tsk->btrace_seq = 0;
#endif
    tsk->splice_pipe = NULL;

    account_kernel_stack(ti, 1);

    return tsk;

out:
    free_thread_info(ti);
    free_task_struct(tsk);
    return NULL;
}

 

 

3. exec()函数

我们知道,所有的exec家族的函数最终都是调用了sys_execve()这个系统调用来实现的

\arch\x86\kernel\process_32.c

int sys_execve(struct pt_regs *regs)
{
    int error;
    char *filename;
    /*
    1. 将可执行文件的名称装入到一个新分配的页面中
    */
    filename = getname((char __user *) regs->bx);
    error = PTR_ERR(filename);
    if (IS_ERR(filename))
        goto out;

    /*
    调用do_execve()执行可执行文件
    */
    error = do_execve(filename,
            (char __user * __user *) regs->cx,
            (char __user * __user *) regs->dx,
            regs);
    if (error == 0) {
        /* Make sure we don‘t return using sysenter.. */
        set_thread_flag(TIF_IRET);
    }
    putname(filename);
out:
    return error;
}

继续跟踪do_execve()

linux-2.6.32.63\fs\exec.c

int do_execve(char * filename,
    char __user *__user *argv,
    char __user *__user *envp,
    struct pt_regs * regs)
{
    /*
    1. 保存要执行的文件相关的数据
    */
    struct linux_binprm *bprm;
    struct file *file;
    struct files_struct *displaced;
    bool clear_in_exec;
    int retval;

    retval = unshare_files(&displaced);
    if (retval)
        goto out_ret;

    retval = -ENOMEM;
    bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);
    if (!bprm)
        goto out_files;

    retval = prepare_bprm_creds(bprm);
    if (retval)
        goto out_free;

    retval = check_unsafe_exec(bprm);
    if (retval < 0)
        goto out_free;
    clear_in_exec = retval;
    current->in_execve = 1;
    /*
    2. 打开要执行的文件,并检查其有效性(这里的检查并不完备)
    */
    file = open_exec(filename);
    retval = PTR_ERR(file);
    if (IS_ERR(file))
        goto out_unmark;
    /*
    3. 在多处理器系统中才执行,用以分配负载最低的CPU来执行新程序
    */
    sched_exec();
    /*
    4. 填充linux_binprm结构
    */
    bprm->file = file;
    bprm->filename = filename;
    bprm->interp = filename;

    retval = bprm_mm_init(bprm);
    if (retval)
        goto out_file;

    bprm->argc = count(argv, MAX_ARG_STRINGS);
    if ((retval = bprm->argc) < 0)
        goto out;

    bprm->envc = count(envp, MAX_ARG_STRINGS);
    if ((retval = bprm->envc) < 0)
        goto out;
    
    /*
    5. 检查文件是否可以被执行,填充linux_binprm结构中的e_uid和e_gid项
    */
    retval = prepare_binprm(bprm);
    if (retval < 0)
        goto out;

    retval = copy_strings_kernel(1, &bprm->filename, bprm);
    if (retval < 0)
        goto out;

    bprm->exec = bprm->p;
    retval = copy_strings(bprm->envc, envp, bprm);
    if (retval < 0)
        goto out;
    /*
    6. 将文件名、环境变量和命令行参数拷贝到新分配的页面中
    */
    retval = copy_strings(bprm->argc, argv, bprm);
    if (retval < 0)
        goto out;

    current->flags &= ~PF_KTHREAD;
    /*
    7. 查询能够处理该可执行文件格式的处理函数,并调用相应的load_library方法进行处理
    */
    retval = search_binary_handler(bprm,regs);
    if (retval < 0)
        goto out;

    /* execve succeeded */
    current->fs->in_exec = 0;
    current->in_execve = 0;
    acct_update_integrals(current);
    /*
    8. 执行成功
    */
    free_bprm(bprm);
    if (displaced)
        put_files_struct(displaced);
    return retval;

out:
    if (bprm->mm) {
        acct_arg_size(bprm, 0);
        mmput(bprm->mm);
    }

out_file:
    if (bprm->file) {
        allow_write_access(bprm->file);
        fput(bprm->file);
    }

out_unmark:
    if (clear_in_exec)
        current->fs->in_exec = 0;
    current->in_execve = 0;

out_free:
    free_bprm(bprm);

out_files:
    if (displaced)
        reset_files_struct(displaced);
out_ret:
    return retval;
}

 

 

4. 查看进程的启动过程工具

要想查看进程的启动过程,可以使用两个工具: strace和LD_DEBUG

source:

#include <stdlib.h>  
#include <stdio.h>  
 
int main()
{  
    printf("hello world\n");  
    return 0;  
} 

编译程序:

gcc -o hello -O2 hello.c 

strace -tt ./hello 

05:47:11.645477 execve("./hello", ["./hello"], [/* 38 vars */]) = 0
05:47:11.646521 brk(0)                  = 0x82f8000
05:47:11.646660 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb77fd000
05:47:11.646745 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
05:47:11.646929 open("/etc/ld.so.cache", O_RDONLY) = 3
05:47:11.647012 fstat64(3, {st_mode=S_IFREG|0644, st_size=50450, ...}) = 0
05:47:11.647176 mmap2(NULL, 50450, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb77f0000
05:47:11.647223 close(3)                = 0
05:47:11.647348 open("/lib/libc.so.6", O_RDONLY) = 3
05:47:11.647409 read(3, "\177ELF\1\1\1\3\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0@\356X\0004\0\0\0"..., 512) = 512
05:47:11.647496 fstat64(3, {st_mode=S_IFREG|0755, st_size=1906124, ...}) = 0
05:47:11.647605 mmap2(0x578000, 1665416, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x578000
05:47:11.647648 mprotect(0x708000, 4096, PROT_NONE) = 0
05:47:11.647693 mmap2(0x709000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x190) = 0x709000
05:47:11.647761 mmap2(0x70c000, 10632, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x70c000
05:47:11.647819 close(3)                = 0
05:47:11.648707 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb77ef000
05:47:11.648797 set_thread_area({entry_number:-1 -> 6, base_addr:0xb77ef6c0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, 
useable:1}) = 0 05:47:11.649201 mprotect(0x709000, 8192, PROT_READ) = 0 05:47:11.649272 mprotect(0x570000, 4096, PROT_READ) = 0 05:47:11.649326 munmap(0xb77f0000, 50450) = 0 05:47:11.649560 fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0 05:47:11.649678 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb77fc000 05:47:11.649754 write(1, "hello world\n", 12hello world ) = 12 05:47:11.649829 exit_group(0) = ?

LD_DEBUG=libs ./hello 

     26605:    find library=libc.so.6 [0]; searching
     26605:     search cache=/etc/ld.so.cache
     26605:      trying file=/lib/libc.so.6
     26605:    
     26605:    
     26605:    calling init: /lib/libc.so.6
     26605:    
     26605:    
     26605:    initialize program: ./hello
     26605:    
     26605:    
     26605:    transferring control: ./hello
     26605:    
hello world
     26605:    
     26605:    calling fini: ./hello [0]
     26605:    
     26605:    
     26605:    calling fini: /lib/libc.so.6 [0]
     26605:    

 

Copyright (c) 2014 LittleHann All rights reserved

Linux进程创建过程,布布扣,bubuko.com

Linux进程创建过程

标签:style   blog   http   color   使用   strong   

原文地址:http://www.cnblogs.com/LittleHann/p/3853854.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!