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#中async await异步关键字用法和异步的底层原理全解析

《C#中asyncawait异步关键字用法和异步的底层原理全解析》:本文主要介绍C#中asyncawait异步关键字用法和异步的底层原理全解析,本文给大家介绍的非常详细,对大家的学习或工作具有一... 目录C#异步编程一、异步编程基础二、异步方法的工作原理三、代码示例四、编译后的底层实现五、总结C#异步编程

C#TextBox设置提示文本方式(SetHintText)

《C#TextBox设置提示文本方式(SetHintText)》:本文主要介绍C#TextBox设置提示文本方式(SetHintText),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑... 目录C#TextBox设置提示文本效果展示核心代码总结C#TextBox设置提示文本效果展示核心代

C#中DrawCurve的用法小结

《C#中DrawCurve的用法小结》本文主要介绍了C#中DrawCurve的用法小结,通常用于绘制一条平滑的曲线通过一系列给定的点,具有一定的参考价值,感兴趣的可以了解一下... 目录1. 如何使用 DrawCurve 方法(不带弯曲程度)2. 如何使用 DrawCurve 方法(带弯曲程度)3.使用Dr

Pytest多环境切换的常见方法介绍

《Pytest多环境切换的常见方法介绍》Pytest作为自动化测试的主力框架,如何实现本地、测试、预发、生产环境的灵活切换,本文总结了通过pytest框架实现自由环境切换的几种方法,大家可以根据需要进... 目录1.pytest-base-url2.hooks函数3.yml和fixture结论你是否也遇到过

使用C#代码在PDF文档中添加、删除和替换图片

《使用C#代码在PDF文档中添加、删除和替换图片》在当今数字化文档处理场景中,动态操作PDF文档中的图像已成为企业级应用开发的核心需求之一,本文将介绍如何在.NET平台使用C#代码在PDF文档中添加、... 目录引言用C#添加图片到PDF文档用C#删除PDF文档中的图片用C#替换PDF文档中的图片引言在当

详解C#如何提取PDF文档中的图片

《详解C#如何提取PDF文档中的图片》提取图片可以将这些图像资源进行单独保存,方便后续在不同的项目中使用,下面我们就来看看如何使用C#通过代码从PDF文档中提取图片吧... 当 PDF 文件中包含有价值的图片,如艺术画作、设计素材、报告图表等,提取图片可以将这些图像资源进行单独保存,方便后续在不同的项目中使

C#使用SQLite进行大数据量高效处理的代码示例

《C#使用SQLite进行大数据量高效处理的代码示例》在软件开发中,高效处理大数据量是一个常见且具有挑战性的任务,SQLite因其零配置、嵌入式、跨平台的特性,成为许多开发者的首选数据库,本文将深入探... 目录前言准备工作数据实体核心技术批量插入:从乌龟到猎豹的蜕变分页查询:加载百万数据异步处理:拒绝界面

C#数据结构之字符串(string)详解

《C#数据结构之字符串(string)详解》:本文主要介绍C#数据结构之字符串(string),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录转义字符序列字符串的创建字符串的声明null字符串与空字符串重复单字符字符串的构造字符串的属性和常用方法属性常用方法总结摘

C#如何动态创建Label,及动态label事件

《C#如何动态创建Label,及动态label事件》:本文主要介绍C#如何动态创建Label,及动态label事件,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C#如何动态创建Label,及动态label事件第一点:switch中的生成我们的label事件接着,

C# WinForms存储过程操作数据库的实例讲解

《C#WinForms存储过程操作数据库的实例讲解》:本文主要介绍C#WinForms存储过程操作数据库的实例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、存储过程基础二、C# 调用流程1. 数据库连接配置2. 执行存储过程(增删改)3. 查询数据三、事务处