Wince读核1-启动流程5

4. PSL和API调用           
接下来这部分资料有些翻译自网络上的文章,大部分是我自己的理解。我们知道ce的api是由几个server进程实现的,包括filesys.exe,gwes.exe,device.exe和services.exe,当然,还有内核nk.exe。当一个进程调用某个api时,调用者的线程通常会"跳进" server进程中执行。这是怎么做到的呢?
大部分ce的api是由coredll.dll导出的。所有的ce应用程序都会链接到这个dll。当一个进程调用某个api,比如GetTickCount()时,它调用的是coredll.dll导出的GetTickCount()。查看coredll.def文件将会发现这么一行,
     GetTickCount=xxx_GetTickCount          
这意味着我们调用的导出函数GetTickCount()在coredll.dll中的实现是函数xxx_GetTickCount()。而xxx_GetTickCount()的实现很简单,仅仅是一个类似如下的封装,
      DWORD xxx_GetTickCount()
      {
        return GetTickCount();
      }
  
要注意的是,这个实现在一般授权情况下获得的ce源代码中是看不到的,查看源代码目录/PRIVATE/WINCEOS/COREOS/CORE下的dir文件会发现当前目录下缺少很多子目录。据说上面的实现就在其中的thunks目录下。MS之所以不公开这些相对来说并不重要的源代码,我想主要原因是安全上的考虑,因为coredll.dll是ce法定的访问内核和其它server进程的唯一途径。
那么上面coredll.dll调用的GetTickCount()又是在哪里实现的呢?在/public/COMMON/OAK/INC/mkfuncs.h中,有如下定义,
     #define GetTickCount WIN32_CALL(DWORD, GetTickCount, (VOID))
在/public/COMMON/OAK/INC/psyscall.h中,有如下定义,
      #define WIN32_CALL(type, api, args)   IMPLICIT_DECL(type, SH_WIN32, W32_ ## api, args)
      #define IMPLICIT_DECL(type, hid, mid, args) (*(type (*)args)IMPLICIT_CALL(hid, mid))
      #define IMPLICIT_CALL(hid, mid) (FIRST_METHOD - ((hid)<<HANDLE_SHIFT | (mid))*APICALL_SCALE)
      #define FIRST_METHOD 0xF0010000
      #define HANDLE_SHIFT 8
      #define APICALL_SCALE 4
      #define W32_GetTickCount   13

在/public/COMMON/SDK/INC/kfuncs.h中,有如下定义,    
     #define SH_WIN32                0
至此,我们可以把GetTickCount()展开为,
      (*(DWORD (*)(VOID))(0xF0010000 - ((0)<<8 | (13))*4))  
这是一个函数指针,指向地址0xF000FFCC。当调用函数xxx_GetTickCount()的时候,最终会跳转到地址0xF000FFCC上执行。这里用到了一个巧妙的小技巧,也就是所谓的PSL,即protected server libraries。下面可以说明为什么在这里插进上面这一段看似无关的文字原因了。因为在上一篇叙述到的代码之后,我们看到如下的代码,           
      ; Set up page table entry for PSL calls to turn the pre-fetch abort into a permission fault rather
      ; than a translation fault. This speeds up the time to execute a PSL call, as this entry can be
      ; cached in the TLB
      ;
      ;       (r10) = ptr to first level page table
      ;为PSL设置页表项。(0xF0000000/1M)*4=0x3C00,所以r0保存的页表项的物理地址对应虚拟地址0xF0000000。
      ;描述符不必设置page的物理地址,因为我们根本就不打算访问到这个物理地址,
      ;我们设置AP permission为内核和用户皆不可访问,意味着访问这个虚拟地址的时候,会发生Prefetch Abort。
      ;而在Prefetch Abort的处理中会继续处理PSL。
             add     r0, r10, #0x3C00                ; Page table entry for 0xF0000000 -> 0xF0100000
             mov     r1, #PTL1_SECTION + PTL1_XN     ; Level 1 Section, with Cachable/bufferable, access
                                                     ; bits and phys address set to zero
      ;; Because the CP15 R1 R bit is set, there are no unreadable settings via the AP permission bits.
             orr     r1, r1, #0x1E0                  ; Set Domain to 15 (to cause domain access fault)
                                                     ; Domain access is set up below..
             str     r1, [r0]                        ; Store the level 1 PTE

在上面这段代码中,还要注意并没有为这个1M的section设置C+B。个人觉得应该设置C+B,在注释里也说明设置了这个属性,但是并没有在代码里表现出来。难道是MS的笔误?设置这个属性可以减少一次内存访问,由于PSL频繁被调用,总的节省的时间是很可观的。由于PSL接下来的处理比较复杂,将在以后叙述。

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值