创建线程create_thread()
创建进程可参考l4ka-pistachio-cb2d287364bc/user/apps/l4test/threads.cc:
create_thread (bool new_space, int cpu)
{
static L4_Fpage_t kip_area, utcb_area;
static L4_Word_t utcb_base;
static bool initialized = false;
static void kip;
if (! initialized)
{
kip = L4_KernelInterface ();
// Put the kip at the same location in all address spaces
// to make sure we can reuse the syscall jump table.
kip_area = L4_FpageLog2 ((L4_Word_t) kip, L4_KipAreaSizeLog2 (kip));
L4_ThreadId_t mylocalid = L4_MyLocalId ();
utcb_base = (L4_Word_t ) &mylocalid;
utcb_base &= ~(L4_UtcbAreaSize (kip) - 1);
// Make room for at least 1000 threads in the UTCB area
utcb_area = L4_Fpage (utcb_base, L4_UtcbSize (kip) 1000);
initialized = true;
}
如果没有初始化,会先获得kip、kip_area、utcb_base、utcb_area这四个变量,后面创建thread时会用到。
在IA-32体系下,L4_MylocalId()返回的local_id正好是当前thread的utcb起始地址(类似Linux将sp设置为task struct末端),gs:[0]指向UTCB address,偏移 -60 的位置存储的是 MyGlobalId,参见“L4 eXperimental Kernel Reference Manual (Version X.2)A.1 Virtual Registers(TCRS)”。
L4的UTCB有点类似Windows中的TEB(Thread Environment Block),先预留一片空间,每当新创建线程时,在这片空间(看作UTCB或TEB结构体的数组)中分配一项。
接下来要分配新的tid号:
L4_ThreadId_t me = L4_Myself ();
L4_ThreadId_t tid = get_new_tid ();
get_new_tid (void)
{
static L4_Word_t next_no = 0;
if (next_no == 0)
// Initialize with my thread_no + 1
next_no = L4_ThreadNo (L4_MyGlobalId ()) + 1;
return L4_GlobalId (next_no++, 1);
}
可以看到新分配的tid号一定会比当前roottask的GlobalId号大,这是L4内核的规定,参考“L4 eXperimental Kernel Reference Manual (Version X.2)2.1 ThreadId”
tid得到后,计算新线程的utcb地址,这样计算所有线程的utcb决不会重叠:
L4_Word_t utcb_location =
utcb_base + L4_UtcbSize (kip) L4_ThreadNo (tid);
如果创建的线程将运行于新的address space,将分三步走。第一次L4_ThreadControl时内核将创建一个address space为空的线程结构体,然后L4_SpaceControl为该thread指定kip和utcb,最后第二次L4_ThreadControl让该线程变为active状态。
if (new_space)
{
// Create inactive thread
int res = L4_ThreadControl (tid, tid, me, L4_nilthread, (void ) -1);
if (res != 1)
printf ("ERROR: ThreadControl returned %dn", res);
L4_Word_t control;
res = L4_SpaceControl (tid, 0, kip_area, utcb_area,
L4_nilthread, &control);
if (res != 1)
printf ("ERROR: SpaceControl returned %dn", res);
// Activate thread
res = L4_ThreadControl (tid, tid, me, me, (void ) utcb_location);
if (res != 1)
printf ("ERROR: ThreadControl returned %dn", res);
}
如果线程运行在同一个address space,工作更简单
else
{
// Create active thread
int res = L4_ThreadControl (tid, me, me, me, (void ) utcb_location);
if (res != 1)
printf ("ERROR: ThreadControl returned %dn", res);
}
最后设置一下运行的CPU,一个thread就创建好了
if (cpu != -1)
L4_Set_ProcessorNo (tid, cpu);
return tid;
}
运行线程start_thread()
void
start_thread( L4_ThreadId_t tid, L4_Word_t ip, L4_Word_t sp )
{
L4_Msg_t msg;
L4_Clear( &msg );
L4_Append( &msg, ip );
L4_Append( &msg, sp );
L4_Load( &msg );
L4_Send( tid );
}
运行一个线程只需向该线程发送一个ipc,ipc参数包括线程运行时的入口点ip、堆栈sp
注意:这个ipc必须由待运行线程的pager发送才有效!
一个简单的example
L4_ThreadId_t thread1;
L4_ThreadId_t thread2;
int n_switch;
void func1()
{
L4_Clock_t usec1;
L4_Clock_t usec2;
printf("start thread1n");
// L4_Sleep (L4_TimePeriod (10001000));
usec1 = L4_SystemClock();
for(int i = 0; i < 300; i++)
L4_ThreadSwitch(thread2);
usec2 = L4_SystemClock();
printf("usec1 %un", usec1.raw);
printf("usec2 %un", usec2.raw);
printf("delta %un", usec2.raw - usec1.raw);
printf("average thread switch time: %fn", (double)(usec2.raw - usec1.raw) / 2000);
printf("n_switch: %dn", n_switch);
}
void func2()
{
printf("start thread2n");
while(true)
{
// L4_ThreadSwitch(L4_nilthread);
L4_ThreadSwitch(thread1);
n_switch++;
}
}
int main()
{
thread1 = create_thread(false, 0);
thread2 = create_thread(false, 0);
n_switch = 0;
printf("in mainn");
// Touch the memory to make sure we never get pagefaults
extern L4_Word_t _end, _start;
for (L4_Word_t x = (&_start); x < &_end; x++)
{
volatile L4_Word_t q;
q = (volatile L4_Word_t) x;
}
start_thread(thread2, func2);
start_thread(thread1, func1);
L4_Sleep(L4_Never);
}end