经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 数据库/运维 » Linux/Shell » 查看文章
linux2.4.0版本内核代码fork.c浅显分析
来源:cnblogs  作者:wxy1567  时间:2021/5/31 9:02:18  对本文有异议

结合fork.c文件分析进程创建的过程


本文为作业任务,只做浅显的分析,为大家提供一个分析的思路,很多细节都没有展示。如果想要更详细的分析请去搜索相关函数代码,博客园内有许多有用的信息供大家学习。

 

  1. int nr_threads;
  2. int nr_running;
  3. int max_threads;
  4. unsigned long total_forks; /* Handle normal Linux uptimes. */
  5. int last_pid;
  6. struct task_struct *pidhash[PIDHASH_SZ];

 

文件开头定义了线程数量,进程数量,最大线程数,创建的进程总个数,最新的pid号以及存放pid号的哈希表。

  1. void add_wait_queue(wait_queue_head_t *q, wait_queue_t * wait)
  2. {
  3. unsigned long flags;
  4. wq_write_lock_irqsave(&q->lock, flags);
  5. wait->flags = 0;
  6. __add_wait_queue(q, wait);
  7. wq_write_unlock_irqrestore(&q->lock, flags);
  8. }
  9. void add_wait_queue_exclusive(wait_queue_head_t *q, wait_queue_t * wait)
  10. {
  11. unsigned long flags;
  12. wq_write_lock_irqsave(&q->lock, flags);
  13. wait->flags = WQ_FLAG_EXCLUSIVE;
  14. __add_wait_queue_tail(q, wait);
  15. wq_write_unlock_irqrestore(&q->lock, flags);
  16. }
  17. void remove_wait_queue(wait_queue_head_t *q, wait_queue_t * wait)
  18. {
  19. unsigned long flags;
  20. wq_write_lock_irqsave(&q->lock, flags);
  21. __remove_wait_queue(q, wait);
  22. wq_write_unlock_irqrestore(&q->lock, flags);
  23. }

 

这部分代码与进程的等待队列有关。Linux内核的等待队列是以双循环链表为基础数据结构,与进程调度机制紧密结合,能够用于实现核心的异步事件通知机制。等待队列在include/linux/wait.h中,这是一个通过list_head连接的典型双循环链表,在这个链表中,有两种数据结构:等待队列头(wait_queue_head_t)和等待队列项(wait_queue_t)。等待队列头和等待队列项中都包含一个list_head类型的域作为"连接件"。由于我们只需要对队列进行添加和删除操作,并不会修改其中的对象(等待队列项),因此,我们只需要提供一把保护整个基础设施和所有对象的锁,这把锁保存在等待队列头中,为wq_lock_t类型。在实现中,可以支持读写锁(rwlock)或自旋锁(spinlock)两种类型,通过一个宏定义来切换。如果使用读写锁,将wq_lock_t定义为rwlock_t类型;如果是自旋锁,将wq_lock_t定义为spinlock_t类型。无论哪种情况,分别相应设置wq_read_lock、wq_read_unlock、wq_read_lock_irqsave、wq_read_unlock_irqrestore、wq_write_lock_irq、wq_write_unlock、wq_write_lock_irqsave和wq_write_unlock_irqrestore等宏。在__wait_queue 中定义的WQ_FLAG_EXCLUSIVE表示节点对应的进程对临界资源具有排他性。remove_wait_queue函数用于将等待队列项wait从以q为等待队列头的等待队列中移除

 

  1. void __init fork_init(unsigned long mempages)
  2. {
  3. /*
  4. * The default maximum number of threads is set to a safe
  5. * value: the thread structures can take up at most half
  6. * of memory.
  7. */
  8. max_threads = mempages / (THREAD_SIZE/PAGE_SIZE) / 2;
  9. init_task.rlim[RLIMIT_NPROC].rlim_cur = max_threads/2;
  10. init_task.rlim[RLIMIT_NPROC].rlim_max = max_threads/2;
  11. }

 

 

如注释所说,默认的最大线程数被设置为一个安全值:线程结构最多可以占用一半的内存。__init在include/linux/wait.h中,作用为将带有__init标识符的函数划分到.init.text段中,此段只在启动时做一次初始化载入。

  1. /* Protects next_safe and last_pid. */
  2. spinlock_t lastpid_lock = SPIN_LOCK_UNLOCKED;
  3. static int get_pid(unsigned long flags)
  4. {
  5. static int next_safe = PID_MAX;
  6. struct task_struct *p;
  7. if (flags & CLONE_PID)
  8. return current->pid;
  9. spin_lock(&lastpid_lock);
  10. if((++last_pid) & 0xffff8000) {
  11. last_pid = 300; /* Skip daemons etc. */
  12.  
  13. goto inside;
  14. }
  15. if(last_pid >= next_safe) {
  16. inside:
  17. next_safe = PID_MAX;
  18. read_lock(&tasklist_lock);
  19. repeat:
  20. for_each_task(p) {
  21. if(p->pid == last_pid ||
  22. p->pgrp == last_pid ||
  23. p->session == last_pid) {
  24. if(++last_pid >= next_safe) {
  25. if(last_pid & 0xffff8000)
  26. last_pid = 300;
  27. next_safe = PID_MAX;
  28. }
  29. goto repeat;
  30. }
  31. if(p->pid > last_pid && next_safe > p->pid)
  32. next_safe = p->pid;
  33. if(p->pgrp > last_pid && next_safe > p->pgrp)
  34. next_safe = p->pgrp;
  35. if(p->session > last_pid && next_safe > p->session)
  36. next_safe = p->session;
  37. }
  38. read_unlock(&tasklist_lock);
  39. }
  40. spin_unlock(&lastpid_lock);
  41. return last_pid;
  42. }

 

这部分代码用来给进程分配pid,对get_pid函数添加自旋锁保证函数的运行,对tasklist_lock添加读锁,确保pid数据安全。last_pid用于记录上一次分配给进程时的pid值。分配的pid一般而言是last_pid+1,如果超出进程个数的最大值(0xffff8000),那么进程pid值从300开始重新查找未用的。也就是说,一般用户进程的pid值范围[300,ffff8000]。(0~299,留给系统)。变量next_safe的含义是,在[last_pid,next_safe]之间,都是没有使用过的pid,一旦last_pid+1大于了next_safe,也就是说pid值进入了不可靠空间,有可能这个值被使用,这时需要遍历task来确认。这样遍历task找到一个没有用过的pid,同时确定next_safe,以保证next_safe到last_pid的区间中pid是空闲的,这样只要再次分配pid时,其值小于next_safe就可以直接分配,而不需要遍历task来查找空闲的pid。

 

  1. static inline int dup_mmap(struct mm_struct * mm)
  2. {
  3. struct vm_area_struct * mpnt, *tmp, **pprev;
  4. int retval;
  5. flush_cache_mm(current->mm);
  6. mm->locked_vm = 0;
  7. mm->mmap = NULL;
  8. mm->mmap_avl = NULL;
  9. mm->mmap_cache = NULL;
  10. mm->map_count = 0;
  11. mm->cpu_vm_mask = 0;
  12. mm->swap_cnt = 0;
  13. mm->swap_address = 0;
  14. pprev = &mm->mmap;
  15. for (mpnt = current->mm->mmap ; mpnt ; mpnt = mpnt->vm_next) {
  16. struct file *file;
  17. retval = -ENOMEM;
  18. if(mpnt->vm_flags & VM_DONTCOPY)
  19. continue;
  20. tmp = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
  21. if (!tmp)
  22. goto fail_nomem;
  23. *tmp = *mpnt;
  24. tmp->vm_flags &= ~VM_LOCKED;
  25. tmp->vm_mm = mm;
  26. mm->map_count++;
  27. tmp->vm_next = NULL;
  28. file = tmp->vm_file;
  29. if (file) {
  30. struct inode *inode = file->f_dentry->d_inode;
  31. get_file(file);
  32. if (tmp->vm_flags & VM_DENYWRITE)
  33. atomic_dec(&inode->i_writecount);
  34. /* insert tmp into the share list, just after mpnt */
  35. spin_lock(&inode->i_mapping->i_shared_lock);
  36. if((tmp->vm_next_share = mpnt->vm_next_share) != NULL)
  37. mpnt->vm_next_share->vm_pprev_share =
  38.  
  39. &tmp->vm_next_share;
  40. mpnt->vm_next_share = tmp;
  41. tmp->vm_pprev_share = &mpnt->vm_next_share;
  42. spin_unlock(&inode->i_mapping->i_shared_lock);
  43. }
  44. /* Copy the pages, but defer checking for errors */
  45. retval = copy_page_range(mm, current->mm, tmp);
  46. if (!retval && tmp->vm_ops && tmp->vm_ops->open)
  47. tmp->vm_ops->open(tmp);
  48. /*
  49. * Link in the new vma even if an error occurred,
  50. * so that exit_mmap() can clean up the mess.
  51. */
  52.  
  53. *pprev = tmp;
  54. pprev = &tmp->vm_next;
  55. if (retval)
  56. goto fail_nomem;
  57. }
  58. retval = 0;
  59. if (mm->map_count >= AVL_MIN_MAP_COUNT)
  60. build_mmap_avl(mm);
  61. fail_nomem:
  62. flush_tlb_mm(current->mm);
  63. return retval;
  64. }
  65. spinlock_t mmlist_lock __cacheline_aligned = SPIN_LOCK_UNLOCKED;
  66. #define allocate_mm() (kmem_cache_alloc(mm_cachep, SLAB_KERNEL))
  67.  
  68. #define free_mm(mm) (kmem_cache_free(mm_cachep, (mm)))
  69.  
  70.  
  71. static struct mm_struct * mm_init(struct mm_struct * mm)
  72. {
  73. atomic_set(&mm->mm_users, 1);
  74. atomic_set(&mm->mm_count, 1);
  75. init_MUTEX(&mm->mmap_sem);
  76. mm->page_table_lock = SPIN_LOCK_UNLOCKED;
  77. mm->pgd = pgd_alloc();
  78. if (mm->pgd)
  79. return mm;
  80. free_mm(mm);
  81. return NULL;
  82. }
  83. /*
  84. * Allocate and initialize an mm_struct.
  85. */
  86.  
  87. struct mm_struct * mm_alloc(void)
  88. {
  89. struct mm_struct * mm;
  90. mm = allocate_mm();
  91. if (mm) {
  92. memset(mm, 0, sizeof(*mm));
  93. return mm_init(mm);
  94. }
  95. return NULL;
  96. }
  97. /*
  98. * Called when the last reference to the mm
  99. * is dropped: either by a lazy thread or by
  100. * mmput. Free the page directory and the mm.
  101. */
  102. inline void __mmdrop(struct mm_struct *mm)
  103. {
  104. if (mm == &init_mm) BUG();
  105. pgd_free(mm->pgd);
  106. destroy_context(mm);
  107. free_mm(mm);
  108. }
  109. /*
  110. * Decrement the use count and release all resources for an mm.
  111. */
  112.  
  113. void mmput(struct mm_struct *mm)
  114. {
  115. if (atomic_dec_and_lock(&mm->mm_users, &mmlist_lock)) {
  116. list_del(&mm->mmlist);
  117. spin_unlock(&mmlist_lock);
  118. exit_mmap(mm);
  119. mmdrop(mm);
  120. }
  121. }
  122. void mm_release(void)
  123. {
  124. struct task_struct *tsk = current;
  125. /* notify parent sleeping on vfork() */
  126.  
  127. if (tsk->flags & PF_VFORK) {
  128. tsk->flags &= ~PF_VFORK;
  129. up(tsk->p_opptr->vfork_sem);
  130. }
  131. }

 

这部分代码为内存管理部分,代码中的注释向我们大致说明了本段代码的功能。

Linux内核通过一个被称为进程描述符的task_struct结构体来管理进程,这个结构体包含了一个进程所需的所有信息。它定义在include/linux/sched.h文件中。每一个进程都会有自己独立的mm_struct,这样每一个进程都会有自己独立的地址空间,这样才能互不干扰。在地址空间中,mmap为地址空间的内存区域(用vm_area_struct结构来表示)链表,表示起来更加方便。mm_struct的结构描述了进程的用户空间的结构,定义了用户空间的段分布:数据段,代码段,堆栈段。其中pgd_t是该进程用户空间地址映射到物理地址时使用vm_area_struct是进程用户空间已映射到物理空间的虚拟地址区间,定义在/include/linux/mm.h。mmap是该空间区块组成的链表。vm_flag是描述对虚拟区间的操作的标志。

 

 

  1. static int copy_mm(unsigned long clone_flags, struct task_struct * tsk)
  2. {
  3. struct mm_struct * mm, *oldmm;
  4. int retval;
  5. tsk->min_flt = tsk->maj_flt = 0;
  6. tsk->cmin_flt = tsk->cmaj_flt = 0;
  7. tsk->nswap = tsk->cnswap = 0;
  8. tsk->mm = NULL;
  9. tsk->active_mm = NULL;
  10. /*
  11. * Are we cloning a kernel thread?
  12. *
  13. * We need to steal a active VM for that..
  14. */
  15. oldmm = current->mm;
  16. if (!oldmm)
  17. return 0;
  18. if (clone_flags & CLONE_VM) {
  19. atomic_inc(&oldmm->mm_users);
  20. mm = oldmm;
  21. goto good_mm;
  22. }
  23. retval = -ENOMEM;
  24. mm = allocate_mm();
  25. if (!mm)
  26. goto fail_nomem;
  27. /* Copy the current MM stuff.. */
  28. memcpy(mm, oldmm, sizeof(*mm));
  29. if (!mm_init(mm))
  30. goto fail_nomem;
  31. down(&oldmm->mmap_sem);
  32. retval = dup_mmap(mm);
  33. up(&oldmm->mmap_sem);
  34. /*
  35. * Add it to the mmlist after the parent.
  36. *
  37. * Doing it this way means that we can order
  38. * the list, and fork() won't mess up the
  39. * ordering significantly.
  40. */
  41. spin_lock(&mmlist_lock);
  42. list_add(&mm->mmlist, &oldmm->mmlist);
  43. spin_unlock(&mmlist_lock);
  44. if (retval)
  45. goto free_pt;
  46. /*
  47. * child gets a private LDT (if there was an LDT in the parent)
  48. */
  49. copy_segments(tsk, mm);
  50. if (init_new_context(tsk,mm))
  51. goto free_pt;
  52. good_mm:
  53. tsk->mm = mm;
  54. tsk->active_mm = mm;
  55. return 0;
  56. free_pt:
  57. mmput(mm);
  58. fail_nomem:
  59. return retval;
  60. }
  61. static inline struct fs_struct *__copy_fs_struct(struct fs_struct *old)
  62. {
  63. struct fs_struct *fs = kmem_cache_alloc(fs_cachep, GFP_KERNEL);
  64. /* We don't need to lock fs - think why ;-) */
  65.  
  66. if (fs) {
  67. atomic_set(&fs->count, 1);
  68. fs->lock = RW_LOCK_UNLOCKED;
  69. fs->umask = old->umask;
  70. read_lock(&old->lock);
  71. fs->rootmnt = mntget(old->rootmnt);
  72. fs->root = dget(old->root);
  73. fs->pwdmnt = mntget(old->pwdmnt);
  74. fs->pwd = dget(old->pwd);
  75. if (old->altroot) {
  76. fs->altrootmnt = mntget(old->altrootmnt);
  77. fs->altroot = dget(old->altroot);
  78. } else {
  79. fs->altrootmnt = NULL;
  80. fs->altroot = NULL;
  81. }
  82. read_unlock(&old->lock);
  83. }
  84. return fs;
  85. }
  86. struct fs_struct *copy_fs_struct(struct fs_struct *old)
  87. {
  88. return __copy_fs_struct(old);
  89. }
  90. static inline int copy_fs(unsigned long clone_flags, struct task_struct * tsk)
  91. {
  92. if (clone_flags & CLONE_FS) {
  93. atomic_inc(&current->fs->count);
  94. return 0;
  95. }
  96. tsk->fs = __copy_fs_struct(current->fs);
  97. if (!tsk->fs)
  98. return -1;
  99. return 0;
  100. }
  101. static int count_open_files(struct files_struct *files, int size)
  102. {
  103. int i;
  104. /* Find the last open fd */
  105.  
  106. for (i = size/(8*sizeof(long)); i > 0; ) {
  107. if (files->open_fds->fds_bits[--i])
  108. break;
  109. }
  110. i = (i+1) * 8 * sizeof(long);
  111. return i;
  112. }
  113. static int copy_files(unsigned long clone_flags, struct task_struct * tsk)
  114. {
  115. struct files_struct *oldf, *newf;
  116. struct file **old_fds, **new_fds;
  117. int open_files, nfds, size, i, error = 0;
  118. /*
  119. * A background process may not have any files ...
  120. */
  121. oldf = current->files;
  122. if (!oldf)
  123. goto out;
  124. if (clone_flags & CLONE_FILES) {
  125. atomic_inc(&oldf->count);
  126. goto out;
  127. }
  128. tsk->files = NULL;
  129. error = -ENOMEM;
  130. newf = kmem_cache_alloc(files_cachep, SLAB_KERNEL);
  131. if (!newf)
  132. goto out;
  133. atomic_set(&newf->count, 1);
  134. newf->file_lock = RW_LOCK_UNLOCKED;
  135. newf->next_fd = 0;
  136. newf->max_fds = NR_OPEN_DEFAULT;
  137. newf->max_fdset = __FD_SETSIZE;
  138. newf->close_on_exec = &newf->close_on_exec_init;
  139. newf->open_fds = &newf->open_fds_init;
  140. newf->fd = &newf->fd_array[0];
  141. /* We don't yet have the oldf readlock, but even if the old
  142. fdset gets grown now, we'll only copy up to "size" fds */
  143. size = oldf->max_fdset;
  144. if (size > __FD_SETSIZE) {
  145. newf->max_fdset = 0;
  146. write_lock(&newf->file_lock);
  147. error = expand_fdset(newf, size);
  148. write_unlock(&newf->file_lock);
  149. if (error)
  150. goto out_release;
  151. }
  152. read_lock(&oldf->file_lock);
  153. open_files = count_open_files(oldf, size);
  154. /*
  155. * Check whether we need to allocate a larger fd array.
  156. * Note: we're not a clone task, so the open count won't
  157. * change.
  158. */
  159. nfds = NR_OPEN_DEFAULT;
  160. if (open_files > nfds) {
  161. read_unlock(&oldf->file_lock);
  162. newf->max_fds = 0;
  163. write_lock(&newf->file_lock);
  164. error = expand_fd_array(newf, open_files);
  165. write_unlock(&newf->file_lock);
  166. if (error)
  167. goto out_release;
  168. nfds = newf->max_fds;
  169. read_lock(&oldf->file_lock);
  170. }
  171. old_fds = oldf->fd;
  172. new_fds = newf->fd;
  173. memcpy(newf->open_fds->fds_bits, oldf->open_fds->fds_bits, open_files/8);
  174. memcpy(newf->close_on_exec->fds_bits, oldf->close_on_exec->fds_bits, open_files/8);
  175. for (i = open_files; i != 0; i--) {
  176. struct file *f = *old_fds++;
  177. if (f)
  178. get_file(f);
  179. *new_fds++ = f;
  180. }
  181. read_unlock(&oldf->file_lock);
  182. /* compute the remainder to be cleared */
  183. size = (newf->max_fds - open_files) * sizeof(struct file *);
  184. /* This is long word aligned thus could use a optimized version */
  185. memset(new_fds, 0, size);
  186. if (newf->max_fdset > open_files) {
  187. int left = (newf->max_fdset-open_files)/8;
  188. int start = open_files / (8 * sizeof(unsigned long));
  189. memset(&newf->open_fds->fds_bits[start], 0, left);
  190. memset(&newf->close_on_exec->fds_bits[start], 0, left);
  191. }
  192. tsk->files = newf;
  193. error = 0;
  194. out:
  195. return error;
  196. out_release:
  197. free_fdset (newf->close_on_exec, newf->max_fdset);
  198. free_fdset (newf->open_fds, newf->max_fdset);
  199. kmem_cache_free(files_cachep, newf);
  200. goto out;
  201. }
  202. static inline int copy_sighand(unsigned long clone_flags, struct task_struct * tsk)
  203. {
  204. struct signal_struct *sig;
  205. if (clone_flags & CLONE_SIGHAND) {
  206. atomic_inc(&current->sig->count);
  207. return 0;
  208. }
  209. sig = kmem_cache_alloc(sigact_cachep, GFP_KERNEL);
  210. tsk->sig = sig;
  211. if (!sig)
  212. return -1;
  213. spin_lock_init(&sig->siglock);
  214. atomic_set(&sig->count, 1);
  215. memcpy(tsk->sig->action, current->sig->action, sizeof(tsk->sig->action));
  216. return 0;
  217. }
  218. static inline void copy_flags(unsigned long clone_flags, struct task_struct *p)
  219. {
  220. unsigned long new_flags = p->flags;
  221. new_flags &= ~(PF_SUPERPRIV | PF_USEDFPU | PF_VFORK);
  222. new_flags |= PF_FORKNOEXEC;
  223. if (!(clone_flags & CLONE_PTRACE))
  224. p->ptrace = 0;
  225. if (clone_flags & CLONE_VFORK)
  226. new_flags |= PF_VFORK;
  227. p->flags = new_flags;
  228. }

 

父进程中在调用fork()派生新进程,实际上相当于创建了进程的一个拷贝;复制出来的子进程有自己的 task_struct结构和系统空间堆栈,但与父进程共享其他所有的资源。Linux为此提供了两个系统调用,一个是fork(),另一个是clone()。我们现在主要讨论fork()。fork()是全部复制,父进程所需的资源全部通过数据结构的复制传递给子进程,而完成这一操作的函数定义就是上方所写的代码段。调用fork时,内核会在copy_mm函数中处理子进程的mm_struct,在copy_files函数中处理拷贝父进程打开的文件的相关事宜,在copy_fs中记录进程所在文件系统的根目录和当前目录信息, copy_sighand中复制进程对信号的处理方式。

  1. /*
  2. * Ok, this is the main fork-routine. It copies the system process
  3. * information (task[nr]) and sets up the necessary registers. It also
  4. * copies the data segment in its entirety. The "stack_start" and
  5. * "stack_top" arguments are simply passed along to the platform
  6. * specific copy_thread() routine. Most platforms ignore stack_top.
  7. * For an example that's using stack_top, see
  8. * arch/ia64/kernel/process.c.
  9. */
  10.  
  11. int do_fork(unsigned long clone_flags, unsigned long stack_start,struct pt_regs *regs, unsigned long stack_size)
  12. {
  13. int retval = -ENOMEM;
  14. struct task_struct *p;
  15. DECLARE_MUTEX_LOCKED(sem);
  16. if (clone_flags & CLONE_PID) {
  17. /* This is only allowed from the boot up thread */
  18.  
  19. if (current->pid)
  20. return -EPERM;
  21. }
  22. current->vfork_sem = &sem;
  23. p = alloc_task_struct();
  24. if (!p)
  25. goto fork_out;
  26. *p = *current;
  27. retval = -EAGAIN;
  28. if (atomic_read(&p->user->processes) >= p->rlim[RLIMIT_NPROC].rlim_cur)
  29. goto bad_fork_free;
  30. atomic_inc(&p->user->__count);
  31. atomic_inc(&p->user->processes);
  32. /*
  33. * Counter increases are protected by
  34. * the kernel lock so nr_threads can't
  35. * increase under us (but it may decrease).
  36. */
  37.  
  38. if (nr_threads >= max_threads)
  39. goto bad_fork_cleanup_count;
  40. get_exec_domain(p->exec_domain);
  41. if (p->binfmt && p->binfmt->module)
  42. __MOD_INC_USE_COUNT(p->binfmt->module);
  43. p->did_exec = 0;
  44. p->swappable = 0;
  45. p->state = TASK_UNINTERRUPTIBLE;
  46. copy_flags(clone_flags, p);
  47. p->pid = get_pid(clone_flags);
  48. p->run_list.next = NULL;
  49. p->run_list.prev = NULL;
  50. if ((clone_flags & CLONE_VFORK) || !(clone_flags & CLONE_PARENT)) {
  51. p->p_opptr = current;
  52. if (!(p->ptrace & PT_PTRACED))
  53. p->p_pptr = current;
  54. }
  55. p->p_cptr = NULL;
  56. init_waitqueue_head(&p->wait_chldexit);
  57. p->vfork_sem = NULL;
  58. spin_lock_init(&p->alloc_lock);
  59. p->sigpending = 0;
  60. init_sigpending(&p->pending);
  61. p->it_real_value = p->it_virt_value = p->it_prof_value = 0;
  62. p->it_real_incr = p->it_virt_incr = p->it_prof_incr = 0;
  63. init_timer(&p->real_timer);
  64. p->real_timer.data = (unsigned long) p;
  65. p->leader = 0; /* session leadership doesn't inherit */
  66. p->tty_old_pgrp = 0;
  67. p->times.tms_utime = p->times.tms_stime = 0;
  68. p->times.tms_cutime = p->times.tms_cstime = 0;
  69. #ifdef CONFIG_SMP
  70. {
  71. int i;
  72. p->has_cpu = 0;
  73. p->processor = current->processor;
  74. /* ?? should we just memset this ?? */
  75.  
  76. for(i = 0; i < smp_num_cpus; i++)
  77. p->per_cpu_utime[i] = p->per_cpu_stime[i] = 0;
  78. spin_lock_init(&p->sigmask_lock);
  79. }
  80. #endif
  81. p->lock_depth = -1; /* -1 = no lock */
  82. p->start_time = jiffies;
  83. retval = -ENOMEM;
  84. /* copy all the process information */
  85.  
  86. if (copy_files(clone_flags, p))
  87. goto bad_fork_cleanup;
  88. if (copy_fs(clone_flags, p))
  89. goto bad_fork_cleanup_files;
  90. if (copy_sighand(clone_flags, p))
  91. goto bad_fork_cleanup_fs;
  92. if (copy_mm(clone_flags, p))
  93. goto bad_fork_cleanup_sighand;
  94. retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs);
  95. if (retval)
  96. goto bad_fork_cleanup_sighand;
  97. p->semundo = NULL;
  98. /* Our parent execution domain becomes current domain
  99. These must match for thread signalling to apply */
  100. p->parent_exec_id = p->self_exec_id;
  101. /* ok, now we should be set up.. */
  102. p->swappable = 1;
  103. p->exit_signal = clone_flags & CSIGNAL;
  104. p->pdeath_signal = 0;
  105. /*
  106. * "share" dynamic priority between parent and child, thus the
  107. * total amount of dynamic priorities in the system doesnt change,
  108. * more scheduling fairness. This is only important in the first
  109. * timeslice, on the long run the scheduling behaviour is unchanged.
  110. */
  111. p->counter = (current->counter + 1) >> 1;
  112. current->counter >>= 1;
  113. if (!current->counter)
  114. current->need_resched = 1;
  115. /*
  116. * Ok, add it to the run-queues and make it
  117. * visible to the rest of the system.
  118. *
  119. * Let it rip!
  120. */
  121. retval = p->pid;
  122. p->tgid = retval;
  123. INIT_LIST_HEAD(&p->thread_group);
  124. write_lock_irq(&tasklist_lock);
  125. if (clone_flags & CLONE_THREAD) {
  126. p->tgid = current->tgid;
  127. list_add(&p->thread_group, &current->thread_group);
  128. }
  129. SET_LINKS(p);
  130. hash_pid(p);
  131. nr_threads++;
  132. write_unlock_irq(&tasklist_lock);
  133. if (p->ptrace & PT_PTRACED)
  134. send_sig(SIGSTOP, p, 1);
  135. wake_up_process(p); /* do this last */
  136.  
  137. ++total_forks;
  138. fork_out:
  139. if ((clone_flags & CLONE_VFORK) && (retval > 0))
  140. down(&sem);
  141. return retval;
  142. bad_fork_cleanup_sighand:
  143. exit_sighand(p);
  144. bad_fork_cleanup_fs:
  145. exit_fs(p); /* blocking */
  146. bad_fork_cleanup_files:
  147. exit_files(p); /* blocking */
  148. bad_fork_cleanup:
  149. put_exec_domain(p->exec_domain);
  150. if (p->binfmt && p->binfmt->module)
  151. __MOD_DEC_USE_COUNT(p->binfmt->module);
  152. bad_fork_cleanup_count:
  153. atomic_dec(&p->user->processes);
  154. free_uid(p->user);
  155. bad_fork_free:
  156. free_task_struct(p);
  157. goto fork_out;
  158. }

 

如开头注释第一句所说,这部分代码是fork.c中最主要的函数。

do_fork首先进行一些参数及权限的检查,仅允许从线程启动。之后进行内存的分配,复制父进程的task_struct。判断进程数量,将从父进程中继承的task_struct初始化,获取新的pid,分配CPU,解锁后设定运行时间。将子进程的pid放入pidhash表中,就可以唤醒子进程了。代码中间部分有设置进程判断,若发现非法进程会直接清理掉。清理函数在代码尾部定义。

 

  1. /* SLAB cache for signal_struct structures (tsk->sig) */
  2. kmem_cache_t *sigact_cachep;
  3. /* SLAB cache for files_struct structures (tsk->files) */
  4. kmem_cache_t *files_cachep;
  5. /* SLAB cache for fs_struct structures (tsk->fs) */
  6. kmem_cache_t *fs_cachep;
  7. /* SLAB cache for vm_area_struct structures */
  8. kmem_cache_t *vm_area_cachep;
  9. /* SLAB cache for mm_struct structures (tsk->mm) */
  10. kmem_cache_t *mm_cachep;
  11. void __init proc_caches_init(void)
  12. {
  13. sigact_cachep = kmem_cache_create("signal_act",
  14. sizeof(struct signal_struct), 0,
  15. SLAB_HWCACHE_ALIGN, NULL, NULL);
  16. if (!sigact_cachep)
  17. panic("Cannot create signal action SLAB cache");
  18. files_cachep = kmem_cache_create("files_cache",
  19. sizeof(struct files_struct), 0,
  20. SLAB_HWCACHE_ALIGN, NULL, NULL);
  21. if (!files_cachep)
  22. panic("Cannot create files SLAB cache");
  23. fs_cachep = kmem_cache_create("fs_cache",
  24. sizeof(struct fs_struct), 0,
  25. SLAB_HWCACHE_ALIGN, NULL, NULL);
  26. if (!fs_cachep)
  27. panic("Cannot create fs_struct SLAB cache");
  28. vm_area_cachep = kmem_cache_create("vm_area_struct",
  29. sizeof(struct vm_area_struct), 0,
  30. SLAB_HWCACHE_ALIGN, NULL, NULL);
  31. if(!vm_area_cachep)
  32. panic("vma_init: Cannot alloc vm_area_struct SLAB cache");
  33. mm_cachep = kmem_cache_create("mm_struct",
  34. sizeof(struct mm_struct), 0,
  35. SLAB_HWCACHE_ALIGN, NULL, NULL);
  36. if(!mm_cachep)
  37. panic("vma_init: Cannot alloc mm_struct SLAB cache");
  38. }

 

最后这部分代码作用是处理进程的缓存,为proc文件系统创建高速缓冲。

 

 

从文件开头的宏定义,到等待队列的处理,到线程数的安全处理,到pid的分配,到进程的内存管理,到父进程复制出子进程。fork()函数中对进程的创建大致是以上步骤。主要在于copy部分对task_struct复制和复制后的初始化。

 

原文链接:http://www.cnblogs.com/wxy1567/p/14806603.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号