客户端请求
在root task中通常会先将地址空间的所有页面从sigma0中分配出来,这样以后创建的thread运行时,若没有指定pager,仍然可以正常运行。
具体的请求页面操作如下所示:
// ** pin all code and data of the root task by requesting the pages from sigma0
for (const char addr = &elf_start; addr < &elf_end; addr += pagesize)
{
if (!request_page((L4_Word_t)addr)) {
panic("root: could not get roottask pages from sigma0.");
}
}
当然,也可以通过系统自身的缺页机制:
// 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;
}
static inline L4_Bool_t request_page(L4_Word_t addr)
{
return !(L4_IsNilFpage( L4_Sigma0_GetPage(sigma0id, L4_Fpage(addr, pagesize)) ) );
}
request_page会调用 L4_Sigma0_GetPage(sigma0id, L4_Fpage(addr, pagesize)) ),向sigma0请求起始地址为addr,大小为pagesize的页面。
L4_INLINE L4_Fpage_t L4_Sigma0_GetPage (L4_ThreadId_t s0, L4_Fpage_t f)
{
return L4_Sigma0_GetPage_RcvWindow (s0, f, L4_CompleteAddressSpace);
}
L4_Sigma0_GetPage_RcvWindow()指定receive windows为L4_CompleteAddressSpace,即当前线程的全部地址空间,向sigma0发送并等待ipc消息:
L4_INLINE L4_Fpage_t L4_Sigma0_GetPage_RcvWindow (L4_ThreadId_t s0,
L4_Fpage_t f,
L4_Fpage_t RcvWindow)
{
L4_MsgTag_t tag;
L4_Msg_t msg;
if (L4_IsNilThread (s0))
{
L4_KernelInterfacePage_t kip = (L4_KernelInterfacePage_t )
L4_GetKernelInterface ();
s0 = L4_GlobalId (kip->ThreadInfo.X.UserBase, 1);
}
L4_MsgClear (&msg);
L4_MsgAppendWord (&msg, f.raw);
L4_MsgAppendWord (&msg, 0);
L4_Set_MsgLabel (&msg, (L4_Word_t) -6UL << 4);
L4_MsgLoad (&msg);
L4_Accept (L4_MapGrantItems (RcvWindow));
tag = L4_Call (s0);
if (L4_IpcFailed (tag))
return L4_Nilpage;
L4_StoreMR (2, &f.raw);
return f;
}
L4_MapGrantItems() 返回一个类型为 L4_Acceptor_t 的 acceptor,它指定了Receive Windows的大小
L4_Accept()把fpage结构体写入到BR0(Buffer Register 0)中
参见L4_User_Manual-x24.4. SHARING MEMORY — MEMORY MAPPING
关于GrantItem参见“L4 eXperimental Kernel Reference Manual (Version X.2)5.3 GrantItem”
当L4_Call()返回时,内核已经完成了page的映射(可以理解为设置好了page table中的项)。
服务端响应
对应的服务端代码在serv/sigma0/sigma0.cc中:
extern "C" void sigma0_main (void)
{
…
for(;;)
{
case L4_SIGMA0_RPC:
{
fpage.raw = L4_Get (&msg, 0);
L4_Word_t addr = L4_Address (fpage);
L4_Word_t attributes = L4_Get (&msg, 1);
…
allocate_page (tid, addr, L4_SizeLog2 (fpage), map));
…
}
…
L4_Put (&msg, 0, 0, (L4_Word_t ) 0, 2, &map);
L4_Load (&msg);
tag = L4_ReplyWait (tid, &tid);
}
}
sigma0_main()如果接受到了L4_SIGMA0_RPC ipc,会分配page:
bool allocate_page (L4_ThreadId_t tid, L4_Word_t addr, L4_Word_t log2size,
L4_MapItem_t & map, bool only_conventional)
{
memregion_t * r;
L4_Fpage_t fp;
…
// Try allocating from one of the memory pools.
for (L4_Word_t i = 0; pools[i] != NULL; i++)
{
pools[i]->reset ();
while ((r = pools[i]->next ()) != NULL)
{
if (r->low > addr_high || r->high < addr)
continue;
fp = r->allocate (addr, log2size);
if (! L4_IsNilFpage (fp))
{
map = L4_MapItem (fp, addr);
alloc_pool.insert
(new memregion_t (addr, addr_high, tid));
return true;
}
}
}
…
}
其中的map = L4_MapItem(fp, addr)返回类型为L4_MapItem_t,它指定返回页面将被映射到请求线程的起始地址为addr的地方,参见L4_User_Manual-x2____4.4. SHARING MEMORY — MEMORY MAPPING
sigma0发送响应ipc后,内核会将所有的映射工作做好。end