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#实现千万数据秒级导入的代码

《C#实现千万数据秒级导入的代码》在实际开发中excel导入很常见,现代社会中很容易遇到大数据处理业务,所以本文我就给大家分享一下千万数据秒级导入怎么实现,文中有详细的代码示例供大家参考,需要的朋友可... 目录前言一、数据存储二、处理逻辑优化前代码处理逻辑优化后的代码总结前言在实际开发中excel导入很

C#使用Spire.Doc for .NET实现HTML转Word的高效方案

《C#使用Spire.Docfor.NET实现HTML转Word的高效方案》在Web开发中,HTML内容的生成与处理是高频需求,然而,当用户需要将HTML页面或动态生成的HTML字符串转换为Wor... 目录引言一、html转Word的典型场景与挑战二、用 Spire.Doc 实现 HTML 转 Word1

C#实现一键批量合并PDF文档

《C#实现一键批量合并PDF文档》这篇文章主要为大家详细介绍了如何使用C#实现一键批量合并PDF文档功能,文中的示例代码简洁易懂,感兴趣的小伙伴可以跟随小编一起学习一下... 目录前言效果展示功能实现1、添加文件2、文件分组(书签)3、定义页码范围4、自定义显示5、定义页面尺寸6、PDF批量合并7、其他方法

C#下Newtonsoft.Json的具体使用

《C#下Newtonsoft.Json的具体使用》Newtonsoft.Json是一个非常流行的C#JSON序列化和反序列化库,它可以方便地将C#对象转换为JSON格式,或者将JSON数据解析为C#对... 目录安装 Newtonsoft.json基本用法1. 序列化 C# 对象为 JSON2. 反序列化

C#文件复制异常:"未能找到文件"的解决方案与预防措施

《C#文件复制异常:未能找到文件的解决方案与预防措施》在C#开发中,文件操作是基础中的基础,但有时最基础的File.Copy()方法也会抛出令人困惑的异常,当targetFilePath设置为D:2... 目录一个看似简单的文件操作问题问题重现与错误分析错误代码示例错误信息根本原因分析全面解决方案1. 确保

基于C#实现PDF转图片的详细教程

《基于C#实现PDF转图片的详细教程》在数字化办公场景中,PDF文件的可视化处理需求日益增长,本文将围绕Spire.PDFfor.NET这一工具,详解如何通过C#将PDF转换为JPG、PNG等主流图片... 目录引言一、组件部署二、快速入门:PDF 转图片的核心 C# 代码三、分辨率设置 - 清晰度的决定因

C# LiteDB处理时间序列数据的高性能解决方案

《C#LiteDB处理时间序列数据的高性能解决方案》LiteDB作为.NET生态下的轻量级嵌入式NoSQL数据库,一直是时间序列处理的优选方案,本文将为大家大家简单介绍一下LiteDB处理时间序列数... 目录为什么选择LiteDB处理时间序列数据第一章:LiteDB时间序列数据模型设计1.1 核心设计原则

Java中HashMap的用法详细介绍

《Java中HashMap的用法详细介绍》JavaHashMap是一种高效的数据结构,用于存储键值对,它是基于哈希表实现的,提供快速的插入、删除和查找操作,:本文主要介绍Java中HashMap... 目录一.HashMap1.基本概念2.底层数据结构:3.HashCode和equals方法为什么重写Has

Unity新手入门学习殿堂级知识详细讲解(图文)

《Unity新手入门学习殿堂级知识详细讲解(图文)》Unity是一款跨平台游戏引擎,支持2D/3D及VR/AR开发,核心功能模块包括图形、音频、物理等,通过可视化编辑器与脚本扩展实现开发,项目结构含A... 目录入门概述什么是 UnityUnity引擎基础认知编辑器核心操作Unity 编辑器项目模式分类工程

C#高效实现Word文档内容查找与替换的6种方法

《C#高效实现Word文档内容查找与替换的6种方法》在日常文档处理工作中,尤其是面对大型Word文档时,手动查找、替换文本往往既耗时又容易出错,本文整理了C#查找与替换Word内容的6种方法,大家可以... 目录环境准备方法一:查找文本并替换为新文本方法二:使用正则表达式查找并替换文本方法三:将文本替换为图