前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >.NET9 Linux-X64 Output dup(超级硬核)

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

作者头像
江湖评谈
发布2024-02-26 17:18:36
940
发布2024-02-26 17:18:36
举报
文章被收录于专栏:天下风云天下风云

本篇是对前两篇:常用Console.WriteLine原理NET9 Linux-x64下Console.WriteLine原理 的收尾。关于内核级的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如下:

代码语言:javascript
复制
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <stdio.h>
int main(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);
    return 0;
}

编译:

代码语言:javascript
复制
#gcc test.c -o test
#./test
8888
#vim demo.txt  //demo.txt会自动创建,跟test.c同一目录
8888

可以看到文件demo.txt里面写入了8888,这是因为当前流指向通过dup设置指向到了demo.txt文件。所以当你运行./test的时候,会往demo.txt里面写入数值。

以上是模拟一个键盘-》文件的流指向,Console.WriteLine只需要模拟一个终端的输出即可,原理其实是一样的。C#代码正是这种做法:

代码语言:javascript
复制
//STDOUT_FILENO标准终端输出
Interop.Sys.Dup(Interop.Sys.FileDescriptors.STDOUT_FILENO)

拓展下:

代码语言:javascript
复制
# whereis unistd.h
unistd.h: /usr/include/unistd.h
#vim /usr/include/unistd.h
vim commd search :/STD
#define STDIN_FILENO    0       /* Standard input.  */
#define STDOUT_FILENO   1       /* Standard output.  */
#define STDERR_FILENO   2       /* Standard error output.  */
代码语言:javascript
复制
0,1,2分别为标准格式,上面例子 dupoutput 则是4.

STDIN_FILENO标准输入
STDOUT_FILENO   标准输出
STDERR_FILENO   标准错误

2.dup2(拓展知识)

dup有一个变体dup2,它的作用是把流指向进行重定位,把新的流赋给旧的流指向,这样新的流指向的即是旧流的指向,函数原型:int dup2(int olds,int news)。

代码语言:javascript
复制
一个正常的标准输出是1.  dup测试:

dup2.c:
#include<stdio.h>
#include<unistd.h>
#include<string.h>
int main(int argc,char** argv)
{
  int out=dup(1);
  const char* str="hello\r\n";
  write(out,str,strlen(str));
  close(out);
  return 0;
}


#gcc dup2.c -o dup2
#./dup2
hello

现在把流指向其它值,比如下面把3指向标准输出流1,这样当往3里面写入(write)数据的时候,打印出hello.

代码语言:javascript
复制
dum2测试:

#include<stdio.h>
#include<unistd.h>
#include<string.h>
int main(int argc,char** argv)
{
  int out=dup2(1,3);
  const char* str="hello\r\n";
  write(3,str,strlen(str));
  close(out);
  return 0;
}

#./dup3
hello

三:托管开头

比如代码:Console.WriteLine("Call Main");它调用了

代码语言:javascript
复制
src\libraries\System.Console\src\System\Console.cs

[MethodImplAttribute(MethodImplOptions.NoInlining)]
public static void WriteLine(string? value)
{
  Out.WriteLine(value);
}

value参数即是字符串:Call Main。Out是什么呢?它是个属性,返回的是s_out。

代码语言:javascript
复制
 src\libraries\System.Console\src\System\Console.cs
 
 public static 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.WriteLine用的是STDOUT_FILENO标准输出

代码语言:javascript
复制
Interop.Sys.Dup(Interop.Sys.FileDescriptors.STDOUT_FILENO)

关于这点在上一篇:NET9 Linux-x64下Console.WriteLine原理 里面有讲到过。Dup原型如下:

代码语言:javascript
复制
internal static partial class Interop
{
    internal static partial class Sys
    {
        [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_Dup", SetLastError = true)]
        internal static partial SafeFileHandle Dup(SafeFileHandle oldfd);
    }
}

整体的就是Dup设置流指向终端,然后通过Console.WriteLine传递的字符串,把这字符串打印到屏幕上。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2024-02-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 江湖评谈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档