专栏名称: dotNET跨平台
专注于.NET Core的技术传播。在这里你可以谈微软.NET,Mono的跨平台开发技术。在这里可以让你的.NET项目有新的思路,不局限于微软的技术栈,横跨Windows,Linux 主流平台
目录
相关文章推荐
小熊科学分析  ·  土壤污染检测-土壤修复必读 ·  6 天前  
小熊科学分析  ·  土壤污染检测-土壤修复必读 ·  6 天前  
51好读  ›  专栏  ›  dotNET跨平台

.NET9 Linux-x64下Console.WriteLine原理

dotNET跨平台  · 公众号  ·  · 2024-02-23 08:00

正文

点击上方蓝字 江湖评谈设为关注




1.前言

之前聊过Windows下面Console.WriteLine的运行过程常用Console.WriteLine原理,你知道吗?本篇看下它在Linu-x64下面最新.NET9运行过程

2.托管代码:

.NET里面流读写数据一般都是通过StreamReaderStreamWriter这两个类操作。它们命名空间在:System.IO。托管库:System.Runtime.dll,比如以下简单的操作小例子:

using (StreamWriter writer = File.CreateText("newfile.txt")){    await writer.WriteLineAsync("First line of example");    await writer.WriteLineAsync("and second line");}

StreamWriter和StreamReader分别派生自TextWriter和TextReader。Console.WriteLine打印出的字符串,则是在CLR库里面直接通过TextWriter写入到的数据流/设备。

比如例子:

static void Main(string[] args){   Console.WriteLine("Call Main");}

Console.WriteLine会调用Out.EnsureInitialized函数,Out即是TextWriter类型,代码如下:

源码地址:https://github.com/dotnet/runtime/blob/main/src/libraries/System.Console/src/System/Console.cs        public static TextWriter Out        {            get            {                static TextWriter EnsureInitialized()                {                    lock (s_syncObject)                    {                        if (s_out == null)                        {                            Volatile.Write(ref s_out, CreateOutputWriter(ConsolePal.OpenStandardOutput()));                        }                        return s_out;                    }                }            }        }

EnsureInitialized函数里面调用了Volatile.Write函数,Volatile.Write函数的意思是,把参数二的值写入到参数一字段里面去。这里的解释就是把CreateOutputWriter(ConsolePal.OpenStandardOutput())返回的值写入s_out字段,s_out也是TextWriter类型。

这里看下CreateOutputWriter(ConsolePal.OpenStandardOutput())返回的值是啥。CreateOutputWriter函数如下:

源码地址:https://github.com/dotnet/runtime/blob/main/src/libraries/System.Console/src/System/Console.cs
private static TextWriter CreateOutputWriter(Stream outputStream){ return outputStream == Stream.Null ? TextWriter.Null : TextWriter.Synchronized(new StreamWriter( stream: outputStream, encoding: OutputEncoding.RemovePreamble(), bufferSize: WriteBufferSize, leaveOpen: true) { AutoFlush = true });}

可以看到CreateOutputWriter返回的依旧是TextWriter,也就是说Volatile.Write把流数据写入到流数据,然后从设备上打印出来。

这里写入的流数据值到底是什么呢?继续看ConsolePal.OpenStandardOutput()函数,它返回的是Stream类型。

源码地址:https://github.com/dotnet/runtime/blob/main/src/libraries/System.Console/src/System/ConsolePal.Unix.cs
public static Stream OpenStandardOutput(){ return new UnixConsoleStream(Interop.CheckIo(Interop.Sys.Dup(Interop.Sys.FileDescriptors.STDOUT_FILENO)), FileAccess.Write);}

这里是Linux/Unix下面的操作,OpenStandardOutput里面实例化了一个UnixConsoleStream类,UnixConsoleStream的构造函数里面,第一个参数是Interop.CheckIo(Interop.Sys.Dup(Interop.Sys.FileDescriptors.STDOUT_FILENO))。Interop.Sys.Dup是Linux下面进行文件表项操作的函数。CheckIo则是个检查函数,此处可以忽略。

举个例子,一般的来说Linux下面在终端打印一个hello World通常通过printf("hello Word")

printf -> stdout(标准输出) -> Dup(STDOUT_FILENO) ->终端输出

可见Linux下面是通过Dup函数传递参数STDOUT_FILENO进行终端操作的,UnixConsoleStream第二个参数是FileAccess.Write,表示写入字符串。然后把这个Stream封装后返回,通过Volatile.Write写入到终端流里面打印出来。

那么其实很清晰了,流程大致如下:

Console.WriteLine ->Out.EnsureInitialized -ConsolePal.OpenStandardOutput() -Interop.Sys.Dup ->Volatile.Write

这里面有很多可以讨论和深入的细节点,了解更多可以加入我的,一起学习,一起进步。

往期精彩回顾

欢迎加入C#12.NET8技术交流群