.NET9 Linux-x64下Console.WriteLine原理
1.前言
之前聊過(guò)Windows下面Console.WriteLine的運(yùn)行過(guò)程常用Console.WriteLine原理,你知道嗎?本篇看下它在Linu-x64下面最新.NET9運(yùn)行過(guò)程
2.托管代碼
.NET里面流讀寫數(shù)據(jù)一般都是通過(guò)StreamReader和StreamWriter這兩個(gè)類操作。它們命名空間在:System.IO。托管庫(kù):System.Runtime.dll,比如以下簡(jiǎn)單的操作小例子:
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庫(kù)里面直接通過(guò)TextWriter寫入到的數(shù)據(jù)流/設(shè)備。
比如例子:
static void Main(string[] args)
{
Console.WriteLine("Call Main");
}
Console.WriteLine會(huì)調(diào)用Out.EnsureInitialized函數(shù),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函數(shù)里面調(diào)用了Volatile.Write函數(shù),Volatile.Write函數(shù)的意思是,把參數(shù)二的值寫入到參數(shù)一字段里面去。這里的解釋就是把CreateOutputWriter(ConsolePal.OpenStandardOutput())返回的值寫入s_out字段,s_out也是TextWriter類型。
這里看下CreateOutputWriter(ConsolePal.OpenStandardOutput())返回的值是啥。CreateOutputWriter函數(shù)如下:
源碼地址: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,也就是說(shuō)Volatile.Write把流數(shù)據(jù)寫入到流數(shù)據(jù),然后從設(shè)備上打印出來(lái)。
這里寫入的流數(shù)據(jù)值到底是什么呢?繼續(xù)看ConsolePal.OpenStandardOutput()函數(shù),它返回的是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里面實(shí)例化了一個(gè)UnixConsoleStream類,UnixConsoleStream的構(gòu)造函數(shù)里面,第一個(gè)參數(shù)是Interop.CheckIo(Interop.Sys.Dup(Interop.Sys.FileDescriptors.STDOUT_FILENO))。Interop.Sys.Dup是Linux下面進(jìn)行文件表項(xiàng)操作的函數(shù)。CheckIo則是個(gè)檢查函數(shù),此處可以忽略。
舉個(gè)例子,一般的來(lái)說(shuō)Linux下面在終端打印一個(gè)hello World通常通過(guò)printf("hello Word")
printf -> stdout(標(biāo)準(zhǔn)輸出) -> Dup(STDOUT_FILENO) ->終端輸出
可見(jiàn)Linux下面是通過(guò)Dup函數(shù)傳遞參數(shù)STDOUT_FILENO進(jìn)行終端操作的,UnixConsoleStream第二個(gè)參數(shù)是FileAccess.Write,表示寫入字符串。然后把這個(gè)Stream封裝后返回,通過(guò)Volatile.Write寫入到終端流里面打印出來(lái)。
那么其實(shí)很清晰了,流程大致如下:
Console.WriteLine ->Out.EnsureInitialized -> ConsolePal.OpenStandardOutput() -> I