Linux 2.6.12 内核 进程 1 的创建(三)
重头戏来了!copy_process() 函数完成父进程描述符中相关资源到子进程中的复制工作,并对关键资源进行了修改,以标识新生成的子进程!
由于此函数非常之长,在此就不列出完整源代码,只对关键部分进行分析!
`</p>
static task_t *copy_process(unsigned long clone_flags,unsigned long stack_start, struct pt_regs *regs, unsigned long stack_size, int __user *parent_tidptr, int __user *child_tidptr, int pid) { 首先对参数 clone_flags 的合理性进行验证! p = dup_task_struct(current); 复制一份当前进程的描述符的拷贝! ...... p->pid = pid; p->tgid = p->pid; 新建进程的线程组号为新建进程的进程号! 这里有一个问题需要注意: 对于普通进程,线程组号 tgid 和进程号 pid 是相同的;对于拥有多个线程的进程来说,进程第一个创建的线程的线程组号 tgid 和进程号 pid 相同,而随后创建的其他线程的 tgid 为第一个创建线程的进程号 pid。当我们调用sys_getpid()取得进程的进程号时,此函数返回的是当前进程的线程组号 tgid! ...... retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs); 为新建进程设置进程描述符中成员变量 thread !该成员变量保存了重要的信息,进程使用的内核栈等信息 ...... sched_fork(p); 初始化新建进程成员变量 sched_info,设置运行状态为 TASK_RUNNING ;进程抢占标志设为 1,禁止内核抢占,将父进程所剩时间片给子进程分配一半! attach_pid(p, PIDTYPE_PID, p->pid); attach_pid(p, PIDTYPE_TGID, p->tgid); 将新创建进程描述符添加到系统进程号的哈希表中,使得可通过进程号快速找到相应进程描述符地址! ...... fork_out: if (retval) return ERR_PTR(retval); return p; 最后返回新创进程的进程描述符! }`
就这样,copy_process 函数结束了它的使命 ……
`</p>
static struct task_struct *dup_task_struct(struct task_struct *orig) { struct task_struct *tsk; struct thread_info *ti; prepare_to_copy(orig); 在当前进程使用FPU的情况下,保存当前进程的 FPU 上下文内容到进程的成员变量 thread_info 中 tsk = alloc_task_struct(); 申请 sizeof(struct task_struct) 大小内存空间,用作新创建进程的进程描述符! if (!tsk) return NULL; ti = alloc_thread_info(tsk); 为新创建的进程申请 thread_info 结构所需的内存空间! if (!ti) { free_task_struct(tsk); return NULL; } *ti = *orig->thread_info; 将当前进程的 thread_info 结构中内容赋值给新创建的进程的 thread_info 结构中(进程1)! *tsk = *orig; 同样道理赋值 task_struct 结构 tsk->thread_info = ti; 修改新创建进程进程描述符的 thread_info 字段,使其指向新创建的 thread_info 结构! ti->task = tsk; 同样道理修改 thread_info 结构的 task 字段使其指向 tss_struct 结构! /* One for us, one for whoever does the "release_task()" (usually parent) */ atomic_set(&tsk->usage,2); 设置新创建子进程的进程描述符的引用计数usage为2。其中一个代表本进程对该进程描述符的引用计数;另一个是父进程的引用计数 return tsk; }`
`</p>
# define alloc_task_struct() kmem_cache_alloc(task_struct_cachep,GFP_KERNEL)`
从 task_struct_cachep 高速缓存描述符中分配一个对象!
在 fork_init() 函数中 已经创建好了task_struct 结构专用的缓冲队列!
`</p>
#define alloc_thread_info(tsk) \ ({ \ struct thread_info *ret; \ \ ret = kmalloc(THREAD_SIZE, GFP_KERNEL); \ if (ret) \ memset(ret, 0, THREAD_SIZE); \ ret; \ })`
从普通缓存中申请 thread_info 大小的内存空间,并清 0
copy_thread 函数对于理解内核栈很重要,所以接下来分析!