Plugs紹介----Net/C#オープンソースオペレーティングシステム学習シリーズ6

44091 ワード

原文住所:http://www.codeproject.com/KB/cs/CosmosPlugs.aspx
紹介する
この文章はCOSMOSの中でどのようにWINDOWS API呼び出しと内部呼び出しを実現するかを示す.Netコード.また、COSMOS、アセンブリ、またはX#言語を使用してハードウェアと直接対話する方法も含まれています.
コスモスって何?
Cosmosは、開発環境としてVisual Studioを使用するオペレーティングシステム開発ツール(development kit)です.それでも、いかなる根拠も.Netの言語、VBを含みます.NET、Fortran、Delphi Prism、IronPython、F#などが開発に利用できます.Cosmos本省もカーネル運転もC#で書かれているのでCosmosと名付けられました.しかも、NOSMOS(.NET Open Source Management Operating System)はバカに聞こえる.
Cosmosは伝統的なオペレーティングシステムではなく、オペレーティングシステムツール(Operating System Kit)と呼ぶべきか、私が言ったように「Operating System Legos」と呼ぶべきか(どのように翻訳するか分からないが、へへへ).Cosmosでは、Visual StudioとC#を使用してアプリケーションを作成するようにオペレーティングシステムを作成できます.ほとんどのユーザーは、数分以内に独自のオペレーティングシステムを自分で書き込み、起動することができます.これらはすべてVisual Studioで完了できます.Cosmosは、Visual Studioと統合されたプロジェクトタイプ、デバッガ、ブレークポイントツール、およびそれを観察する(watchers)などを提供します.C#またはVBをデバッグできます.NETアプリケーションのようにオペレーティングシステムをデバッグします.
どんな場合にPlugsが必要か
以下の3つのシーンでPlugsを使用する必要があります.
1.内部コール(Internal Calls)
2. P/Invoke
3.ダイレクトアセンブリ
内部呼び出しとP/Invokeの場合
はい.Netフレームワークが提供する各種クラスのうち、一部は使用しない.Netコードで実現されるのではなく、使用されるローカルコードです.このような理由は2つあります.
1.実現される方法はWindows APIに依存する(P/Invokeの場合)
2.実現方法は高度に最適化されたC++に依存するか、または.Net実行時のアセンブリコード(内部呼び出しの場合)
P/Invokeは、既存のWindow encryption APIにアクセスし、ネットワークにアクセスし、他の類似の機能にアクセスするためにスクリーンに図を描くために使用されます.
内部呼び出しには直接アクセスが必要である.Net実行時のクラスで使用します.たとえば、メモリ管理のクラスに直接アクセスしたり、速度が要求されたりします.MathのようにPowメソッドは内部呼び出しを使用します.
Plugsは、C#、アセンブリ、または任意のものを用いることができる.Net言語で実現します.
ダイレクトアセンブリの場合
ハードウェアと直接会話する場合、CosmosはPICバス、CPU IOバス、メモリなどと対話できる必要があります.アクセスメモリでは、非タイプのセキュリティポインタ(unsafe pointers)がよく使用されますが、場合によっては自分でアセンブリコードを手書きで作成する必要があります.Plugsはインタフェースに相当し、C#へのアセンブリコードへの直接アクセスを提供し、アセンブリ呼び出しへのアクセスをC#コードのメソッド呼び出しのようにする.
CosmosにX 86アセンブリを書く
クラスをCosmosで使用してX 86アセンブリを実装できます


 
 
View Code
new  Move(Registers.DX, (xComAddr  +   1 ).ToString());
new  Move(Registers.AL,  0 .ToString());
new  Out( " dx " " al " ); //  disable all interrupts
new  Move(Registers.DX, (xComAddr  +   3 ).ToString());
new  Move(Registers.AL,  0x80 .ToString());
new  Out( " dx " " al " ); //   Enable DLAB (set baud rate divisor)
new  Move(Registers.DX, (xComAddr  +   0 ).ToString());
new  Move(Registers.AL,  0x1 .ToString());
new  Out( " dx " " al " ); //       Set diviso (low byte)
new  Move(Registers.DX, (xComAddr  +   1 ).ToString());
new  Move(Registers.AL,  0x00 .ToString()); 
new  Out( " dx " " al " ); //   //  set divisor (high byte)   
  

しかし、Cosmosはより高いレベルの呼び出しX#もサポートしています.X#は、X 86アセンブリに直接対応する安全なアセンブリ言語です.X#は以下の通りです.


 
 
UInt16 xComStatusAddr  =  (UInt16)(aComAddr  +   5 ); 
Label 
=   " WriteByteToComPort " ;
Label 
=   " WriteByteToComPort_Wait " ;
 
DX 
=  xComStatusAddr;
AL 
=  Port[DX];
AL.Test(
0x20 );
JumpIfEqual(
" WriteByteToComPort_Wait " );
DX 
=  aComAddr;
AL 
=  Memory[ESP  +   4 ];
Port[DX] 
=  AL;
Return(
4 );
Label 
=   " DebugWriteEIP " ;
AL 
=  Memory[EBP  +   3 ];
EAX.Push();
Call
< WriteByteToComPort > ();
AL 
=  Memory[EBP  +   2 ];
EAX.Push();
Call
< WriteByteToComPort > ();
AL 
=  Memory[EBP  +   1 ];
EAX.Push();
Call
< WriteByteToComPort > ();
AL 
=  Memory[EBP];
EAX.Push();
Call
< WriteByteToComPort > ();
                Return();  

Plugsの実装を開始
まず私たちのplugが何をするかを決めなければなりません.例えば、Math.Abs(double)は内部呼び出しを実現するために使用される


 
 
.method  public  hidebysig  static  float64 Abs(float64  ' value ' ) cil managed internalcall
{
    .custom instance 
void  System.Security.SecuritySafeCriticalAttribute::.ctor()

この方法を直接使用してCosmosに対応するplugがない場合、コンパイラは「plug needed」エラーを発生します.IL 2 CPUではILコードがないのでX 86コードにコンパイルします.したがって、「plug needed」エラーは、内部呼び出しまたはP/Invokeに依存する方法が必要であることを意味します.そうしないと、Cosmosはコンパイルできません.
MathでPowのこの例では、Cosmosのカーネルには、コンパイル時に自動的に呼び出されるplugが含まれているため、コンパイル可能である.
コンパイラは、実行時にplugを使用して実際のコードを置き換えます.Plugで提供されるコードは、CosmosがWindowsまたはCLRの下で実行されていないため、Cosmosで直接使用できない内部呼び出しおよびWINDOWS APIの呼び出しを置き換えるために使用される.Plugはこのような強制埋め込みと置換のフォーマットである(It’s a form of forced inlining and replacement)
Plugを作成するには、新しいクラスを作成する必要があります.カーネル内のPlugは個々の個別のプログラムセット(assemblies)として作成され、カーネルによって個別に参照される.これにより、IL 2 CPUはplugを含み、使用することができる.


 
 
[Plug(Target  =   typeof ( global ::System.Math))]    
public   class  MathImpl  {
        
public   static   double  Abs( double  value)  {
            
if  (value  <   0 ) {
                
return   - value;
            } 
else  {
                
return  value;
            }
        } 

ここでは1つの方法を示したが,実際にはPlugクラスには複数の方法が含まれる.この例ではPlugの属性(attribute)が重要な要素である.IL 2 CPUというplugはSystemを置き換えるために使われていると伝えています.Mathクラスのメソッド.そしてIL 2 CPUはSystemと探しに行きます.Mathのメソッドに対応するメソッドを交換します.
ダイレクトアセンブリplug
ダイレクトアセンブリのplugは、C#ダイレクトとX 86アセンブリのインタラクションを実行するために使用されるコードである.例えば、IOPortクラスは、デバイスドライバがハードウェアデバイスと通信する必要があるときにCPUバスに直接アクセスすることを可能にする.
まず、空のC#クラスを作成し、置き換えられる空のメソッドを作成します.この置換メソッドの戻りタイプがVOIDでない場合、plugのメソッドは、C#コンパイラがコンパイルできるように任意に値を返す必要があります.それでも、plugは置換されたターゲットメソッドを無視し、plugで実装されたメソッドで元のメソッドを置換するため、このメソッドの戻り値は使用されません.


 
 
public   abstract   class  IOPortBase  {
        
public   readonly  UInt16 Port;
 
        
//  all ctors are internal - Only Core ring can create it.. but hardware ring can use it.
         internal  IOPortBase(UInt16 aPort)
        {
            Port 
=  aPort;
        }
        
internal  IOPortBase(UInt16 aBase, UInt16 aOffset)
        {
            
//  C# math promotes things to integers, so we have this constructor
            
//  to relieve the use from having to do so many casts
            Port  =  (UInt16)(aBase  +  aOffset);
        }
 
        
// TODO: Reads and writes can use this to get port instead of argument
         static   protected   void  Write8(UInt16 aPort,  byte  aData) { }  //  Plugged
         static   protected   void  Write16(UInt16 aPort, UInt16 aData) { }  //  Plugged
         static   protected   void  Write32(UInt16 aPort, UInt32 aData) { }  //  Plugged
 
        
static   protected   byte  Read8(UInt16 aPort) {  return   0 ; }  //  Plugged
         static   protected  UInt16 Read16(UInt16 aPort) {  return   0 ; }  //  Plugged
         static   protected  UInt32 Read32(UInt16 aPort) {  return   0 ; }  //  Plugged  

ご覧のように「Write」メソッドは空ですが、「Read」メソッドには名目上の戻り値が必要です.
このクラスはコードで置き換えられます.


 
 
using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Text;
using  Cosmos.IL2CPU.Plugs;
using  Assembler  =  Cosmos.Compiler.Assembler.Assembler;
using  CPUx86  =  Cosmos.Compiler.Assembler.X86;
 
namespace  Cosmos.Core.Plugs
{
    [Plug(Target 
=   typeof (Cosmos.Core.IOPortBase))]
    
public   class  IOPortImpl
    {
        [Inline]
        
public   static   void  Write8(UInt16 aPort,  byte  aData)
        {
            
// TODO: This is a lot of work to write to a single port.
            
//  We need to have some kind of inline ASM option that can
            
//  emit a single out instruction
             new  CPUx86.Move { DestinationReg  =  CPUx86.Registers.EDX, SourceReg  =  CPUx86.Registers.EBP, SourceDisplacement  =   0x0C , SourceIsIndirect  =   true  };
            
new  CPUx86.Move { DestinationReg  =  CPUx86.Registers.EAX, SourceReg  =  CPUx86.Registers.EBP, SourceDisplacement  =   0x08 , SourceIsIndirect  =   true  };
            
new  CPUx86.Out { DestinationReg  =  CPUx86.Registers.AL };
        }
 
        [Inline]
        
public   static   void  Write16(UInt16 aPort, UInt16 aData)
        {
            
new  CPUx86.Move { DestinationReg  =  CPUx86.Registers.EDX, SourceReg  =  CPUx86.Registers.EBP, SourceIsIndirect  =   true , SourceDisplacement  =   0x0C  };
            
new  CPUx86.Move { DestinationReg  =  CPUx86.Registers.EAX, SourceReg  =  CPUx86.Registers.EBP, SourceIsIndirect  =   true , SourceDisplacement  =   0x08  };
            
new  CPUx86.Out { DestinationReg  =  CPUx86.Registers.AX };
        }
 
        [Inline]
        
public   static   void  Write32(UInt16 aPort, UInt32 aData) 
        {
            
new  CPUx86.Move { DestinationReg  =  CPUx86.Registers.EDX, SourceReg  =  CPUx86.Registers.EBP, SourceIsIndirect  =   true , SourceDisplacement  =   0x0C  };
            
new  CPUx86.Move { DestinationReg  =  CPUx86.Registers.EAX, SourceReg  =  CPUx86.Registers.EBP, SourceIsIndirect  =   true , SourceDisplacement  =   0x08  };
            
new  CPUx86.Out { DestinationReg  =  CPUx86.Registers.EAX };
        }
 
        [Inline]
        
public   static   byte  Read8(UInt16 aPort)
        {
            
new  CPUx86.Move { DestinationReg  =  CPUx86.Registers.EDX, SourceReg  =  CPUx86.Registers.EBP, SourceIsIndirect  =   true , SourceDisplacement  =   0x08  };
            
// TODO: Do we need to clear rest of EAX first?
            
//     MTW: technically not, as in other places, it _should_ be working with AL too..
             new  CPUx86.Move { DestinationReg  =  CPUx86.Registers.EAX, SourceValue  =   0  };
            
new  CPUx86.In { DestinationReg  =  CPUx86.Registers.AL };
            
new  CPUx86.Push { DestinationReg  =  CPUx86.Registers.EAX };
            
return   0 ;
        }
 
        [Inline]
        
public   static  UInt16 Read16(UInt16 aPort)
        {
            
new  CPUx86.Move { DestinationReg  =  CPUx86.Registers.EDX, SourceReg  =  CPUx86.Registers.EBP, SourceIsIndirect  =   true , SourceDisplacement  =   0x08  };
            
new  CPUx86.Move { DestinationReg  =  CPUx86.Registers.EAX, SourceValue  =   0  };
            
new  CPUx86.In { DestinationReg  =  CPUx86.Registers.AX };
            
new  CPUx86.Push { DestinationReg  =  CPUx86.Registers.EAX };
            
return   0 ;
        }
 
        [Inline]
        
public   static  UInt32 Read32(UInt16 aPort)
        {
            
new  CPUx86.Move { DestinationReg  =  CPUx86.Registers.EDX, SourceReg  =  CPUx86.Registers.EBP, SourceIsIndirect  =   true , SourceDisplacement  =   0x08  };
            
new  CPUx86.In { DestinationReg  =  CPUx86.Registers.EAX };
            
new  CPUx86.Push { DestinationReg  =  CPUx86.Registers.EAX };
            
return   0 ;
        }
    }

注意:この例のコード(アセンブリのように見えるコードを指す-訳者注)はX#コードではありません.私たちの古いplugは古い文法を使って書いています.
C#を使用してIOPortというクラスに直接アクセスできるplugがあります.次の例はATAクラスから抜粋します


 
 
public   override   void  ReadBlock(UInt64 aBlockNo, UInt32 aBlockCount,  byte [] aData) {
      CheckDataSize(aData, aBlockCount);
      SelectSector(aBlockNo, aBlockCount);
      SendCmd(Cmd.ReadPio);
      IO.Data.Read8(aData);
    }
 

その他のplugの例
BCL(Binary Classes Library?.netのフレームワークで提供されるクラス)では、Consoleクラスで使用される内部呼び出しの一部は通常、WINDOWS APIに使用されます.各呼び出し階層を個別に置き換える必要はありません.WINDOWS API呼び出しに直接マッピングする必要はありません.より高い呼び出し階層ツリーでTextScreenクラスを呼び出すだけで、これらの方法を完全に置き換えることができます.(We don't need to plug only the methods that directly map to Windows API calls, but instead we plug methods much higher up the tree and completely replace the implementation to call our TextScreen class instead.)


 
 
namespace  Cosmos.System.Plugs.System.System {
    [Plug(Target 
=   typeof ( global ::System.Console))]
    
public   static   class  ConsoleImpl {
 
        
private   static  ConsoleColor mForeground  =  ConsoleColor.White;
        
private   static  ConsoleColor mBackground  =  ConsoleColor.Black;
 
        
public   static  ConsoleColor get_BackgroundColor() {
            
return  mBackground;
        }
 
        
public   static   void  set_BackgroundColor(ConsoleColor value) {
            mBackground 
=  value;
            Cosmos.Hardware.Global.TextScreen.SetColors(mForeground, mBackground);
        }   

(翻訳はこれで終わります.レベルが限られています.ご指導を歓迎します)