uclinux内核线程的创建(转)
創建內核線程可以使用kernel_thread函數:
/*
?* Create a kernel thread.
?*/
pid_t kernel_thread(int?(*fn) (void?*),?void?*arg,?unsigned?long?flags)
{
?????struct?pt_regs regs;
?
?????memset(®s, 0,?sizeof(regs));
?
?????regs.r1 = (unsigned?long)arg;
?????regs.p1 = (unsigned?long)fn;
?????regs.pc = (unsigned?long)kernel_thread_helper;
?????regs.orig_p0 = -1;
?????/* Set bit 2 to tell ret_from_fork we should be returning to kernel
????????mode.??*/
?????regs.ipend = 0x8002;
?????__asm__ __volatile__("%0 = syscfg;":"=d"(regs.syscfg):);
?????return?do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL,
????????????????NULL);
}
注意這里的pc值的設置,它指向了kernel_thread_help,這將是這個內核線程要執行的第一行語句:
/*
?* This gets run with P1 containing the
?* function to call, and R1 containing
?* the "args".??Note P0 is clobbered on the way here.
?*/
void?kernel_thread_helper(void);
__asm__(".section .text\n"
?????".align 4\n"
?????"_kernel_thread_helper:\n\t"
?????"\tsp += -12;\n\t"
?????"\tr0 = r1;\n\t"?"\tcall (p1);\n\t"?"\tcall _do_exit;\n"?".previous;");
在這段代碼中,將跳轉到用戶指定的函數,然后調用do_exit進行一些清理工作。
具體的創建工作由do_fork完成,此時傳遞進去的stack_start和stack_size的值都為0。
1.1.1???do_fork
這個函數完成線程的創建,它的關鍵代碼如下:
/*
?*??Ok, this is the main fork-routine.
?*
?* It copies the process, and if successful kick-starts
?* it and waits for it to finish using the VM if required.
?*/
long?do_fork(unsigned?long?clone_flags,
???????????unsigned?long?stack_start,
???????????struct?pt_regs *regs,
???????????unsigned?long?stack_size,
???????????int?__user *parent_tidptr,
???????????int?__user *child_tidptr)
{
?????struct?task_struct *p;
?????int?trace = 0;
?????struct?pid *pid = alloc_pid();
?????long?nr;
……………………
?
?????p = copy_process(clone_flags, stack_start, regs, stack_size, parent_tidptr, child_tidptr, pid);
?
?????/*
??????* Do this prior waking up the new thread - the thread pointer
??????* might get invalid after that point, if the thread exits quickly.
??????*/
?????if?(!IS_ERR(p)) {
?????????struct?completion vfork;
…………………….
?
?????????if?(!(clone_flags & CLONE_STOPPED))
??????????????wake_up_new_task(p, clone_flags);
?????????else
??????????????p->state = TASK_STOPPED;
……………………….
?????}?else?{
?????????free_pid(pid);
?????????nr = PTR_ERR(p);
?????}
?????return?nr;
}
它首先為此線程分配一個pid號,然后復制出一個新的task_struct,最后喚醒此線程,當然此時還不會進入執行狀態。
1.1.2???copy_process
這個函數用于從當前線程復制一個task_struct出來。
/*
?* This creates a new process as a copy of the old one,
?* but does not actually start it yet.
?*
?* It copies the registers, and all the appropriate
?* parts of the process environment (as per the clone
?* flags). The actual kick-off is left to the caller.
?*/
static?struct?task_struct *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,
???????????????????????struct?pid *pid)
{
?????int?retval;
?????struct?task_struct *p = NULL;
……………………….
?????retval = -ENOMEM;
?????p = dup_task_struct(current);
?????if?(!p)
?????????goto?fork_out;
………………………..
?????retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs);
?????if?(retval)
?????????goto?bad_fork_cleanup_namespaces;
…………………………
?????return?p;
}
它首先調用dup_task_struct得到一個task_struct,同時也給這個新的線程分配了一個thread_info的結構體,這也是這個新線程的棧,使用BUDDY算法分配,保證以8K對齊。
接著調用copy_thread進行線程的復制。
int
copy_thread(int?nr,?unsigned?long?clone_flags,
?????????unsigned?long?usp,?unsigned?long?topstk,
?????????struct?task_struct *p,?struct?pt_regs *regs)
{
?????struct?pt_regs *childregs;
?
?????childregs = (struct?pt_regs *) (task_stack_page(p) + THREAD_SIZE) - 1;
?????*childregs = *regs;
?????childregs->r0 = 0;
?
?????p->thread.usp = usp;
?????p->thread.ksp = (unsigned?long)childregs;
?????p->thread.pc = (unsigned?long)ret_from_fork;
?
?????return?0;
}
注意這里在新線程的棧的底端復制了一份pt_regs,而這份pt_regs的PC指針是指向kernel_thread_helper的。且新線程的PC指針是指向ret_from_fork函數。
1.1.3???wake_up_new_task
這個函數用于把線程放到一個CPU核的任務隊列中。
/*
?* wake_up_new_task - wake up a newly created task for the first time.
?*
?* This function will do some initial scheduler statistics housekeeping
?* that must be done for every newly created context, then puts the task
?* on the runqueue and wakes it.
?*/
void?fastcall wake_up_new_task(struct?task_struct *p,?unsigned?long?clone_flags)
{
?????struct?rq *rq, *this_rq;
?????unsigned?long?flags;
?????int?this_cpu, cpu;
?
?????rq = task_rq_lock(p, &flags);
?????BUG_ON(p->state != TASK_RUNNING);
?????this_cpu = smp_processor_id();
?????cpu = task_cpu(p);
?
?????/*
??????* We decrease the sleep average of forking parents
??????* and children as well, to keep max-interactive tasks
??????* from forking tasks that are max-interactive. The parent
??????* (current) is done further down, under its lock.
??????*/
?????p->sleep_avg = JIFFIES_TO_NS(CURRENT_BONUS(p) *
?????????CHILD_PENALTY / 100 * MAX_SLEEP_AVG / MAX_BONUS);
?
?????p->prio = effective_prio(p);
?
?????if?(likely(cpu == this_cpu)) {
?????????if?(!(clone_flags & CLONE_VM)) {
??????????????/*
???????????????* The VM isn't cloned, so we're in a good position to
???????????????* do child-runs-first in anticipation of an exec. This
???????????????* usually avoids a lot of COW overhead.
???????????????*/
??????????????if?(unlikely(!current->array))
???????????????????__activate_task(p, rq);
??????????????else?{
???????????????????p->prio = current->prio;
???????????????????p->normal_prio = current->normal_prio;
???????????????????list_add_tail(&p->run_list, ¤t->run_list);
???????????????????p->array?= current->array;
???????????????????p->array->nr_active++;
???????????????????inc_nr_running(p, rq);
??????????????}
??????????????set_need_resched();
?????????}?else
??????????????/* Run child last */
??????????????__activate_task(p, rq);
?????????/*
??????????* We skip the following code due to cpu == this_cpu
???????????*
??????????*???task_rq_unlock(rq, &flags);
??????????*???this_rq = task_rq_lock(current, &flags);
??????????*/
?????????this_rq = rq;
?????}?else?{
?????????this_rq = (struct?rq *)cpu_rq(this_cpu);
?
?????????/*
??????????* Not the local CPU - must adjust timestamp. This should
??????????* get optimised away in the !CONFIG_SMP case.
??????????*/
?????????p->timestamp = (p->timestamp - this_rq->most_recent_timestamp)
???????????????????????+ rq->most_recent_timestamp;
?????????__activate_task(p, rq);
?????????if?(TASK_PREEMPTS_CURR(p, rq))
??????????????resched_task(rq->curr);
?
?????????/*
??????????* Parent and child are on different CPUs, now get the
??????????* parent runqueue to update the parent's ->sleep_avg:
??????????*/
?????????task_rq_unlock(rq, &flags);
?????????this_rq = task_rq_lock(current, &flags);
?????}
?????current->sleep_avg = JIFFIES_TO_NS(CURRENT_BONUS(current) *
?????????PARENT_PENALTY / 100 * MAX_SLEEP_AVG / MAX_BONUS);
?????task_rq_unlock(this_rq, &flags);
}
這個函數挺長的,但實際上將新線程加入隊列的工作是由__activate_task這個函數完成的:
/*
?* __activate_task - move a task to the runqueue.
?*/
static?void?__activate_task(struct?task_struct *p,?struct?rq *rq)
{
?????struct?prio_array *target = rq->active;
?
?????if?(batch_task(p))
?????????target = rq->expired;
?????enqueue_task(p, target);
?????inc_nr_running(p, rq);
}
再看enqueue_task:
static?void?enqueue_task(struct?task_struct *p,?struct?prio_array *array)
{
?????sched_info_queued(p);
?????list_add_tail(&p->run_list,?array->queue + p->prio);
?????__set_bit(p->prio,?array->bitmap);
?????array->nr_active++;
?????p->array?=?array;
}
快樂蝦
http://blog.csdn.net/lights_joy/
lights@hb165.com
??
本文適用于
ADSP-BF561
uclinux-2008r1.5-RC3(移植到vdsp5)
Visual DSP++ 5.0(update 5)
??
歡迎轉載,但請保留作者信息
總結
以上是生活随笔為你收集整理的uclinux内核线程的创建(转)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 小米的游戏加速在哪里(小米官方售后服务)
- 下一篇: __FUNCTION__, __FILE