转载自 :http://blog.csdn.net/wangzaiwei2006/article/details/6358694
名称:
线程创建
函数名:
cyg_thread_create--创建一个新的线程
概要 :
[cpp] view plaincopy
#include <cyg/kernel/kapi.h>
void cyg_thread_create(cyg_addrword_t sched_info, cyg_thread_entry_t* entry, cyg_addrword_t entry_data, char* name, void* stack_base, cyg_ucount32 stack_size, cyg_handle_t* handle, cyg_thread* thread);
说明:
函数cyg_thread_create允许应用程序代码和ecos包创建新的线程.在许多应用程序中这只会在系统初始化的过程中发生,并且所有需要的数据都应经静态地指定好了。然而,如果有必要的话,新的线程可能在任何时候被创建。新创建的线程总是处于挂起状态,直到通过调用cyg_thread_resume函数唤醒他才会处于运行状态.同样的,如果线程在系统初始化的时候被创建,则直到ecos的调度器开始运行后他们才会开始运行
参数name主要是用于debug的目的,这使得我们能更容易的记录哪个cyg-thread线程结构和哪个application-level的线程有联系。内核配置选项CYGVAR_KERNEL_THREADS_NAME控制着这个参数是否真的使用到.
在创建过程中每个线程都被分配到一个唯一的句柄(handle),并且线程会被存储到这个句柄参数指定的位置。随后对于该线程的操作包括cyg_thread_resume操作都应该使用这个句柄来区分每个线程。
对于每个线程,内核需要少量的空间来以cyg_thread数据结构的形式存储,以此来保存比如该线程目前状态的信息。为了避免在内核中进行动态内存申请,这块空间都由higher-level code来提供,典型的形式是使用静态变量,线程的参数提供了这段空间。
线程入口指针 :
线程的入口指针一般表现为以下形式
[cpp] view plaincopy
void
thread_entry_function(cyg_addrword_t data)
{
…
}
cyg_thread_create 的第二个参数就是一个指向这种函数的指针.第三个参数entry_data通常用来向这个函数提供参数,典型的是以指针的形式,指针可能指向一些静态数据,或者是一个小整数,或者如果线程不需要什么参数数据的话 , 干脆是0。
如果线程的入口函数返回,那这就相当于线程调用了cyg_thread_exit。这样尽管这个线程不再运行,它依然在注册在调度器中。如果应用程序需要重新使用这个cyg_thread数据结构,那应该首先调用cyg_thread_delete。
线程优先级
参数sched_info给调度器提供额外的信息。确切的细节取决于正在使用的调度器。对于bitmap和mlqueue调度器它是一个小整数,通常在0至31范围内,0是最高优先级。最低的优先级通常只用于系统的空闲线程。优先级的具体数目是由内核配置选项CYGNUM_KERNEL_SCHED_PRIORITIES控制的。
了解在系统的各个线程,包括eCos的包中创建的线程的优先级,并确保所有线程在适当的优先级运行是应用程序开发人员的责任。对于其他一些包中创建的线程,这些包的说明文档中应该指明其必须参数说明。
函数cyg_thread_set_priority,cyg_thread_get_priority,以及cyg_thread_get_current_priority可以用来对线程的优先级进行操作。
栈和栈大小 :
每个线程都需要开辟自己的堆栈存放局部变量,并记录函数的调用与返回。栈一般由调用代码提供,通常以静态数据的形式,这样内核就不需要任何动态内存分配功能。 cyg_thread_create有两个参数是有关栈的,堆栈基址指针和栈的总大小。在许多处理器上堆栈从是由上往下生长的,因此内核将堆栈大小加上基地址,就可以确定栈在内存起始位置。
给线程开辟的堆栈的确切大小依赖于许多因素。最重要的当然是在该线程要执行什么样的代码:如果代码包含了很深的函数嵌套调用调用,递归,或者使用了很大的局部数组,那么堆栈大小需要设置为一个相当大的数值。另外还有一些架构问题,例如CPU的寄存器数目以及函数调用的约定都会对堆栈的使用造成影响。而且根据架构的不同,有些其他代码比如中断处理程序等偶尔会直接运行在当前线程的堆栈。这取决于配置选项,部分比如CYGIMP_HAL_COMMON_INTERRUPTS_USE_INTERRUPT_STACK和CYGSEM_HAL_COMMON_INTERRUPTS_ALLOW_NESTING。
确定应用程序的确切堆栈大小是应用程序开发人员的责任,因为内核无法预先知道一个给定的线程将会运行什么样的代码。但是,eCos系统提供了合理分配堆栈大小一些提示,以两个常数的形式:CYGNUM_HAL_STACK_SIZE_MINIMUM和CYGNUM_HAL_STACK_SIZE_TYPICAL。这些定义放在相应的HAL包中。MINIMUM值适合哪种只运行一个单一功能并只使用很少的系统调用的线程。试图建立一个比这更小线程堆栈是非法的。TYPICAL值使用在嵌套不超过6个或者相似情况,并有在堆栈中不使用大的数组的应用程序中。
如果没有正确的估计堆栈的大小并且堆栈发生了溢出,则可能导致某种形式的内存破坏。而这可很难追查。内核确实包含了一些代码,以帮助检测堆栈溢出,由配置选项CYGFUN_KERNEL_THREADS_STACK_CHECKING控制:在栈顶预留出少量的空间,并用特定的标记填充:每当一个线程上下文切换时就检查这个标记,并如果标记无效了就可以认为(但是不足以充分证明)发生了堆栈溢出。如果系统启用了debugging那么这种堆栈检查时默认开启的。一个相关的配置选项是CYGFUN_KERNEL_THREADS_STACK_MEASUREMENT:启用此选项意味着,一个线程可以调用函数cyg_thread_measure_stack_usage找出到目前为止使用堆栈的最大值。但是要注意一点,这个值可能不一定是真正的最大值,因为,例如,有可能在当前运行时最坏的情况下没有发生中断。
有效上下文 :
cyg_thread_create可以在初始化或者线程切换的时候被调用,在DSR中可能不会被调用到
范例:
下面是一个线程创建的例子。在这里创建五个线程,一个生产者和四个消费者。这些线程在系统的cyg_user_start中创建的:根据配置在别的地方做这个例子可能更合适,比如在main函数中
[cpp] view plaincopy
#include <cyg/hal/hal_arch.h>
#include <cyg/kernel/kapi.h>
// These numbers depend entirely on your application
#define NUMBER_OF_WORKERS 4
#define PRODUCER_PRIORITY 10
#define WORKER_PRIORITY 11
#define PRODUCER_STACKSIZE CYGNUM_HAL_STACK_SIZE_TYPICAL
#define WORKER_STACKSIZE (CYGNUM_HAL_STACK_SIZE_MINIMUM + 1024)
static unsigned char producer_stack[PRODUCER_STACKSIZE];
static unsigned char worker_stacks[NUMBER_OF_WORKERS][WORKER_STACKSIZE];
static cyg_handle_t producer_handle, worker_handles[NUMBER_OF_WORKERS];
static cyg_thread producer_thread, worker_threads[NUMBER_OF_WORKERS];
static void
producer(cyg_addrword_t data)
{
…
}
static void
worker(cyg_addrword_t data)
{
…
}
void
cyg_user_start(void)
{
int i;
cyg_thread_create(PRODUCER_PRIORITY, &producer, 0, "producer",
producer_stack, PRODUCER_STACKSIZE,
&producer_handle, &producer_thread);
cyg_thread_resume(producer_handle);
for (i = 0; i < NUMBER_OF_WORKERS; i++) {
cyg_thread_create(WORKER_PRIORITY, &worker, i, "worker",
worker_stacks[i], WORKER_STACKSIZE,
&(worker_handles[i]), &(worker_threads[i]));
cyg_thread_resume(worker_handles[i]);
}
}
线程的入口指针和C++
在C++中编写的代码,线程的入口函数必须是类的静态成员函数或者是类以外的一个普通函数。它不能是一个类的成员函数,因为这种成员函数隐式的需要一个额外的参数this,内核没法知道这个this参数的值。解决这个问题的方法是利用一个特殊的静态成员函数,例如:
[cpp] view plaincopy
class fred {
public:
void thread_function();
static void static_thread_aux(cyg_addrword_t);
};
void
fred::static_thread_aux(cyg_addrword_t objptr)
{
fred* object = reinterpret_cast<fred*>(objptr);
object->thread_function();
}
static fred instance;
extern "C" void
cyg_start( void )
{
…
cyg_thread_create( …,
&fred::static_thread_aux,
reinterpret_cast<cyg_addrword_t>(&instance),
…);
…
}
这里有效地使用cyg_thread_create的entry_data参数保留了this指针。不幸的是这种方法需要一些C++的使用转换,所以一些本来能够保证的类型安全在C++编程中做不到了。
阅读(3726) | 评论(0) | 转发(0) |