您的位置:首页 > 运维架构

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

2012-11-28 23:00 399 查看
<pre name="code" class="cpp">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;}</pre><br>

<br>

<pre></pre>

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

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

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

<p>3.将task_struct和thread_info联系起来</p>

<p>4.其他相关的设置。</p>

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

<p></p>

<pre></pre>

<pre></pre>

<pre></pre>

<pre></pre>

<pre></pre>

<pre></pre>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐