详解.net中IL语言

什么是IL语言

中间语言,又称(IL语言)。充当Clr与.net 平台的中间语言,比如用C#编写程序,编译器首先是把C#代码转译成IL语言,最终由Clr解释执行,下面我们学习下IL语言。

如何读懂IL语言

  • 写一个helloworld的.net 程序,编译运行完成。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    		static void Main(string[] args)
    {
    Console.WriteLine("hello world");
    }
    ```

    - 使用ildasm.exe(C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6 Tools)反编译代码,得到IL代码如下:

    ```IL
    .method private hidebysig static void Main(string[] args) cil managed
    {
    .entrypoint
    // 代码大小 13 (0xd)
    .maxstack 8
    IL_0000: nop
    IL_0001: ldstr "hello world"
    IL_0006: call void [mscorlib]System.Console::WriteLine(string)
    IL_000b: nop
    IL_000c: ret
    } // end of method Program::Main
    ```

    - 查找对应的[指令表](http://blog.csdn.net/xiaouncle/article/details/71248830),来确定对应的含义


    <table class="table">
    <thead>
    <tr>
    <th>指令名称</th>
    <th>说明</th>
    </tr>
    </thead>
    <tbody>
    <tr>
    <td>Ldstr</td>
    <td>推送对元数据中存储的字符串的新对象引用。</td>
    </tr>
    <tr>
    <td>Nop</td>
    <td>如果修补操作码,则填充空间。尽管可能消耗处理周期,但未执行任何有意义的操作。</td>
    </tr>
    <tr>
    <td>Call</td>
    <td>调用由传递的方法说明符指示的方法。</td>
    </tr>
    <tr>
    <td>Ret</td>
    <td>从当前方法返回,并将返回值(如果存在)从调用方的计算堆栈推送到被调用方的计算堆栈上。</td>
    </tr>
    </tbody>
    </table>

    - 其它几个名词的的解释

    **hidebysig**: 与之对就的是hidebyname,这个是确定使用方法的签名还是使用方法的名称来确定调用哪个方法.


    - 整个的IL语言解释

    ```IL
    .method private hidebysig static void Main(string[] args) cil managed
    {
    .entrypoint //代码入口
    // 代码大小 13 (0xd)
    .maxstack 8 //整个程序的堆栈大小
    IL_0000: nop //无实在意义
    IL_0001: ldstr "hello world" //定义字符
    IL_0006: call void [mscorlib]System.Console::WriteLine(string) //调用WriteLine变量
    IL_000b: nop
    IL_000c: ret //返回
    } // end of method Program::Main
    ```


    ### 更复杂的Demo

    - 添加编写如下C#代码:

    ``` C#
    class Program
    {
    static void Main(string[] args)
    {
    var a = 0;
    var b = 1;
    var c = Add(a, b);
    Console.WriteLine(c.ToString());
    }

    public static int Add(int x,int y)
    {
    return x + y;
    }
    }

    ```

    - 生成相关的IL代码及解释

    ``` IL
    .method private hidebysig static void Main(string[] args) cil managed
    {
    .entrypoint
    // 代码大小 27 (0x1b)
    .maxstack 2
    .locals init ([0] int32 a,
    [1] int32 b,
    [2] int32 c) //定义3个变量
    IL_0000: nop
    IL_0001: ldc.i4.0 //将整数值 0 作为 int32 推送到计算堆栈上。
    IL_0002: stloc.0 //从计算堆栈的顶部弹出当前值并将其存储到索引 0 处的局部变量列表中。
    IL_0003: ldc.i4.1 //将整数值 1 作为 int32 推送到计算堆栈上。
    IL_0004: stloc.1 //从计算堆栈的顶部弹出当前值并将其存储到索引 1 处的局部变量列表中。
    IL_0005: ldloc.0 //将索引 0 处的局部变量加载到计算堆栈上,这里指a。
    IL_0006: ldloc.1 //将索引 1 处的局部变量加载到计算堆栈上,这里指b。
    IL_0007: call int32 ILTest.Program::Add(int32,
    int32) //调用Add方法
    IL_000c: stloc.2 //将索引 2 处的局部变量加载到计算堆栈上,这里指c。
    IL_000d: ldloca.s c //将位于特定索引处的局部变量的地址加载到计算堆栈上(短格式)。
    IL_000f: call instance string [mscorlib]System.Int32::ToString()
    IL_0014: call void [mscorlib]System.Console::WriteLine(string)
    IL_0019: nop
    IL_001a: ret
    } // end of method Program::Main

    ```

    Add方法:

    ``` IL
    .method public hidebysig static int32 Add(int32 x,int32 y) cil managed
    {
    // 代码大小 9 (0x9)
    .maxstack 2
    .locals init ([0] int32 V_0) //创建一个V_0的局部变量
    IL_0000: nop
    IL_0001: ldarg.0 //将索引为 0 的参数加载到计算堆栈上。
    IL_0002: ldarg.1 //将索引为 1 的参数加载到计算堆栈上。
    IL_0003: add //将两个值相加并将结果推送到计算堆栈上。
    IL_0004: stloc.0
    IL_0005: br.s IL_0007 //无条件地将控制转移到目标指令(短格式)
    IL_0007: ldloc.0 //将索引 0 处的局部变量加载到计算堆栈上。
    IL_0008: ret
    } // end of method Program::Add
阅读更多

服务器CPU居高不下--解决问题历程

基本的概述

在一个服务器的集群上面,服务器的CPU长时间居高不下,响应的时间也一直很慢,即使扩容了服务器CPU的下降效果也不是很明显。

对于CPU过高的原因,可以总结到以下原因:

  • 太多的循环或者死循环

  • 加载了过多的数据,导致产生了很多的大对象

  • 产生了过多的对象,GC回收过于频繁(如:字符串拼接)

对于上面的情况,难点不是优化代码,难点在于定位到问题的所在,下面我们就用Dump抓包的方式来定位到问题的所在。介绍这个内容之前,我们要先回顾下.Net中垃圾回收的基础知识和一个工具的准备。


基础知识


垃圾回收触发条件

  • 代码显示调用System.GC的静态方法

  • windows报告低内存情况

  • CLR正在卸载AppDoamin

  • CLR正在关闭

阅读更多

多线程如何排队执行

场景

有一个这样场景,程序会有一个非常耗时的操作,但要求耗时的操作完成后,再顺序的执行一个不耗时的操作,而且这个程序的调用,可能存在同时调用的情况。

具体的模型如下:

moxing

从Start开始触发了5个线程,经过一个longTimeJob同时执行,我们不关心longJob的执行时间和先后顺序,根据Start的先后顺序来执行一个ShortJob。下面我们用代码来模拟上面的过程。

举例说明:有ABCD 4个线程,进入的顺序也是ABCD,A耗时3s,B耗时7s,C耗时1s,D耗时3s. 所以如果当4个线程都同时开始执行时,完成的先后顺序为 CADB,但我们要求的顺序是ABCD,也就是说C要等待AB执行完后,才能继续后续的工作。

我们可以用请求bing搜索来模拟longTimeJob,根据传入的序列来决定请求多少次,主要模拟方法如下:


    private static async Task Test()
    {
        var arry = new[]
        {
            10, 9, 8, 7, 6, 5, 4, 3, 2, 1
        };
        var listTask = new List<Task<int>>();
        foreach (var i in arry)
        {
            var i1 = i;
            var task = Task.Run(() => DoJob(i1));

            listTask.Add(task);
        }

        foreach (var task in listTask)
        {
            Console.WriteLine("输出-->:" + await task);//
        }
    }     

    public static Task<int> DoJob(int o)
    {
        return Task.Run(() =>
        {
            DoLongTimeThing(o);
            return o;
        });
    }      

    public static void DoLongTimeThing(int i)
    {
        Console.WriteLine("执行-->:" + i);
        for (int j = 0; j < i; j++)
        {
            HttpGet("http://cn.bing.com/");
        }

        Console.WriteLine("执行完毕-->:" + i);
    }

    public static string HttpGet(string url)
    {
        try
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            request.Method = "GET";

            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
            string content = reader.ReadToEnd();
            return content;
        }
        catch (Exception e)
        {
            return e.Message;
        }

    }

执行结果:

taskjob

阅读更多

IIS执行原理

服务器的监听(IIS6.0+版本)

  1. 当请求到达服务器时,请求最终会到达TCPIP.SYS驱动程序,TCPIP.SYS将请求转发给HTTP.SYS网络驱动程序的请求队列中(可以理解为专门处理http请求的进程),当然在处理请求的过程中,HTTP.SYS进程会维护一个配置表用缓存请求的url和和应用程序池对应的关系。

  2. 当一个http请求被捕获到,HTTP.SYS会读取配置表,如果对应的应用程序没有启动,则HTTP.SYS会启动IIS相对应的应用程序。具体运行机制可以理解成为:

HTTP.SYS

HTTP.SYS是TCP之上的一个网络驱动程序,因此,HTTP.SYS不再属于IIS(这里说的IIS都是IIS6.0+版本,下文如果不特殊指明,默认为IIS6.0+版本),它已经从IIS中独立了出来。 Http.Sys独立有以下几个优点:

  • 可靠性: HTTP.SYS运行在内核模式下,作为操作系统的驱动程序运行。因此,HTTP.SYS不会受到用户代码的影响,它始终处于稳定运行状态,对用户的http请求进行监听,并及时作出反应。

  • 高性能: 从用户发送http请求到系统返回响应结果的这一过程都是HTTP.SYS在内核模式下完成的。不需要在内核模式和用户模式下进行切换,这样就极大地节省了系统资源,提高了请求的响应速度。

IIS处理

W3SVC

  1. W3SVC服务是一个独立运行的程序,寄宿在svchost.exe进程中,负责用户的参数监视和重新启动应用池的工作。 当一个请求进入HTTP.SYS的队列中,会通知W3SVC服务根据IIS中的配置去创建对应的应用进程,进行处理。

W3WP.exe

阅读更多