`
xiaoer_1982
  • 浏览: 1889411 次
  • 性别: Icon_minigender_2
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

uclinux创建内核线程的几种方式

阅读更多

快乐虾

http://blog.csdn.net/lights_joy/

lights@hb165.com

本文适用于

ADSP-BF561

uclinux-2010r1-pre

Visual DSP++ 5.0(update 5)

欢迎转载,但请保留作者信息

1.1.1 kernel_thread创建线程

创建内核线程可以使用kernel_thread函数:

/*

* Create a kernel thread.

*/

pid_t kernel_thread(int (*fn) (void *), void *arg, unsigned long flags)

{

struct pt_regs regs;

memset(&regs, 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, &regs, 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_startstack_size的值都为0

1.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.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_regsPC指针是指向kernel_thread_helper的。且新线程的PC指针是指向ret_from_fork函数。

1.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, &current->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;

}

至此,一个内核线程真正加入到CPU的任务队列。

1.1.2 kthread_create创建线程

先看看这个函数的实现:

/**

* kthread_create - create a kthread.

* @threadfn: the function to run until signal_pending(current).

* @data: data ptr for @threadfn.

* @namefmt: printf-style name for the thread.

*

* Description: This helper function creates and names a kernel

* thread. The thread will be stopped: use wake_up_process() to start

* it. See also kthread_run(), kthread_create_on_cpu().

*

* When woken, the thread will run @threadfn() with @data as its

* argument. @threadfn() can either call do_exit() directly if it is a

* standalone thread for which noone will call kthread_stop(), or

* return when 'kthread_should_stop()' is true (which means

* kthread_stop() has been called). The return value should be zero

* or a negative error number; it will be passed to kthread_stop().

*

* Returns a task_struct or ERR_PTR(-ENOMEM).

*/

struct task_struct *kthread_create(int (*threadfn)(void *data),

void *data,

const char namefmt[],

...)

{

struct kthread_create_info create;

create.threadfn = threadfn;

create.data = data;

init_completion(&create.started);

init_completion(&create.done);

spin_lock(&kthread_create_lock);

list_add_tail(&create.list, &kthread_create_list);

spin_unlock(&kthread_create_lock);

wake_up_process(kthreadd_task);

wait_for_completion(&create.done);

if (!IS_ERR(create.result)) {

struct sched_param param = { .sched_priority = 0 };

va_list args;

va_start(args, namefmt);

vsnprintf(create.result->comm, sizeof(create.result->comm),

namefmt, args);

va_end(args);

/*

* root may have changed our (kthreadd's) priority or CPU mask.

* The kernel thread should not inherit these properties.

*/

sched_setscheduler_nocheck(create.result, SCHED_NORMAL, &param);

set_user_nice(create.result, KTHREAD_NICE_LEVEL);

set_cpus_allowed_ptr(create.result, cpu_all_mask);

}

return create.result;

}

这个函数将填充一个kthread_create_info结构体,并将之放到待创建的线程链表中,注意,它并没有直接将线程插入到CPU要运行的任务队列中,这个工作将由kthreadd这个内核线程来完成。

int kthreadd(<spa

分享到:
评论

相关推荐

    uclinux内核中断处理_v0.3

    ### uclinux内核中断处理_v0.3 #### 数据结构 **1.1 irq_desc** `irq_desc` 结构体是 uclinux 内核为每个中断定义的数据结构,用于描述中断特性及其处理方式。它与硬件无关,提供了一种抽象的方式来管理不同类型...

    MiniGUI for uCLinux移植

    同时,确保你有uCLinux内核的源码,因为可能需要对内核进行特定的配置和修改。 3. **内核配置**:在uCLinux内核源码中,打开对图形子系统的支持,比如Framebuffer驱动。如果目标硬件有特定的图形加速器,可能还需要...

    四种流行的嵌入式实时操作系统的比较研究

    #### 市场上几种比较常用的RTOS ##### 1.1 市场份额排名与性能表现 根据CMP公司在2005年6月份发布的市场调研报告,以及2003年加拿大航天局的研究结果,可以总结出以下几点: - **VxWorks**:由Wind River Systems...

    tcp ip 函数 分析

    Linux是一种广泛使用的开源操作系统内核,而uClinux则是针对资源受限的嵌入式系统的变种版本。两者的主要区别包括: - **内存管理**:Linux支持虚拟内存管理,而uClinux由于不支持MMU(Memory Management Unit),...

    嵌入式LINUX 系统开发概述

    - **4.1.3 uClinux内核的内存管理** - 采用了虚拟内存管理机制,支持动态内存分配。 - **4.1.4 uClinux系统的多进程处理** - 实现了基于线程的进程管理,支持多任务并发执行。 - **4.1.5 uClinux系统的应用程序...

    MCF5282的I2C总线在uClinux操作系统下的驱动程序设计

    同步操作通常使用等待队列或者轮询,而异步操作则可以使用工作队列、任务队列或者工作线程等方式。 - I2C总线驱动的框架结构:在Linux内核中,I2C驱动程序通常继承自I2C核心提供的抽象接口,比如i2c_adapter、i2c_...

    Linux下ARM堆栈解析

    在uClinux内核中,通常会定义一个回调函数`callback_trace`用于在检测到异常时收集当前调用栈的信息。这一过程涉及到对当前线程的`struct pt_regs`结构体进行解析,提取出关键寄存器的值,从而构建出当前函数调用...

    注意事项中嵌教育嵌入式linux开发PPT课件.pptx

    设备驱动开发是课程的重点之一,学员将学习uClinux内核模块和设备驱动的开发,涵盖不同类型的驱动,如蜂鸣器、按键、串口、触摸屏、网络、USB和液晶屏驱动。此外,课程还包括ARMLinux的移植,引导程序Uboot的移植,...

    教你学u COS移植

    学习OSKit可以帮助理解操作系统内核的工作原理,为uC/OS的移植提供理论基础,特别是如何实现任务调度和线程上下文切换。 再者,实时CORBA(Common Object Request Broker Architecture)结构构架是另一种与uC/OS...

    Up-Star2410指导资料

    学习如何在Linux环境下使用pthread库创建、管理和同步线程,理解互斥锁、信号量、条件变量等同步机制,是嵌入式开发者的基本技能之一。 #### 2.3 串行端口程序设计 串行通信在嵌入式系统中非常常见,无论是与...

    控制用单片机操作系统的选择归类.pdf

    本文将主要探讨几种主流的实时操作系统,包括商用型和免费型,以帮助用户在选择适合的单片机操作系统时做出明智决策。 首先,VxWorks由美国WindRiver公司开发,是一款高性能、高可靠性的实时操作系统。它的内核精炼...

    UP-Star2410实验指导v3.0

    - **2.2 多线程应用程序设计**:讲解如何在嵌入式Linux环境中设计和实现多线程程序,提高程序的并发执行能力。 - **2.3 串行端口程序设计**:介绍如何利用串行端口进行通信编程,实现数据传输等功能。 - **2.4 简单...

    嵌入式系统开发之路.pdf

    - **RTEMS、eCOS、RedBoot**:介绍了几种小型RTOS和bootloader,如RTEMS的实用性,eCOS与RedBoot的混合使用,展示了嵌入式操作系统的多样性和选择性。 ### 操作系统比较 - **VxWorks**:被标榜为世界上领先的...

    Java在基于WEB的嵌入式系统中的应用

    Java语言是作为一种面向对象、多线程编程语言,特别适合于网络环境的应用开发。它的设计理念中有一项重要的原则就是“一次编写,到处运行”(WORA),这意味着Java代码可以在不同的平台和设备上运行,而不需要重新...

    嵌入式学习方法

    - **驱动开发与内核分析**:掌握Linux内核原理,学习如何编写设备驱动程序,这对嵌入式开发至关重要。 #### 四、嵌入式Linux学习 - **嵌入式微处理器结构与应用**:深入学习ARM架构的原理及应用,尤其是ARM汇编...

Global site tag (gtag.js) - Google Analytics