首页 > 代码库 > Linux进程创建过程
Linux进程创建过程
相关学习资料
linux内核设计与实现+原书第3版.pdf(3.3章)深入linux内核架构(中文版).pdf深入理解linux内核中文第三版.pdf《独辟蹊径品内核Linux内核源代码导读》http://www.yanyulin.info/pages/2013/11/linux0.htmlhttp://blog.csdn.net/ddna/article/details/4958058http://www.cnblogs.com/coolgestar02/archive/2010/12/31/1922629.htmlhttp://blog.sina.com.cn/s/blog_4ba5b45e0102e3to.htmlhttp://www.kernel.org/
目录
1. Linux/Unix进程创建简介2. fork()函数3. exec()函数4. 查看进程的启动过程工具
1. Linux/Unix进程创建简介
0x1: linux和windows在进程创建上的区别
unix/linux的进程创建和Windows有很大不一样
1. windowswindows采用了createProcess()来进行新进程的创建,大致流程如下: 1) 申请一块全新的内存(包括内核空间和用户空间) 2) 打开新进程对应的磁盘文件,将文件内容复制到新申请的内存中 3) 启动主线程从新进程的函数入口点(默认是main)开始顺序执行在windows的哲学中,每一个新进程都是一个新的、独立的内存空间,进程之间彼此相对独立。虽然在内核对象中也有父进程和子进程这些字段,但是这只是一个弱关系,windows中的父子进程并没有强制性的依赖关系。关于windows的进程创建过程,请参阅另一篇文章http://www.cnblogs.com/LittleHann/p/3458736.html2. linux/unix对于linu/unix的操作系统来说,它并不像windows那样采用"产生(spawn)"进程的机制。而是将创建进程的步骤分解到两个单独的函数中去执行: 1) fork() fork()通过"拷贝"当前进程,创建一个子进程。这个时候的子进程和父进程的区别仅仅在于PID(进程号)、PPID(父进程号)、和某些资源和统计量 2) exec() exec()函数则负责读取可执行文件并将其载入地址空间开始运行把这两个函数(fork、exec)组合起来的最终效果就等同于windows中的createProcess
0x2: linux中的0号、1号进程
1. 进程0Linux引导中创建的第一个进程,完成加载系统后,演变为进程调度、交换及存储管理进程(也就是说0号进程自从创建完1号进程后就不会再次去创建其他进程了,之后由1号进程负责新子进程的创建)Linux中1号进程是由0号进程来创建的,由于在创建进程时,程序一直运行在内核态,而进程运行在用户态,因此创建0号进程涉及到特权级的变化,即从特权级0变到特权级3,Linux是通过模拟中断返回来实现特权级的变化以及创建0号
进程,通过将0号进程的代码段选择子以及程序计数器EIP直接压入内核态堆栈,然后利用iret汇编指令中断返回跳转到0号进程运行。2. 进程1init 进程,由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. fork2. clone3. 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(¤t->sighand->siglock); recalc_sigpending(); if (signal_pending(current)) { spin_unlock(¤t->sighand->siglock); write_unlock_irq(&tasklist_lock); retval = -ERESTARTNOINTR; goto bad_fork_free_pid; } if (clone_flags & CLONE_THREAD) { atomic_inc(¤t->signal->count); atomic_inc(¤t->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(¤t->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 */]) = 005:47:11.646521 brk(0) = 0x82f800005:47:11.646660 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb77fd00005: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) = 305:47:11.647012 fstat64(3, {st_mode=S_IFREG|0644, st_size=50450, ...}) = 005:47:11.647176 mmap2(NULL, 50450, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb77f000005:47:11.647223 close(3) = 005:47:11.647348 open("/lib/libc.so.6", O_RDONLY) = 305: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) = 51205:47:11.647496 fstat64(3, {st_mode=S_IFREG|0755, st_size=1906124, ...}) = 005:47:11.647605 mmap2(0x578000, 1665416, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x57800005:47:11.647648 mprotect(0x708000, 4096, PROT_NONE) = 005:47:11.647693 mmap2(0x709000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x190) = 0x70900005:47:11.647761 mmap2(0x70c000, 10632, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x70c00005:47:11.647819 close(3) = 005:47:11.648707 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb77ef00005: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}) = 005:47:11.649201 mprotect(0x709000, 8192, PROT_READ) = 005:47:11.649272 mprotect(0x570000, 4096, PROT_READ) = 005:47:11.649326 munmap(0xb77f0000, 50450) = 005:47:11.649560 fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 005:47:11.649678 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb77fc00005:47:11.649754 write(1, "hello world\n", 12hello world) = 1205: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