L4:thread creation


创建线程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