您好,欢迎访问三七文档
当前位置:首页 > 商业/管理/HR > 企业文化 > 关于linux系统如何实现fork的研究(二)
引言前一篇关于linux系统如何实现fork的研究(一)通过代码已经说明了从用户态怎么通过软中断实现调用系统调用clone函数,而clone函数的精华copy_process函数就在此篇文章中进行分析。我们知道,在linux系统中,应用层可以创建子进程和子线程(轻量级进程)两种程序分支结构。而对于linux内核而且,并不详细区分子进程和子线程(轻量级进程)的区别,他们都使用的是task_struct结构(此结构极其复杂,包含非常多的数据结构),而不同的是子进程和子线程的task_struct初始化结果不同。task_struct结构是一个进程或线程的标识和存在的凭证,调度程序就是通过task_struct结构来区分不同的进程(线程)。里面包含了进程(线程)所有需要用到的结构(内存描述符,文件描述符,信号描述符,信号处理函数,调度优先级等)。而我们知道,一个进程(线程)不止有自己的task_struck结构,还必须有一个自己的内核栈,当执行进程切换时,部分进程上下文会保存于其进程的内核栈中,而中断发生时的中断上下文也会保存于正在持续的进程内核栈中。在copy_process函数中内核栈的初始化导致了fork()的两次返回值不同(之后会说明)。当然,copy_process还涉及到许多操作,比如新进程(线程)的安全检测,pid的分配,关系调整(父子进程、进程组关系,命名空间关系等),内存结构的初始化等等,这些我们在之后的代码中慢慢道来。copy_process1./*代码目录:linux源码/kernel/Fork.c*/2.3.staticstructtask_struct*copy_process(unsignedlongclone_flags,4.unsignedlongstack_start,5.unsignedlongstack_size,6.int__user*child_tidptr,7.structpid*pid,8.inttrace)9.{10.intretval;11.structtask_struct*p;12.13./*CLONE_FS不能与CLONE_NEWNS或CLONE_NEWUSER同时设置*/14.if((clone_flags&(CLONE_NEWNS|CLONE_FS))==(CLONE_NEWNS|CLONE_FS))15.returnERR_PTR(-EINVAL);16.17.if((clone_flags&(CLONE_NEWUSER|CLONE_FS))==(CLONE_NEWUSER|CLONE_FS))18.returnERR_PTR(-EINVAL);19.20./*创建线程时线程之间要共享信号处理函数*/21.if((clone_flags&CLONE_THREAD)&&!(clone_flags&CLONE_SIGHAND))22.returnERR_PTR(-EINVAL);23.24./*25.*父子进程共享信号处理函数时必须共享内存地址空间26.*这就是为什么书上写的fork出来的父子进程有其独立的信号处理函数,因为他们的内存地址空间不同27.*/28.if((clone_flags&CLONE_SIGHAND)&&!(clone_flags&CLONE_VM))29.returnERR_PTR(-EINVAL);30.31./*32.*防止参数init进程的兄弟进程33.*只有init进程的signal-flags&SIGNAL_UNKILLABLE为真34.*因为当进程退出时实际上是成为了僵尸进程(zombie),而要通过init进程将它回收,而如果此进程为init的兄弟进程,则没办法将其回收35.*/36.if((clone_flags&CLONE_PARENT)&&37.current-signal-flags&SIGNAL_UNKILLABLE)38.returnERR_PTR(-EINVAL);39.40./*如果新的进程将会有新的用户空间或者pid,则不能让它共享父进程的线程组或者信号处理或者父进程*/41.if(clone_flags&CLONE_SIGHAND){42.if((clone_flags&(CLONE_NEWUSER|CLONE_NEWPID))||43.(task_active_pid_ns(current)!=44.current-nsproxy-pid_ns_for_children))45.returnERR_PTR(-EINVAL);46.}47.48./*附加安全检查*/49.retval=security_task_create(clone_flags);50.if(retval)51.gotofork_out;52.53.retval=-ENOMEM;54./*为新进程分配structtask_struct内存和内核栈内存*/55.p=dup_task_struct(current);56.if(!p)57.gotofork_out;58.59./*ftrace是用于内核性能分析和跟踪的*/60.ftrace_graph_init_task(p);61.62./*futex初始化,其用于SYSTEMVIPC,具体可见*/63.rt_mutex_init_task(p);64.65.#ifdefCONFIG_PROVE_LOCKING66.DEBUG_LOCKS_WARN_ON(!p-hardirqs_enabled);67.DEBUG_LOCKS_WARN_ON(!p-softirqs_enabled);68.#endif69.retval=-EAGAIN;70./*检查tsk-signal-rlim[RLIMIT_NPROC].rlim_cur是否小于等于用户所拥有的进程数,rlim结构体表示相关资源的最大值*/71.if(atomic_read(&p-real_cred-user-processes)=task_rlimit(p,RLIMIT_NPROC)){72./*INIT_USER是root权限。检查父进程是否有root权限*/73.if(p-real_cred-user!=INIT_USER&&!capable(CAP_SYS_RESOURCE)&&!capable(CAP_SYS_ADMIN))74.gotobad_fork_free;75.}76.current-flags&=~PF_NPROC_EXCEEDED;77.78./*将父进程的cred复制到子进程的real_cred和cred。structcred用于安全操作的结构*/79.retval=copy_creds(p,clone_flags);80.if(retval0)81.gotobad_fork_free;82.83.retval=-EAGAIN;84./*进程数量是否超出系统允许最大进程数量,最大进程数量跟内存有关,一般原则是所有的进程内核栈(默认8K)加起来不超过总内存的1/8,可通过/proc/sys/kernel/threads-max改写此值*/85.if(nr_threads=max_threads)86.gotobad_fork_cleanup_count;87.88./*如果实现新进程的执行域和可执行格式的内核函数都包含在内核模块中,则递增其使用计数*/89.if(!try_module_get(task_thread_info(p)-exec_domain-module))90.gotobad_fork_cleanup_count;91.92.delayacct_tsk_init(p);/*Mustremainafterdup_task_struct()*/93.94./*清除PF_SUPERPRIV(表示进程使用了超级用户权限)和PF_WQ_WORKER(使用了工作队列)*/95.p-flags&=~(PF_SUPERPRIV|PF_WQ_WORKER);96./*设置PF_FORKNOEXEC表明此子进程还没有进行execve()系统调用*/97.p-flags|=PF_FORKNOEXEC;98.99./*初始化子进程的子进程链表和兄弟进程链表为空*/100.INIT_LIST_HEAD(&p-children);101.INIT_LIST_HEAD(&p-sibling);102./*见*/103.rcu_copy_process(p);104.p-vfork_done=NULL;105./*初始化分配锁,此锁用于保护分配内存,文件,文件系统等操作*/106.spin_lock_init(&p-alloc_lock);107.108./*信号列表初始化,此列表保存被挂起的信号*/109.init_sigpending(&p-pending);110.111./*代码执行时间变量都置为0*/112.p-utime=p-stime=p-gtime=0;113.p-utimescaled=p-stimescaled=0;114.#ifndefCONFIG_VIRT_CPU_ACCOUNTING_NATIVE115.p-prev_cputime.utime=p-prev_cputime.stime=0;116.#endif117.#ifdefCONFIG_VIRT_CPU_ACCOUNTING_GEN118.seqlock_init(&p-vtime_seqlock);119.p-vtime_snap=0;120.p-vtime_snap_whence=VTIME_SLEEPING;121.#endif122.123.#ifdefined(SPLIT_RSS_COUNTING)124.memset(&p-rss_stat,0,sizeof(p-rss_stat));125.#endif126./*此变量一般用于epoll和select,从父进程复制过来*/127.p-default_timer_slack_ns=current-timer_slack_ns;128.129./*初始化进程IO计数结构*/130.task_io_accounting_init(&p-ioac);131.acct_clear_integrals(p);132.133./*初始化cputime_expires结构*/134.posix_cpu_timers_init(p);135.136./*设置进程创建时间*/137.p-start_time=ktime_get_ns();138.p-real_start_time=ktime_get_boot_ns();139.140./*io_context和audit_context置空*/141.p-io_context=NULL;142.p-audit_context=NULL;143./*如果创建的是线程,因为需要修改到当前进程的描述符,会先上锁*/144.if(clone_flags&CLONE_THREAD)145.threadgroup_change_begin(current);146.cgroup_fork(p);147.#ifdefCONFIG_NUMA148.p-mempolicy=mpol_dup(p-mempolicy);149.if(IS_ERR(p-mempolicy)){150.retval=PTR_ERR(p-mempolicy);151.p-mempolicy=NULL;152.gotobad_fork_cleanup_threadgroup_lock;1
本文标题:关于linux系统如何实现fork的研究(二)
链接地址:https://www.777doc.com/doc-3138583 .html