C#由窗体原子表溢出造成的软件闪退的问题解决方法

news2024/11/18 21:44:43

报错信息

在这里插入图片描述
由于在MS.Win32.UnsafeNativeMethods.RegisterClassEx产生了报错信息,但是一直向外部抛出错误但始终没有被捕捉成功,直到报错被UI线程捕获,但是仍然没有进行处理,所有造成WPF的应用闪退。

解析报错信息

1.从异常初始位置查看原因

我们可以找到MS.Win32.UnsafeNativeMethods.RegisterClassEx的这个函数,显然时来自于win32的微软的类库,通常来说win32时用于关联Windows系统窗体的类库,通常会在.net的GUI框架中会大量使用,例如WPF和WinForm中。所以我们先定位报错的函数

 [DllImport("user32.dll", BestFitMapping = false, CharSet = CharSet.Unicode, EntryPoint = "RegisterClassEx", SetLastError = true)]
 [SecurityCritical]
 [SuppressUnmanagedCodeSecurity]
 internal static extern ushort IntRegisterClassEx(NativeMethods.WNDCLASSEX_D wc_d);

 [SecurityCritical]
 internal static ushort RegisterClassEx(NativeMethods.WNDCLASSEX_D wc_d)
 {
     ushort num = IntRegisterClassEx(wc_d);
     if (num == 0)
     {
         throw new Win32Exception();
     }

     return num;
 }

如上述代码,我们基本定位了我们问题的所在位置,当num的结果为0时,抛出一个win32的错误信息,这个与我们的报错中来自win32的报错一致。所以我们基本可以确认,报错来自于此处的函数。

2.查看报错对应函数的用法

当我们已经根据报错信息定位到此函数后,并且发现函数来源于win32的库,则代表,这个时微软官方的库,所以我们前往微软官方文档网站去查询对应函数的信息。
在这里插入图片描述
从微软官方的函数解析文档我们可以发现,这个函数的作用在于根据输入的参数注册一个窗体,并返回一个唯一标识所注册类的类原子。我们前面说过由于num的参数为0,代表注册失败才会报的错误,所以我们需要知道为什么我们创建一个唯一标识符的类原子会出现失败的情况。此时我们发现文档中著名函数的返回值是一个ATOM的参数,与代码中的ushort的返回值明显不一样,所以ATOM可能代表一个微软的自定义的类型,所以我们使用win32 ATOM的关键字去搜索

3.查看为什么会出现报错

在这里插入图片描述
此时我们在Bing上找到了微软关于ATOM的解释。
在这里插入图片描述
如官方文档所示,ATOM是一个由系统定义的表,他里面存储的是多个字符串的信息。
在这里插入图片描述
其中我们通过官方文档对ATOM的描述我们可以知道,当我们的原子表没有更多的空间时,并且传入的字符串不在所属空间时,则会调用失败。并且官方文档中还特别声明了ATOM的创建通常来自于RegisterClass的函数,与我们报错信息的函数一致。所以我们综上所述,此次报错时由于我们在注册窗体时,需要对ATOM的表创建一个新的唯一标识符节点,但是表被占用满了,所以创建失败,抛出Win32的错误到外部。但是一直不能被捕获,直到最外层被UI线程捕获后,但是没有进行处理,造成软件崩溃。

解决报错

1.尝试寻找可以捕获的地方

由于是来自微软库的报错,通常这种报错的原因非常难定位,所以我们通常会在报错的堆栈调用中进行try catch进行对应报错捕获,在根据报错信息去查询我们可能引起此问题的语句再哪里。其次使用创建转储文件的形式放在windbg中去查看报错调用的堆栈信息,检测是否还有弹窗没有显示的有用信息。
但是非常可惜的是,由于设备在客户现场,并且在公司的设备无法短时间内复现报错,并且公司要求软件问题需要在3天内解决。所以我们不能使用windbg去抓取dump来分析堆栈。其次由于此次报错是来自于win32的报错,在WPF中,由非常多库直接或间接使用了win32的库,在经历过数个小时的定位决定放弃捕获的方式。

2.监控ATOM表

由于时间和报错库的原因,所以我们只能通过去监控ATOM表的节点几时增加的形式去逐步排查我们的代码。
此时我在GitHub找到了一个专门用来监控ATOM表的软件。ATOM Table Monitor。

https://github.com/JordiCorbilla/atom-table-monitor

在这里插入图片描述
在这里时我们就需要调低监控刷新频率,然后让软件处于运行状态观察,在上面状态下运行时可能会增减ATOM的节点。
然后梳理软件的运行逻辑。例如在我的一个上位机检测软件中,分几大模块,初始化数据,拍照,运算,数据处理。然后再模块中再去深度的梳理我们可能发现的问题。尤为注意的一点,代码看着没有问题不代表实际没有问题。尤其是软件中逻辑复杂,涉及到大量多线程的状态下,及其有可能是某一个库会经过复杂的转换从而去重复调用一些内容。

3.寻找代码异常的地方

此时我们对软件的大模块分析可以基本确认问题发生在哪个区域,例如假设我的问题发生在数据计算的大模块中,那么我需要将模块的代码进行逐步的拆分,然后检测是哪一个部分的问题。
例如,假设我下述代码中不知道哪一个函数由有问题,但是全部运行时就会出现问题,所以我们可以使用我们在数据结构最常见的二分查找法。由于代码都是自上而下顺序执行的,并且代码数量固定,所以使用二分查找法可以极大的减少时间花费。

//例如在下述代码中,我只执行方法ABC观察是否会复现问题
//假设未复现时,我们可以确定时方法DEF出现问题,此时我们需要对方法DEF再次进行二分查找
 static void Main(string[] args)
 {
     MethonA();
     MethonB();
     MethonC();
     return;
     MethonD();
     MethonE();
     MethonF();
 }

当我们执行到需要有返回结果的函数时,我们可以给他一个合理的自定义结果,使得软件可以持续的运行下去。通常情况下,我们都是选取首次运行结果,并将其保持的全局变量中,然后将其赋值到新的对象中,以确保软件不会因为数值问题出现影响

4.成功锁定代码异常位置

public double GetArea()
{
    double area = 0;

    switch (ShapeType)
    {
        case ShapeTypes.Rectangle:
            area = RoiSize.Width * RoiSize.Height;
            break;
        case ShapeTypes.Segment:
        case ShapeTypes.Polygon:
        case ShapeTypes.Path:
        	#region 此处是问题所在位置
        	var geometry = System.Windows.Media.Geometry.Parse(PathData);
			#endregion
            area = geometry.GetArea();
            break;
        case ShapeTypes.Circle:
        case ShapeTypes.Ellipse:
            area = Math.PI * (RoiSize.Width / 2) * (RoiSize.Height / 2);
            break;
    }

    return area;
}

此函数的意思是,当我们得到一个来自于数据中的几何图形,然后获取其的面积的方法。当几何图像的类型为Path(即是来自于WPF的<Path.Data>的数据)时,将数据转换为Geometry的图形对象,并且获取图形对象的面积。
但是我在仔细审查其中代码时我发现,这个对象关联的类和方法并没有使用HwndWrapper的对象,但是从我们上述报错中,我们发现创建ATOM的节点是从这个类中进入的。后面我继续查找了大概5个小时的时间仍然没有任何收获。并且我发现在其他地方使用此函数却不会造成ATOM节点增加。但是由于时间的原因。所以我决定通过传入的Path.Data的数据源自己重写一个获取对应数据几何面积的方法。

5.重写对应的方法

我们可以通过查看他的函数的执行流程。
此时我们通过他的方法函数,我们可以发现,他是将输入的字符串进行解析,然后根据每个字母后面代表的点数据来绘制一个几何图形。但是由于获取面积的函数无法被反编译,所以我们只能根据他解析的字符串对应的数据进行重写方法

internal void ParseToGeometryContext(StreamGeometryContext context, string pathString, int startIndex)
{
    _formatProvider = CultureInfo.InvariantCulture;
    _context = context;
    _pathString = pathString;
    _pathLength = pathString.Length;
    _curIndex = startIndex;
    _secondLastPoint = new Point(0.0, 0.0);
    _lastPoint = new Point(0.0, 0.0);
    _lastStart = new Point(0.0, 0.0);
    _figureStarted = false;
    bool flag = true;
    char c = ' ';
    while (ReadToken())
    {
        char token = _token;
        if (flag)
        {
            if (token != 'M' && token != 'm')
            {
                ThrowBadToken();
            }

            flag = false;
        }

        switch (token)
        {
            case 'M':
            case 'm':
                _lastPoint = ReadPoint(token, allowcomma: false);
                context.BeginFigure(_lastPoint, isFilled: true, isClosed: false);
                _figureStarted = true;
                _lastStart = _lastPoint;
                c = 'M';
                while (IsNumber(allowComma: true))
                {
                    _lastPoint = ReadPoint(token, allowcomma: false);
                    context.LineTo(_lastPoint, isStroked: true, isSmoothJoin: false);
                    c = 'L';
                }

                break;
            case 'H':
            case 'L':
            case 'V':
            case 'h':
            case 'l':
            case 'v':
                EnsureFigure();
                do
                {
                    switch (token)
                    {
                        case 'l':
                            _lastPoint = ReadPoint(token, allowcomma: false);
                            break;
                        case 'L':
                            _lastPoint = ReadPoint(token, allowcomma: false);
                            break;
                        case 'h':
                            _lastPoint.X += ReadNumber(allowComma: false);
                            break;
                        case 'H':
                            _lastPoint.X = ReadNumber(allowComma: false);
                            break;
                        case 'v':
                            _lastPoint.Y += ReadNumber(allowComma: false);
                            break;
                        case 'V':
                            _lastPoint.Y = ReadNumber(allowComma: false);
                            break;
                    }

                    context.LineTo(_lastPoint, isStroked: true, isSmoothJoin: false);
                }
                while (IsNumber(allowComma: true));
                c = 'L';
                break;
            case 'C':
            case 'S':
            case 'c':
            case 's':
                EnsureFigure();
                do
                {
                    Point point;
                    if (token == 's' || token == 'S')
                    {
                        point = ((c != 'C') ? _lastPoint : Reflect());
                        _secondLastPoint = ReadPoint(token, allowcomma: false);
                    }
                    else
                    {
                        point = ReadPoint(token, allowcomma: false);
                        _secondLastPoint = ReadPoint(token, allowcomma: true);
                    }

                    _lastPoint = ReadPoint(token, allowcomma: true);
                    context.BezierTo(point, _secondLastPoint, _lastPoint, isStroked: true, isSmoothJoin: false);
                    c = 'C';
                }
                while (IsNumber(allowComma: true));
                break;
            case 'Q':
            case 'T':
            case 'q':
            case 't':
                EnsureFigure();
                do
                {
                    if (token == 't' || token == 'T')
                    {
                        if (c == 'Q')
                        {
                            _secondLastPoint = Reflect();
                        }
                        else
                        {
                            _secondLastPoint = _lastPoint;
                        }

                        _lastPoint = ReadPoint(token, allowcomma: false);
                    }
                    else
                    {
                        _secondLastPoint = ReadPoint(token, allowcomma: false);
                        _lastPoint = ReadPoint(token, allowcomma: true);
                    }

                    context.QuadraticBezierTo(_secondLastPoint, _lastPoint, isStroked: true, isSmoothJoin: false);
                    c = 'Q';
                }
                while (IsNumber(allowComma: true));
                break;
            case 'A':
            case 'a':
                EnsureFigure();
                do
                {
                    double width = ReadNumber(allowComma: false);
                    double height = ReadNumber(allowComma: true);
                    double rotationAngle = ReadNumber(allowComma: true);
                    bool isLargeArc = ReadBool();
                    bool flag2 = ReadBool();
                    _lastPoint = ReadPoint(token, allowcomma: true);
                    context.ArcTo(_lastPoint, new Size(width, height), rotationAngle, isLargeArc, flag2 ? SweepDirection.Clockwise : SweepDirection.Counterclockwise, isStroked: true, isSmoothJoin: false);
                }
                while (IsNumber(allowComma: true));
                c = 'A';
                break;
            case 'Z':
            case 'z':
                EnsureFigure();
                context.SetClosedState(closed: true);
                _figureStarted = false;
                c = 'Z';
                _lastPoint = _lastStart;
                break;
            default:
                ThrowBadToken();
                break;
        }
    }
}

重写后的代码。根据我观察发现,他每个是包含直线和曲线的点集,但是曲线是如何计算的,这个是我们未知的内容。所以我先跳过曲线部分的计算,然后将所以点连接在一起并生成一个闭合几何图形,并求取闭合几何图形的坐标。

 #region 解析Pathdata的字符串并求取面积。
 //由于在使用 Geometry.Parse函数时会造成系统原子表不断增加节点,直到溢出,所以替换使用
 /// <summary>
 /// 通过pathdata字符串数据求取面积
 /// </summary>
 /// <param name="pathData"></param>
 /// <returns></returns>
 private double CalculatePathArea(string pathData)
 {
     try
     {
         string[] commandsAndPoint2ds = pathData.Split(' ');
         List<Point2d> Point2ds = new List<Point2d>();

         Point2d currentPoint2d;

         for (int i = 0; i < commandsAndPoint2ds.Length; i++)
         {
             string item = commandsAndPoint2ds[i];
             if (item == "M" || item == "L")
             {
                 string[] coordinates = commandsAndPoint2ds[i + 1].Split(',');
                 for (int k = 0; k < coordinates.Count(); k++)
                 {
                     double x = double.Parse(coordinates[k]);
                     double y = double.Parse(coordinates[k+1]);
                     currentPoint2d = new Point2d { X = x, Y = y };
                     Point2ds.Add(currentPoint2d);
                     k++;
                 }           
             }
             else if (item == "C")
             {
                 string[] controlPoint2d1Coordinates = commandsAndPoint2ds[i + 1].Split(',');
                 for(int k = 0;k< controlPoint2d1Coordinates.Length; k++)
                 {
                     double cp1X = double.Parse(controlPoint2d1Coordinates[k]);
                     double cp1Y = double.Parse(controlPoint2d1Coordinates[k+1]);

                     currentPoint2d = new Point2d { X = cp1X, Y = cp1Y };
                     Point2ds.Add(currentPoint2d);
                     k++;
                 }
                 
             }
         }

         double area = 0;
         int j = Point2ds.Count - 1;
         for (int i = 0; i < Point2ds.Count; i++)
         {
             area += (Point2ds[j].X + Point2ds[i].X) * (Point2ds[j].Y - Point2ds[i].Y);
             j = i;
         }

         return Math.Abs(area / 2);
     }
     catch (Exception ex)
     {
         LogHelper.ErrorLogger.Error("获取面积参数失败"+ex.ToString());
         return 0;
     }
     
 }
 /// <summary>
 /// 将pathdata数据拆分为可以被解析的数据。方式:通过对M,L,C3个字母周围添加空格实现,并删除字符串的最后一个结束识别符z
 /// </summary>
 /// <param name="input"></param>
 /// <returns></returns>
 private string AddSpacesAroundSpecificLetters(string input)
 {
     string result = "";
     if (input.Length > 0)
     {
         input = input.Substring(0, input.Length - 1);
     }
     input = input.Replace(" ",",");
     for (int i = 0; i < input.Length; i++)
     {
         char currentChar = input[i];
         if (currentChar == 'M' || currentChar == 'L' || currentChar == 'C')
         {
             if (i > 0)
                 result += " ";
             result += currentChar;
             if (i < input.Length - 1)
                 result += " ";
         }
         else
         {
             result += currentChar;
         }
     }
     return result;
 }
 #endregion

后面我拿了大概50万组数据进行测试,发现我的计算结果与实际结果最大不超过3%的数值。在设备中是允许的误差的。由于补丁急需快速上线,后续的工作安排,原本还需要对PathData的曲线算法研究,就此结束了。

6.替换算法后重新测试

后续测试48小时后ATOM表的节点一直处于正常状态,所以这个报错的延申问题就此解决了

总结

至此,从上到下大概花费了2天半的时间(加上重写好算法内容),算是按时完成。由于这个报错的关键是,这个报错的全部信息都来自于微软的官方库,我们不能通过报错信息去定位我们的代码区域。并且由于此设备的代码多达几十万行,所以我也不能通过梳理代码逻辑去检查这个问题,事实证明这个小问题确实发现不了。
但是这句话到底有没有问题呢?其实我也不确定,我后面单独将这个方法拉出来做压力测试时候发现,他对ATOM表并没有影响,但是在设备上,确实是会出现这个问题,所以我自己主观上觉得,可能是这个方法中的一个对象在执行完后由于某种原因并没有被释放。并且在我压力测试此函数时,因为没有复杂的上下文,所以执行完后就立即释放就不会出现问题。这个原因在一开始我关闭软件后,ATOM表对应的节点全部被清空了,所以可以证明由于这个方法导致多个有同样功能的类对象没有被释放导致的。但是后续我重新使用Windbg和VS内存快照进行监控,又找不到明显的不被释放的类对象,后续由于时间原因,暂时只能总结这么多了。由于个人水平不高的原因,有什么好想法的网友可以给建议

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2175966.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Camera Raw:打开图像

在图像工作流程中&#xff0c;无论是 Raw 格式图像文件还是 JPEG、TIFF 文件&#xff0c;都可以先使用 Camera Raw 打开并调整后&#xff0c;再进入其它 Adobe 软件如 Photoshop 中进行进一步的编辑和处理。 一、打开 Raw 格式图像 1、通过 Adobe Bridge 打开 在 Adobe Bridge …

Excel插件:dd统计与排名

Excel插件&#xff1a;dd统计与排名 使用教程 专门为学校成绩统计与排名设计的插件 一、安装后如图 二、 功能介绍&#xff1a; &#xff08;一&#xff09;单科统计与排名 1、 模板说明&#xff08;单科用&#xff09; 2、 单科三分四率统计 PS&#xff1a;可以设置界值&am…

哈希知识点总结:哈希、哈希表、位图、布隆过滤器

目录 哈希 哈希表 哈希常用方法 1、直接定址法 2、存留余数法 哈希冲突 哈希冲突的解决办法 1、闭散列&#xff1a;开放定址法 &#xff08;1&#xff09;线性探测法 &#xff08;2&#xff09;二次探测法 2、开散列 哈希桶 / 拉链法 哈希的运用 位图 set操作 …

07-阿里云镜像仓库

07-阿里云镜像仓库 注册阿里云 先注册一个阿里云账号&#xff1a;https://www.aliyun.com/ 进入容器镜像服务控制台 工作台》容器》容器服务》容器镜像服务 实例列表》个人实例 仓库管理》镜像仓库》命名空间》创建命名空间 仓库管理》镜像仓库》镜像仓库》创建镜像仓库 使…

c++11~c++20 内联命名空间

在工作&#xff0c;我们经常会引入第三方库&#xff0c;偶尔会碰到同名的函数和类型&#xff0c;造成编译冲突的问题。一般我们可以使用命名空间&#xff0c;例如 #include <iostream> #include <iostream> using namespace std;namespace S1 {void foo(){cout &l…

Meta首款多模态Llama 3.2开源:支持图像推理,还有可在手机上运行的版本 | LeetTalk Daily...

“LeetTalk Daily”&#xff0c;每日科技前沿&#xff0c;由LeetTools AI精心筛选&#xff0c;为您带来最新鲜、最具洞察力的科技新闻。 Meta最近推出的Llama Stack的发布标志着一个重要的里程碑。这一新技术的推出不仅为开发者提供了强大的多模态能力&#xff0c;还为企业和初…

重构部队信息安全:部队涉密载体建设新策略

一、完善保密体系架构 1. 加强保密规章制度&#xff1a;制定或刷新关于机密信息管理的相关规定&#xff0c;明确机密信息的生成、复制、传输、使用、储存及销毁等核心环节的操作准则与责任分配&#xff0c;确保整个流程的标准化运作。 2. 明确个人保密义务&#xff1a;通过保密…

古老的啤酒酿造技艺:传承与发扬

在人类文明的浩瀚历史中&#xff0c;啤酒酿造技艺源远流长&#xff0c;承载着世代匠人的智慧与匠心。这些古老的技艺&#xff0c;不仅是一种手艺&#xff0c;更是一种文化的传承。今天&#xff0c;我们将一起走进这神秘的酿造世界&#xff0c;探寻古老啤酒酿造技艺的传承与发扬…

性能调优知识点(mysql)三

SQL底层执行原理 MySQL的内部组件结构&#xff1a;大体来说&#xff0c;MySQL 可以分为 Server 层和存储引擎层store两部分 Server层:主要包括连接器、查询缓存、分析器、优化器、执行器等&#xff0c;涵盖 MySQL 的大多数核心服务功能&#xff0c;以及所有的内置函数&#xf…

基于Python大数据可视化的民族服饰数据分析系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码 精品专栏&#xff1a;Java精选实战项目…

基于51单片机的多通道数字电压表proteus仿真

地址&#xff1a;https://pan.baidu.com/s/1zfDI2sjSGFHkYh33Sw6gHQ 提取码&#xff1a;1234 仿真图&#xff1a; 芯片/模块的特点&#xff1a; AT89C52/AT89C51简介&#xff1a; AT89C52/AT89C51是一款经典的8位单片机&#xff0c;是意法半导体&#xff08;STMicroelectron…

【数据结构】链表(1)

【概念】 一种物理存储结构上的非连续存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的引用链接次序来实现的 也就是说&#xff0c;链表是由一个一个的节点组织起来的&#xff0c;如车厢一般&#xff0c;整体就叫做链表 【链表结构】 节点可以理解为”节点对象“&#…

详解代理模式-【静态代理与JDK动态代理】(非常的斯国一)

目录 静态代理 什么是静态代理: ​ 特点: 例子&#xff1a; JDK动态代理&#xff08;主要讲点&#xff09; 大纲&#xff1a; 1、与静态代码的联系 2、JDK动态代理的主流程 3、Proxy的源码 整体概述&#xff1a; 重要点的翻译 &#xff1a; newProxyInstance源码&am…

Adobe Photoshop 2024 v25.12 (macOS, Windows) 发布下载 - 照片和设计软件

Adobe Photoshop 2024 v25.12 (macOS, Windows) - 照片和设计软件 Acrobat、After Effects、Animate、Audition、Bridge、Character Animator、Dimension、Dreamweaver、Illustrator、InCopy、InDesign、Lightroom Classic、Media Encoder、Photoshop、Premiere Pro、Adobe XD…

算法宝典——二分查找算法

1.认识二分查找 二分查找的时间复杂度:O(logN) 二分查找属于算法中耳熟能详的一类&#xff0c;通常的我们会说只有数组有序才可以使用二分查找&#xff0c;不过这种说法并不完全正确&#xff0c;只要数据具有"二段性"就可以使用二分查找&#xff0c;即我们可以找出一…

Spring 事务管理-AOP

1. 事务管理 1.1 事务回顾 概念 事务是一组操作的集合&#xff0c;它是一个不可分割的工作单位&#xff0c;这些操作要么同时成功&#xff0c;要么同时失败。 操作 开启事务(一组造作开始前&#xff0c;开启事务)&#xff1a;start transaction / begin ; 提交事务(这组操…

网络:TCP协议-报头字段

个人主页 &#xff1a; 个人主页 个人专栏 &#xff1a; 《数据结构》 《C语言》《C》《Linux》《网络》 文章目录 前言一、TCP协议格式16位源端口号 和 16位目的端口号4位首部长度16位窗口大小32位序号 和 32位确认序号6种标记位 和 16位紧急指针 总结 前言 本文是我对于TCP协…

毕业设计选题:基于ssm+vue+uniapp的校园二手交易平台小程序

开发语言&#xff1a;Java框架&#xff1a;ssmuniappJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;M…

cmd命令大全详解

CMD是Windows操作系统中的命令行解释器&#xff0c;它允许用户通过键入命令来执行各种操作。以下是一些常用的CMD命令及其简要说明&#xff1a; dir - 显示目录中的文件和子目录。 cmddir cd - 更改当前目录。 cmdcd [目录路径] mkdir - 创建新目录。 cmdmkdir [目录名] rmd…

银河麒麟操作系统设置网卡混杂模式的方法

银河麒麟操作系统设置网卡混杂模式的方法 1、使用场景2、操作方法步骤1&#xff1a;查看网络接口信息步骤2&#xff1a;设置网卡进入混杂模式退出混杂模式 3、注意事项 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在网络管理和监控中&am…