Plugs介绍(翻译)---- .net/C#开源操作系统学习系列六

2023-10-14 09:30

本文主要是介绍Plugs介绍(翻译)---- .net/C#开源操作系统学习系列六,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

原文地址:http://www.codeproject.com/KB/cs/CosmosPlugs.aspx

介绍

这篇文章将展示在COSMOS中如何实现基于WINDOWS API调用和内部调用的.net代码。另外,也包含了如何使用COSMOS,汇编或者X#语言与硬件直接进行交互。

Cosmos是什么

Cosmos一个使用Visual Studio作为开发环境的操作系统开发工具(development kit)。尽管如此,任何基于.net的语言,包括VB.NET,Fortran,Delphi Prism,IronPython,F#等等都可以用来进行开发。Cosmos本省和内核运行都是使用C# 来写的,所以命名为Cosmos。而且,NOSMOS(.NET Open Source Managed Operating System)听起来太蠢了。

Cosmos不是传统意义上的操作系统,它更应该叫做操作系统工具(Operating System Kit) ,或者正如我所说的叫做“Operating System Legos”(不知如何翻译,嘿嘿)。Cosmos让您能像使用Visual Studio和C#创建应用程序一样创建操作系统。大部分用户可以在几分钟之内自己写和引导一个他们自己的操作系统,所有这些都可以在Visual Studio中完成。Cosmos提供了与Visual Studio集成的项目类型、调试器、断点工具和观察其(watchers)等。你可以向调试你的C#或者VB.NET应用程序一样调试你的操作系统。

什么情况下需要Plugs

在以下三种场景中需要用到Plugs:

1. 内部调用(Internal Calls)

2. P/Invoke

3. 直接汇编

内部调用和P/Invoke的情况

在.net框架提供的各种类中,有一部分不是使用.net代码来实现的,而是使用的本地代码。这样做的原因有两:

1. 被实现的方法依赖于Windows API(P/Invoke的情况)

2.被实现的方法依赖于高度优化过的C++或者在.net运行时中的汇编代码(内部调用的情况)

P/Invoke被用来往屏幕上画图,访问已存在的Window encrypition API,访问网络和其他一些类似的功能。

内部调用被需要直接访问.net运行时的类来使用。比如直接访问内存管理的类,或者对速度有要求的情况。如Math.Pow方法就是使用的内部调用。

Plugs可以使用C# ,汇编或者任何.net语言来实现。

直接汇编的情况

对于直接与硬件进行会话,Cosmos必须能够和PIC总线,CPU IO总线,内存等进行交互。访问内存经常使用的是非类型安全的指针(unsafe pointers),尽管如此,在某些情况下还是得自己手写汇编代码。Plugs相当于接口,提供给C#直接访问汇编代码,使得对汇编调用的访问就像C#代码的方法调用一样。

在Cosmos中写X86汇编

可以在Cosmos中使用类来实现X86汇编

 
ExpandedBlockStart.gif 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#是一种类型安全的直接与X86汇编对应的汇编语言。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”错误。因为在IL2CPU没有IL代码把它编译为X86代码。所以“plug needed”错误的意思是你需要一些依赖内部调用或者P/Invoke的方法,否则Cosmos将无法编译。

在Math.Pow这个例子中,是可以通过编译的因为Cosmos的内核已经包含了一个在编译的时候会自动被调用的plug.

编译器在运行时中使用plug来替换实际的代码。Plug中提供的代码被用来替换对内部调用和WINDOWS API的调用,这些调用无法在Cosmos中直接使用因为Cosmos不是运行在Windows或者CLR下面的。Plug就是这么一个强制嵌入和替换的格式(It’s a form of forced inlining and replacement)

为了创建一个Plug,我们需要创建一个新的类。在内核中的Plug被创建为各个单独的程序集(assemblies)并且被内核单独引用。这样允许IL2CPU包含和使用plug。

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

虽然这里是显示了一个方法,但其实Plug类能包括多个方法。在这个例子中Plug的属性(attribute)是关键因素。它告诉IL2CPU这个plug用来替换System.Math类中的方法。然后IL2CPU便会去找与System.Math中方法对应的方法,并把他们给换掉。

直接汇编的plug

直接汇编的plug是被用来运行C#直接和X86汇编进行交互的代码。比如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任然是使用比较老的语法来写的。

现在我们有了一个plug,我们可以使用C#直接访问IOPort这个类。下面这个例子摘自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);
        }   

(翻译就到此结束了,水平有限,欢迎大家指点)

转载于:https://www.cnblogs.com/li0803/archive/2011/08/11/2134369.html

这篇关于Plugs介绍(翻译)---- .net/C#开源操作系统学习系列六的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/209631

相关文章

C#实现高性能Excel百万数据导出优化实战指南

《C#实现高性能Excel百万数据导出优化实战指南》在日常工作中,Excel数据导出是一个常见的需求,然而,当数据量较大时,性能和内存问题往往会成为限制导出效率的瓶颈,下面我们看看C#如何结合EPPl... 目录一、技术方案核心对比二、各方案选型建议三、性能对比数据四、核心代码实现1. MiniExcel

什么是ReFS 文件系统? ntfs和refs的优缺点区别介绍

《什么是ReFS文件系统?ntfs和refs的优缺点区别介绍》最近有用户在Win11Insider的安装界面中发现,可以使用ReFS来格式化硬盘,这是不是意味着,ReFS有望在未来成为W... 数十年以来,Windows 系统一直将 NTFS 作为「内置硬盘」的默认文件系统。不过近些年来,微软还在研发一款名

使用easy connect之后,maven无法使用,原来需要配置-Djava.net.preferIPv4Stack=true问题

《使用easyconnect之后,maven无法使用,原来需要配置-Djava.net.preferIPv4Stack=true问题》:本文主要介绍使用easyconnect之后,maven无法... 目录使用easGWowCy connect之后,maven无法使用,原来需要配置-DJava.net.pr

在.NET平台使用C#为PDF添加各种类型的表单域的方法

《在.NET平台使用C#为PDF添加各种类型的表单域的方法》在日常办公系统开发中,涉及PDF处理相关的开发时,生成可填写的PDF表单是一种常见需求,与静态PDF不同,带有**表单域的文档支持用户直接在... 目录引言使用 PdfTextBoxField 添加文本输入域使用 PdfComboBoxField

C#如何调用C++库

《C#如何调用C++库》:本文主要介绍C#如何调用C++库方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录方法一:使用P/Invoke1. 导出C++函数2. 定义P/Invoke签名3. 调用C++函数方法二:使用C++/CLI作为桥接1. 创建C++/CL

C#使用StackExchange.Redis实现分布式锁的两种方式介绍

《C#使用StackExchange.Redis实现分布式锁的两种方式介绍》分布式锁在集群的架构中发挥着重要的作用,:本文主要介绍C#使用StackExchange.Redis实现分布式锁的... 目录自定义分布式锁获取锁释放锁自动续期StackExchange.Redis分布式锁获取锁释放锁自动续期分布式

Java学习手册之Filter和Listener使用方法

《Java学习手册之Filter和Listener使用方法》:本文主要介绍Java学习手册之Filter和Listener使用方法的相关资料,Filter是一种拦截器,可以在请求到达Servl... 目录一、Filter(过滤器)1. Filter 的工作原理2. Filter 的配置与使用二、Listen

redis过期key的删除策略介绍

《redis过期key的删除策略介绍》:本文主要介绍redis过期key的删除策略,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录第一种策略:被动删除第二种策略:定期删除第三种策略:强制删除关于big key的清理UNLINK命令FLUSHALL/FLUSHDB命

C# foreach 循环中获取索引的实现方式

《C#foreach循环中获取索引的实现方式》:本文主要介绍C#foreach循环中获取索引的实现方式,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录一、手动维护索引变量二、LINQ Select + 元组解构三、扩展方法封装索引四、使用 for 循环替代

C# Where 泛型约束的实现

《C#Where泛型约束的实现》本文主要介绍了C#Where泛型约束的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录使用的对象约束分类where T : structwhere T : classwhere T : ne