Chinaunix首页 | 论坛 | 博客
  • 博客访问: 492110
  • 博文数量: 72
  • 博客积分: 1851
  • 博客等级: 上尉
  • 技术积分: 1464
  • 用 户 组: 普通用户
  • 注册时间: 2010-09-16 17:50
文章分类

全部博文(72)

文章存档

2013年(1)

2012年(17)

2011年(51)

2010年(3)

分类: LINUX

2011-07-24 22:57:05

copy_process函数的执行

具体执行流程如下:

1.检查相关的标志组合是否合法。

  1. //共享文件命名空间又新创建命名空间,返回错误
  2. if((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|    CLONE_FS))
  3.     /*
  4.         static inline void *ERR_PTR(long error)
  5.         {
  6.             return (void*)error
  7.         }
  8.     **/
  9.         return ERR_PTR(-EINVAL);
  10. //线程组必须共享信号量
  11. if((clone_flags & CLONE_THREAD) && !(clone_flags & CLONE_SIGHAND))
  12.         return ERR_PTR(-EINVAL);
  13. //共享虚拟内存必须共享信号量
  14. if((clone_flags & CLONE_SIGHAND) && !(clone_flags & CLONE_VM))
  15.         return ERR_PTR(-EINVAL);

2.创建线程

  1. p = dup_task_struct(current).
  2.      if(!p) goto fork_out;
  3. dup_task_struct函数定义如下:
  4. static struct task_struct *dup_task_struct(struct task_struct *orig)
  5. {
  6.     struct task_struct *tsk;
  7.     struct thread_info *ti;
  8.     int err;
  9.     
  10.     prepare_to_copy(orig);
  11.     /*内核中的所有内存动态分配都是由slab分配器负责*/
  12.     tsk = alloc_task_struct();
  13.     if(!task) return NULL;
  14.     /* 这里的分配有些特别
  15.     * #define alloc_thread_info(task) ((struct thread_info *)    \
  16.     *    __get_free_pages(GFP_KERNEL,get_order(THREAD_SIZE)))
  17.     */
  18.     ti = alloc_thread_info();
  19.     if(!ti) {
  20.         free_task_struct(tsk);
  21.         return NULL;
  22.     }
  23.     *tsk = *orig;//所有的数据结构都一样
  24.     tsk->stack = ti;//只有内核态栈不相同
  25.     //初始化数据结构struct prop_local_single,事件计数器,具体不祥?
  26.     err = prop_local_init_single(&tsk->dirties);
  27.     if(!err) {
  28.         free_thread_info(ti);
  29.         free_task_struct(tsk);
  30.         return NULL;
  31.     }
  32. //将内核栈也进行赋值,分配内存之后,开始进行赋值,这样的话,除了内核栈一样之外,其它的都相同
  33. /*    #define task_thread_info(task) ((struct thread_info *)(task) ->stack)
  34. *    *task_thread_info(tsk) = *task_thread_info(orig);//怎么栈又赋值到原来的去了?
  35. *    task_thread_info(tsk)->task = *tsk;
  36. */
  37.     setup_thread_stack(tsk,orig);
  38.     atomic_set(&tsk->usage,2);
  39.     atomic_set(&tsk->fs_excl,0);
  40.     tsk->splice_pipe = NULL;
  41.     return tsk;
  42. }

上面主要工作是分配空间,进行用户栈,内核栈的复制工作,进程复制中最主要的区别是内核栈task_struct->stack,通常它存放在thread_union结构中,这里面保存着与cpu相关的所有信息.

  1. union thread_info {
  2.     struct thread_info thread_info;
  3.     //这里通过配置可以是4KB或者8KB,这样内核栈可以为4KB或8KB,它与thread_info保存在一起,这样thread_info就在内核栈中,同时位于栈顶。
  4.     unsigned long stack[THREAD_SIZE/sizeof(long)];
  5. };

其中thread_info为内核栈,定义如下:

  1. struct thread_info {
  2.     struct task_struct     *task;//该thread所属的task
  3.     struct exec_domain    *exec_domain;
  4.     unsigned long         flags;
  5.     unsigned long         status;
  6.     __u32             cpu;
  7.     int                 preempt_count;
  8.     mm_segment_t         addr_limit;
  9.     void                 *sysenter_return;
  10.     struct restart_block    restart_block;//实现的信号机制
  11.     unsigned long         previous_step;
  12.     __u8                supervisor_stack[0];//为了动态扩展
  13. }

flags:能够保存进程相关的各种标记,其中有:

TIF_SIGPENDING:如果当前有悬挂的信号

TIF_NEED_RESCHED:该进程是否被调度

更多的设置在文件中。

cpu:当前进程正在执行的cpu号。

preempt_count:当前进程被抢占的次数。

addr_limit:进程可能使用的那一段虚拟地址空间,用户态线程有地址限制,但内核线程可以访问所有的虚拟地址空间。

通过栈顶指针来获取当前进程信息:

  1. register unsigned long current_stack_pointer    ams(“esp”)    __attributed_used__;
  2. static inline struct thread_info *current_thread_info()
  3. {
  4.     return (struct thread_info*)(current_stack_pointer & ~THREAD_SIZE-1))
  5. }

当前进程也是通过该结构获取的。

2.检查资源限制

  1. //检查用户进程个数
  2. if(atomic_read(&p->user->processes) >= p->signal->rlim[RLIMIT_NPROC].rlim_cur) {
  3.     if(!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE) &&
  4.     p->user != current->nsproxy->user_ns->root_user)
  5.         goto bad_fork_free;
  6. }
  7.     atomic_inc(&p->user->__count);
  8.     atomic_inc(&p->user->processes);
  9.     get_group_info(p->group_info);
  10.     //检查线程个数
  11.     if(nr_threads >= max_threads)
  12.         goto bad_fork_cleanup_count;
  13.     //检查是否有执行模块
  14.     if(!try_module_get(task_thread_info(p) ->exec_domain->module))
  15.         goto bad_fork_cleanup_count;
  16.     if(p->binfmt && !try_module_get(p->binfm->module))
  17.         goto bad_fork_cleanup_put_domain;

3.初始化数据结构

  1. p->did_exec = 0;
  2.     //分配并初始化task中的delay数据结构
  3.     delayacct_tsk_init(p);
  4.     //赋值到p->flags中
  5.     copy_flags(clone_flags,p);
  6.     INIT_LIST_HEAD(&p->children);
  7.     INIT_LIST_HEAD(&p->sibling);
  8.     p->vfork_done = NULL;
  9.     spin_lock_init(&p->alloc_lock);
  10.     clear_tsk_thread_flag(p,TIF_SIGPENDING);
  11.     ….......

4.调度程序

  1. sched_fork(p,clone_flags);
  2. 该函数原型如下:
  3.     void sched_fork(struct task_struct *p,int clone_flags)
  4.     {
  5.         int cpu = get_cpu();
  6.         /*__sched_fork(struct task_struct *p)    
  7.         p->se.exec_start         = 0;
  8.         p->se.sum_exec_runtime    = 0;
  9.         p->se.prev_sum_exec_runtime = 0;
  10.         INIT_LIST_HEAD(&p->run_list);
  11.         p->state = TASK_RUNNING;
  12.         */
  13.         __sched_fork(p);
  14.         set_task_cpu(p,cpu);
  15.         if(!rt_prio(p->prio))//如果不是实时进程就使用公平调度
  16.             p->sched_class = &fair_sched_class;
  17.         put_cpu();
  18.     }

5.copy_parent_information

 上面在分配空间时,只是一个简单的赋值,下面才真正进行赋值操作

  1. //复制undo信号
  2. if((retval = copy_semundo(clone_flags,p)))
  3.     goto bad_fork_cleanup_semudo;
  4. //复制file_table表
  5. if((retval = copy_files(clone_flags,p)))
  6.     goto bad_fork_cleanup_semudo;
  7. //复制struct fs_struct结构
  8. if((retval = copy_fs(clone_flags,p)))
  9.     goto bad_fork_cleanup_files;
  10. //复制信号处理函数
  11. if((retval = copy_sighand(clone_flags,p)))
  12.     goto bad_fork_cleanup_sighand;
  13. //复制signal_struct结构
  14. if((retval=copy_signal(clone_flags,p)))
  15.     goto bad_fork_cleanup_fs;
  16. //复制内存空间
  17. if((retval = copy_mm(clone_flags,p)))
  18.     goto bad_fork_cleanup_sighand;
  19. //复制认证信息
  20. if((retval=copy_keys(clone_flags,p)))
  21.     goto bad_fork_cleanup_mm;
  22. //复制命名空间
  23. if((retval=copy_namespaces(clone_flags,p)))
  24.     goto bad_fork_cleanup_keys;
  25. //复制线程栈数据
  26. retval = copy_thread(0,clone_flags,stack_start,stack_size,p,regs);
  27.     if(retval)
  28.         goto bad_fork_cleanup_namespaces;

这些复制函数都有一个标志参数clone_flags,因为如果是CLONE_ABC,则标明在父子进程之间共享,否则就新创建一个数据结构。

6.setID,parent information

  1. //从全局命名空间中获取pid
  2. p->pid = pid_nr(pid);
  3. p->tgid = p->pid;
  4. if(clone_flags & CLONE_THREAD)
  5.     p->tgid = current->tgid;//主进程是进程组id
  6. ....
  7. if(clone_flags & (CLONE_PARENT|CLONE_THREAD))
  8.     //如果创建的是线程,当前进程的父进程才是该线程的双亲
  9.     p->real_parent = current->real_parent;
  10. else    p->real_parent = current;//否则就是当前进程了
  11. p->parent = p->real_parent;
  12. if(clone_flags & CLONE_THREAD)
  13. {
  14.     //创建的是线程的话,线程组id就是当前线程的组id,同时加入列表中
  15.     p->group_leader = current->group_leader;
  16.     list_add_tail_rcu(&p->thread_group,&p->group_leader->thread_group);
  17.     …......
  18. }
  19.     //将该进程加入父进程的链表中
  20.     add_parent(p);
  21.     //p==p->group_leader,是否是进程组领导者
  22.     if(thread_group_leader(p)) {
  23.         //命名空间中的,新命名空间中的init角色
  24.         if(clone_flags & CLONE_NEWPID)
  25.         p->nsproxy->pid_ns->child_reaper = p;
  26.         //继承父进程的信息
  27.         p->signal->tty = current->signal->tty;
  28.         set_task_pgrp(p,task_pgrp_nr(current));
  29.         set_task_session(p,task_session_nr(current));
  30.         attach_pid(p,PIDTYPE_PGID,task_pgrp(current));
  31.         attach_pid(p,PIDTYPE_SID,task_session(current));
  32.         //放入全局任务队列
  33.         list_add_tail_rcu(&p->tasks,&init_task.tasks);
  34.         //cpu局部进程数目自增
  35.         __get_cpu_var(process_counts)++;
  36.     }
  37.         attach_pid(p,PIDTYPE,pid);
  38.         nr_threads++;
  39. }
  40.     //全局fork进程自增
  41.     total_forks ++;
  42.     spin_unlock(&current->sighand->siglock);
  43.     write_unlock_irq(&tasklist_lock);
  44.     //设置proc事件
  45.     proc_fork_connector(p);
  46.     cgroup_post_fork(p);
  47.     return p;

参考资料:

linux-2.24.3源代码

professional linux kernel architecture

lxr.linux.no

阅读(1477) | 评论(0) | 转发(0) |
0

上一篇:进程创建

下一篇:Linux 线程分析

给主人留下些什么吧!~~