关闭

内核进程创建之分配task_struct(do_fork->copy_process->dup_task_struct())

2670人阅读 评论(0) 收藏 举报
static struct task_struct *dup_task_struct(struct task_struct *orig)
{
	struct task_struct *tsk;	//sizeof(task_struct) = 3236;这个值是通过gdb得到的,
                                        //可以看到单个的task_struct的大小已经超过了3K,但是系统最开始只为一个进程分配了两页的空间,thread_info在
                                        //页的开始的地方,页的高端地址即是该进程的内核堆栈。thread_info结构体的第一个指针指向其task_struct结构。
        struct thread_info *ti;		//sizeof(thead_info) = 72
	unsigned long *stackend;
	int node = tsk_fork_get_node(orig); //get node information for about to be created task;
                                 //如果使用了NUMA技术,则返回orig中的pref_node_fork字段;否则返回numa_node_id,因为没有定义NUMA所以是0
        int err;

	prepare_to_copy(orig);	//arch/x86/kernel/process_32.c:187	:从代码看来这个函数主要以非抢占的方式来设置与FPU相关的东西
                                //不过,注意这个宏:#define task_thread_info(task)    ((struct thread_info *)(task)->stack)

/*kmem_cache_alloc_node(task_struct_cachep, GFP_KERNEL, node)告诉我们,系统是从专业高速缓存中分配空间的,
注意,在分配thred_info及相关的页面时,是从分配的page。这里涉及到内存管理的知识,我们以后再讲。
*/

        tsk = alloc_task_struct_node(node); // include的/linux/slab.h --> mm/slub.cif (!tsk)return NULL;
        ti = alloc_thread_info_node(tsk, node); //ti指向thread_info的首地址,同时也是系统为新进程分配的两个连续页面的首地址。
/*
调用宏,可以看到这部分的内存是分配的正常的页面。
#define alloc_thread_info_node(tsk, node) \
({ \
struct page *page = alloc_pages_node(node, THREAD_FLAGS, \
THREAD_ORDER); \
struct thread_info *ret = page ? page_address(page) : NULL; \
\
ret; //返回两个连续页面的首地址 \
})
#define THREAD_FLAGS (GFP_KERNEL | __GFP_NOTRACK)//意思是这个分配代表运行在内核空间的进程而进行的
#define THREAD_ORDER    1                        //分配连续的两个页面
 
*/
       if (!ti) {free_task_struct(tsk);return NULL;} 
       err = arch_dup_task_struct(tsk, orig); // arch/x86/kernel/process.c:33 ; copy the parent's task_struct to child's task_struct.
       if (err)goto out;
       tsk->stack = ti; //child's threadinfo pointer points in its task_struct points to its parent's threadinfo
       /* task_struct的stack字段指向本进程的thread_info结构的首地址,即指向内核为进程分配的两个连续页面的首地址*/
 /*

       将父进程的task_struct中的内容拷贝到子进程的task_struct中/。
 */       
       err = prop_local_init_single(&tsk->dirties); //???if (err)goto out;setup_thread_stack(tsk, orig); //include/linux/sched.h:2385;copy parent's thread_info to child's thread_info,并检查设置FPU相关项。/*
static inline void setup_thread_stack(struct task_struct *p, struct task_struct *org)
{
    *task_thread_info(p) = *task_thread_info(org); //将父进程thread_info中的内容完全复制到子进程相应的字段中
    task_thread_info(p)->task = p;                 //但是,还是要将子进程thread_info字段中的task_struct字段指向子进程自己的task_struct结构
}



clear_user_return_notifier(tsk);  //include/linux/thread_info.h:69;从用户空间返回时不通知内核???
/*
static inline void clear_user_return_notifier(struct task_struct *p)
{
    clear_tsk_thread_flag(p, TIF_USER_RETURN_NOTIFY);

TIF_USER_RETURN_NOTIFY indicates notify kernel of userspace return 

*/
        clear_tsk_need_resched(tsk);  //不允许调度该进程
/*
    clear_tsk_thread_flag(tsk,TIF_NEED_RESCHED);
    TIF_NEED_RESCHED indicates that the process should be or would like to be replaced with
    another process by the scheduler.


*/
        stackend = end_of_stack(tsk); //注意stackend的计算方法哦。/*
static inline unsigned long *end_of_stack(struct task_struct *p)
{
    return (unsigned long *)(task_thread_info(p) + 1); //这个地方+1,则指针向上移动了sizeof(thread_info)指向了系统堆栈的最大下界。
}
*/
*stackend = STACK_END_MAGIC; /* for overflow detection */STACK_END_MAGIC是一个栈溢出标记。#ifdef CONFIG_CC_STACKPROTECTORtsk->stack_canary = get_random_int();#endif/* One for us, one for whoever does the "release_task()" (usually parent) */atomic_set(&tsk->usage,2); 

/*
  将task_struct的usage字段表明有几个进程正在使用该结构,初始化为2,表明一个是进程本身的,而另一个则是表明父进程也使用该结构。

*/
       atomic_set(&tsk->fs_excl, 0); //禁止该进程独占文件系统#ifdef CONFIG_BLK_DEV_IO_TRACEtsk->btrace_seq = 0;#endiftsk->splice_pipe = NULL;account_kernel_stack(ti, 1); //???/*
static void account_kernel_stack(struct thread_info *ti, int account)
{
    struct zone *zone = page_zone(virt_to_page(ti));  //获取thread_info所在的zone结构。

    mod_zone_page_state(zone, NR_KERNEL_STACK, account);
}
调用的第二个函数与内存管理机制有关,以后再说,重点在第一个函数。
#define virt_to_page(kaddr)    pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)
这里:

__pa(kaddr) >> PAGE_SHIFT

可以直接获取页面的页号,因为_pa(kaddr)获取实际的物理地址,而它所做的工作不过是将kaddr减3G(为什么呢?);然后右移12位及得到了相应的页号。
*/return tsk;out:free_thread_info(ti);free_task_struct(tsk);return NULL;}



至此,dup_task_struct算是讲完了,总结一下,它完成的主要功能如下:

1.在专业高速缓冲内存上分配task_struct,并完成初始化

2.在普通内存中分配thread_info及连续的两个页面,完成初始化

3.将task_struct和thread_info联系起来

4.其他相关的设置。

重点:task_struct 和thread_info的联系,及与thread_info所在的连续的两个内存页面的联系。







   
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:401071次
    • 积分:5221
    • 等级:
    • 排名:第5447名
    • 原创:112篇
    • 转载:63篇
    • 译文:0篇
    • 评论:37条
    最新评论