• <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>
    • 軟件測試技術
    • 軟件測試博客
    • 軟件測試視頻
    • 開源軟件測試技術
    • 軟件測試論壇
    • 軟件測試沙龍
    • 軟件測試資料下載
    • 軟件測試雜志
    • 軟件測試人才招聘
      暫時沒有公告

    字號: | 推薦給好友 上一篇 | 下一篇

    Lua: technical note 3

    發布: 2007-7-04 20:48 | 作者: admin | 來源:  網友評論 | 查看: 33次 | 進入軟件測試論壇討論

    領測軟件測試網

    Lua Technical Note 3

    Interfacing Lua to an operating system

    by Gavin Wraith

    This note explains how to extend Lua to take advantage of system calls. Although my own efforts have been confined to an operating system that may be unknown to most readers ( RISC OS), I believe that the principles involved are fairly universal. I write this note in the hope of getting useful criticism. It is an abstract of what I have done in implementing RiscLua.

    RISC OS was designed for a specific family of processors, the ARM. User programs interact with RISC OS only via a specific processor instruction, SWI (SoftWare Interrupt). Every processor has an analogue of this, though doubtless called something different (TRAP?). Using a software interrupt involves the following steps:

    1. Write some processor registers with appropriate data (some of which may be pointers to fixed addresses in the program's memory area).
    2. Call the SWI.
    3. Read some registers.
    In practice, only a subset of processor registers is ever used for passing data between program and operating system, namely R0, R1, ..., R7. All the registers are 32 bits wide. It requires seven instructions of code to produce a C function
    
    extern void swi_call(int swi_number, void * regbuffer);
    
    
    for doing the SWI call. The regbuffer argument points to a 32-byte array for writing and reading the register values. For those who are familiar with the ARM's instruction set, here is the relevant assembler fragment:
    
      swi_call:
                STMFD sp!, {R4-R8,R12,link}
                MOV R12,R0   ; SWI number
                MOV R8,R1    ; base of register values
                LDMIA R8,{R0-R7}          
                SWI &71       ; OS_CallASWIR12
                STMIA R8,{R0-R7}            
                LDMFD sp!, {R4-R8,R12,PC}
                
    
    The following is code for a builtin C function
    
    static int risc_swi (lua_State *L)
    {
      int swinum;
      void *r;
      if (lua_isstring(L,1))
        swinum = swi_str2num(luaL_check_string(L,1));  /* convert string to number */
      else
        if (lua_isnumber(L,1))
           swinum = luaL_check_int(L,1);
        else
          lua_error(L,"swi: arg1 should be a string or a number.");
      if (!lua_isuserdata(L,2))
          lua_error(L,"swi: arg2 should be userdata");
      r = lua_touserdata(L,2);
      swi_call(swinum,r);
      lua_pushnil(L);
      return 1;
    }
    
    
    It defines a Lua function swi for system calls.

    The data written to before and read from the registers after a software interrupt are frequently pointers to fixed addresses in the program's memory area, where various kinds of data may be held. These data may be 32-bit integers, strings or pointers to other fixed buffers. It is necessary that these arrays be fixed, for reasons hidden in the murky past of RISC OS. Each task is responsible for allocating its own message buffer and then it informs the task manager where it is. If the buffer were to be moved, there would be trouble. Since Lua's datatypes are garbage collected, we have to implement these fixed arrays using the userdata type. We assign a particular tag, called "writeable", for userdata pointing to these arrays. Here is C code for a function risc_dim

    
          static int writeable_tag;
          
          static int risc_dim (lua_State *L)
          {
            void *p;
            if ((p = malloc((size_t) luaL_check_int(L,1))) != (void *)0)
                lua_pushusertag(L,p, writeable_tag);
            else
              lua_pushnil(L);
            return 1;   
          }
    
    
    for a builtin lua function dim(n) which produces a userdatum with the writeable tag pointing to a fixed buffer holding n bytes. In addition we need functions to read data from a fixed buffer into a lua variable, and to write data to a fixed buffer from a lua variable. The types of data we have to consider are
    • 32-bit integers to/from lua numbers
    • strings to/from lua strings
    • 32-bit pointers to/from lua userdata
    I omit the details of these conversion functions.

    Of course, the user of RiscLua should be shielded from these details. So I wrap all these functions up as methods for a table

    
    array = function (n)
      local a = {}
      a.n = n -- size of array
      a.b = dim(n) -- bottom of array (address of first byte)
      a.after = { b = disp(a.b,a.n) } -- next byte
      a.words = array_words
      a.chars = array_chars
      a.int = array_int
      a.ptr = array_ptr
      a.strp = array_strp
      a.char = array_char
      a.str = array_str
     return a
     end
    
    
    These methods have values which are global functions named array_xxx. The "words" method is used to read 32-bit values, and the "chars" method to read in 8-bit values. They take tables as arguments, indexed by integers giving offsets into the fixed buffer. The values in the tables can be numbers (for byte values) or strings (for multiple bytes) in the case of chars, and in the case of "words" they can be numbers (for 32-bit integers), C-strings held in a buffer (for pointers to their address), or tables of the kind defined by array (for pointers to buffers). Here is the lua code
    
    array_words = function (self,t)
        if (tag(self.b) ~= writeable) then
           error("words: arg1 not an array") end
        if (type(t) ~= "table") then
           error("words: arg2 must be a table") end
        local fns = {
             number = function (i,v) putword(%self.b,i,v) end,
             table = function (i,v)
                      if (tag(v.b) ~= writeable) then
                         error("words: arg not an array") end
                      putword(%self.b,i,v.b) end,
             string = function (i,v) putword(%self.b,i,str2ptr(v)) end,
             default = function () error("words: bad type") end
                      }
            for i,v in t do
                         if (fns[type(v)]) then
                           fns[type(v)](i,v)
                         else
                            fns.default()
                         end
                        end
         end
         
    array_chars = function (self,t)
                  if (tag(self.b) ~= writeable) then
                     error("chars: arg1 not an array") end
                  if (type(t) ~= "table") then
                     error("chars: arg2 must be a table") end
                  local fns = {
                      number = function (i,v) putbyte(%self.b,i,v) end,
                      string = function (i,v)
                                  local len,k = strlen(v),1
                                  while (k <= len) do
                                      putbyte(%self.b,i,strbyte(v,k))
                                      k = k + 1; i = i + 1;
                                   end
                                end,
                       default = function () error("chars: bad type") end
                             }
                  for i,v in t do
                        if (fns[type(v)]) then
                           fns[type(v)](i,v)
                        else
                           fns.default()
                        end
                               end
       end
    
    
    The functions putword, putbyte are builtin C-functions that do the obvious things. The result is that if we define, say
    
      x,y = array(n),array(m)
    
    
    we can do
    
      x:chars { [0] = "hello".."\0" } -- only 6 bytes taken up so far
      x:words { [2] = a_num, [3] = y }
    
    
    storing a number a_num at bytes 8,9,10,11 and the userdatum y.b at bytes 12,13,14,15 of the fixed buffer pointed to by x.b.

    The other methods are for reading integers, strings and pointers stored in fixed buffers. So x:int(2) should yield the value of a_num again, and x:str(0) should yield "hello". This, I hope, describes the syntax of reading and writing fixed buffers.

    The actual interface to the operating system is given by

    
    swi = {
            regs = array(32),
            call = function (self,x)
                     %swi(x,self.regs.b)
                    end
          }
        
    
    Note how the "call" method hides the raw swi function described above. With array and swi defined in a prelude file, we are in a position to use Lua to exploit everything that the operating system offers. Of course, this prelude is still very low level, but it offers enough to build libraries for writing "wimp" (Windows Icons Menus Pointers) programs that use RISC OS's graphical user interface. Here, as an example of how the system calls can be used, is Lua code to define a function w_task that creates a wimp task:
    
     w_task = function (taskname,version,mesgs)
      assert(type(taskname) == "string", " taskname not a string")
      assert(type(version) == "number", " version not a number")
      assert(type(mesgs) == "table", " mesgs not a table")
      local title = _(taskname)
      local wt = { err = _ERRORMESSAGE,
       title = title,
       action = {}, -- table of action methods indexed by events
       block = array(256), 
       msgs = array(4+4*getn(mesgs)),
       pollword = array(4),  
       poll = function (self,uservar)
         local f,quit
         self.mask = self.mask or 0
         repeat
          swi.regs:words {
           [0] = self.mask,
           [1] = self.block,
           [3] = self.pollword }
          swi:call("Wimp_Poll")
          f = self.action[swi.regs:int(0)]
          if f then quit = f(self,uservar) end
         until quit
         swi.regs:words {
          [0] = self.handle,
          [1] = TASK }
         swi:call("Wimp_CloseDown")
         _ERRORMESSAGE = self.err
        end -- function       
       }             
      wt.msgs:words(mesgs) -- load messages buffer
      swi.regs:words {
       [0] = version,
       [1] = TASK,
       [2] = wt.title,
       [3] = wt.msgs }
      swi:call("Wimp_Initialise")
      wt.handle = swi.regs:int(1)
      _ERRORMESSAGE = function (errm)  -- set error handler
        local b = %wt.block
        b:words { [0] = LUA_ERROR }
        b:chars { [4] = errm .."\0" }
        swi.regs:words { [0] = b, [1] = 16, [2] = %title }
        swi:call("Wimp_ReportError")   
      end -- function
      return wt
     end -- function  
    
    
    Once a wimp task has been initialised and has set up its data it goes to sleep by calling the "poll" method, handing over execution to the task manager in the RISC OS kernel. When the task manager wakes it up again it puts an event code in register R0. The lines
    
     f = self.action[swi.regs:int(0)]
          if f then quit = f(self,uservar) end
    
    
    show that the task responds by executing an action method indexed by the returned event code. This is how the non-preemptive multitasking of RISC OS works. When the task is initialised it sets up its own error handler to output error messages in a window, and before closing down it restores the previous error handler. Using the w_task function, and similar library functions for loading templates for windows and menus, all the programmer has to do is define handler methods for events, e.g.
    
      mytask = w_task("MyTask",310, { [0] = M_DataLoad, [1] = M_Quit })
      .....................
        
      mytask.action[Mouse_Click] = function (self) ........ end
      .....................
                                     
      mytask:poll()
    
    
    Although the examples contain detail that will not mean much to those unfamiliar with RISC OS, the basic principles should be much the same for other platforms:
    • a C function for system calls,
    • a C function for allocating buffers,
    • C functions for reading and writing Lua variables to buffers,
    • wrapper objects in Lua concealing these functions as methods,
    • constructor functions using system calls to create task objects.

    延伸閱讀

    文章來源于領測軟件測試網 http://www.kjueaiud.com/


    關于領測軟件測試網 | 領測軟件測試網合作伙伴 | 廣告服務 | 投稿指南 | 聯系我們 | 網站地圖 | 友情鏈接
    版權所有(C) 2003-2010 TestAge(領測軟件測試網)|領測國際科技(北京)有限公司|軟件測試工程師培訓網 All Rights Reserved
    北京市海淀區中關村南大街9號北京理工科技大廈1402室 京ICP備10010545號-5
    技術支持和業務聯系:info@testage.com.cn 電話:010-51297073

    軟件測試 | 領測國際ISTQBISTQB官網TMMiTMMi認證國際軟件測試工程師認證領測軟件測試網

    老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月

  • <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>