當前位置: 妍妍網 > 碼農

.NET9 Linux-X64 Output dup(超級硬核)

2024-02-24碼農

點選上方 藍字 江湖評談 設為關註




本篇是對前兩篇: , 的收尾。關於內核級的WriteFile和Linux-dup會在 分享。

一:Volatile.Write

這個函式是指令級別的操控,它把第二個參數的值賦給第一個參數。返回值為void。

現代化的CPU都是多核,一 組譯,多核同時讀取,載入,執行等任務。 有可能某核裏的程式碼某個階段會延遲,卡頓,以及bit幹擾等情況,其它核正常執行。 導致了前面的程式碼還沒載入完畢,後面的程式碼已經執行完成的情況。 為了不造成這種異常的現象,需要確保在讀取或者寫入某一條指令的時候,防止執行的操作執行到載入的前面,一切以正常的順序執行。 Volatile.Write函式就是確保這樣情況不再發生,讀取或者寫入的數據是完整無缺的。這就是 俗說的: 記憶體屏障

Console.WriteLine裏面用這個函式,主要是確保Linux下Dup設定的流指向以及Windows下WriteFile設定的流指向是正確的, 不會錯亂。

二:Linux dup+dup2

1.dup

講了下Console.WriteLine在Linux下呼叫了dup設定了流指向為終端輸出,透過這個流指向把WriteLine參數裏面的字串給它打印到螢幕上。本篇補充一些細節。

.NET9在Linux-x64上面的設定流指向是Dup函式,它在表頭檔#include <unistd.h>。透過一個檔寫入來模擬下 這個 過程。 test.c如下:

#include<unistd.h>#include<stdlib.h>#include<fcntl.h>#include<stdio.h>intmain(int argc,char** argv){int file = open("demo.txt", O_CREAT | O_RDWR | O_TRUNC);int dupoutput = dup(file);char buf[20];ssize_t n; n = read(STDIN_FILENO, buf, sizeof(buf)); write(dupoutput, buf, n);return0;}

編譯:

#gcc test.c -o test#./test8888#vim demo.txt //demo.txt會自動建立,跟test.c同一目錄8888

可以看到檔demo.txt裏面寫入了8888,這是因為當前流指向透過dup設定指向到了demo.txt檔。所以當你執行./test的時候,會往demo.txt裏面寫入數值。

以上是模擬一個鍵盤-】檔的流指向,Console.WriteLine只需要模擬一個終端的輸出即可,原理其實是一樣的。C#程式碼正是這種做法:

//STDOUT_FILENO標準終端輸出Interop.Sys.Dup(Interop.Sys.FileDescriptors.STDOUT_FILENO)

拓展下:

# whereis unistd.hunistd.h: /usr/include/unistd.h#vim /usr/include/unistd.hvim commd search :/STD#define STDIN_FILENO 0 /* Standard input. */#define STDOUT_FILENO 1 /* Standard output. */#define STDERR_FILENO 2 /* Standard error output. */

0,1,2分別為標準格式,上面例子 dupoutput 則是4.STDIN_FILENO標準輸入STDOUT_FILENO 標準輸出STDERR_FILENO 標準錯誤

2.dup2(拓展知識)

dup有一個變體dup2,它的作用是把流指向進行重定位,把新的流賦給舊的流指向,這樣新的流指向的即 舊流的指向,函式原型: int dup2(int olds,int news)。

一個正常的標準輸出是1. dup測試:dup2.c:#include<stdio.h>#include<unistd.h>#include<string.h>intmain(int argc,char** argv){int out=dup(1);constchar* str="hello\r\n"; write(out,str,strlen(str)); close(out);return0;}#gcc dup2.c -o dup2#./dup2hello

現在把流指向其它值,比如下面把3指向標準輸出流1,這樣當往3裏面寫入(write)數據的時候,打印出hello.

dum2測試:#include<stdio.h>#include<unistd.h>#include<string.h>intmain(int argc,char** argv){int out=dup2(1,3);constchar* str="hello\r\n"; write(3,str,strlen(str)); close(out);return0;}#./dup3hello

三:托管開頭

比如程式碼:Console.WriteLine("Call Main");它呼叫了

src\libraries\System.Console\src\System\Console.cs[MethodImplAttribute(MethodImplOptions.NoInlining)]publicstaticvoidWriteLine(stringvalue){ Out.WriteLine(value);}

value參數即是字串:Call Main 。Out是什麽呢?它是個內容,返回的是 s_out。

 src\libraries\System.Console\src\System\Console.cspublicstatic TextWriter Out {get { Debug.Assert(!Monitor.IsEntered(s_syncObject));return Volatile.Read(ref s_out) ?? EnsureInitialized();static TextWriter EnsureInitialized() {lock (s_syncObject) // Ensures Out and OutputEncoding are synchronized. {if (s_out == null) { Volatile.Write(ref s_out, CreateOutputWriter(ConsolePal.OpenStandardOutput())); }return s_out; } }}

s_out實際上就是Linux下面Dup設定的流指向終端輸出,這裏微軟的Console.Wr iteLine用的是STDOUT_FILENO標準輸出

Interop.Sys.Dup(Interop.Sys.FileDescriptors.STDOUT_FILENO)

關於這點在上一篇 裏面有講到過。Dup原型如下:

internalstaticpartial classInterop{internalstaticpartial classSys { [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_Dup", SetLastError = true)]internalstaticpartial SafeFileHandle Dup(SafeFileHandle oldfd); }}

整體的就是Dup設定流指向終端,然後透過Console.WriteLine傳遞的字串,把這字串打印到螢幕上。

更多的頂級技術學習和了解可以加入

往期精彩