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 函数对于理解内核栈很重要,所以接下来分析!