Linux | 浅谈Shell运行原理【王婆竟是资本家】

在这里插入图片描述
在这里插入图片描述

💧Shell的运行原理

👉Shell的基本概念与作用

Linux严格意义上说的是一个操作系统,我们称之为“核心(kernel)“ ,但我们一般用户,不能直接使用kernel。而是通过kernel的“外壳”程序,也就是所谓的shell,来与kernel沟通。如何理解?为什么不能直接使用kernel?

  • 因为对于普通用户去直接使用OS的内核存在一定的风险性,所以在这个内核(kernel)的外层,就包裹了一层shell,我们虽然不能直接访问操作系统的内核,但是可以通过这层shell与OS内核进行一个中间交互,从而与内核去产生一个互动

在Windows中,我们以GUI图形化界面作为交互方式;在Linux中,我们以命令行作为交互方式

  • 但它们本质上是一样的,都是为了让用户进行相关操作,而图形化界面和命令行界面就是我们所说的“外壳程序”【shell】
  • 说了这么多,其实对于Shell的最简单定义:命令行解释器(command Interpreter)主要包含:
  • 将使用者的命令翻译给核心(kernel)处理
  • 同时,将核心的处理结果翻译给使用者

👉原理的展示与剖析

为了更好地帮助理解,我画了如下图示
在这里插入图片描述

  • 对比windows GUI,我们操作windows 不是直接操作windows内核,而是通过图形接口点击,从而完成我们的操作(比如进入D盘的操作,我们通常是双击D盘盘符.或者运行起来一个应用程序
  • shell 对于Linux,有相同的作用,主要是对我们的指令进行解析,解析指令给Linux内核。反馈结果在通过内核运行出结果,通过shell解析给用户。

在这里插入图片描述

  • 其实对于Shell,说白了它就是一个【命令行解释器】,等待我们去输入一条指令然后对这条指令进行一个解析,把解析之后的命令传递给操作系统,然后把结果以特定的方式拿到后以不同的形式展现给用户,比如说“打印”

在这里插入图片描述

  • 从作用上来将,Shell其实和我们现在所使用的图形化界面是一模一样的,比方说在Windows中我们双击打开了软件这个文件夹后,也就进入了这个文件夹,查看到了里面所放的内容,看到了另外一个界面,这其实就和【cd】+【ls】是一个道理,只是获取输入和显示的方式不一样。
  • 对于Windows的图形化界面来说它的制作更加复杂一些,因此用户体验感更好,Linux的这种纯命令行式的操作比较直观一些,但是体验感不是很佳

【于是有个同学他就说,对于我们在Windows下点击一个图标时操作系统是不是将它解析成指令然后反馈给操作系统去执行】
===》完全不是❌,大家不要将Linux中的指令和Windows中的图形化界面混为一谈。当你点击图形化界面这种操作的时候,它就直接被操作系统执行了,如果非要说有指令存在的话那执行的就是这段指令的代码,双击图标显示就是执行【cd + ls】的代码。而不是说点击了某个图标就是变成了对应的指令去执行

对于Shell这一块的理解光这么说说坑大家还是难以深刻地理解,所以我们通过一个生活中的小案例来看看👀


👉Shell外壳感性理解【一门亲事】

了解了Shell的一些基本概念和原理之后,接下去我们通过一个小场景来感性地理解一下Shell

  • 故事背景是这样的:你呢叫张三,你的爸爸是村里的村长,在毕业后你找了一份很好的工作回到家里,此时你的爸爸说:“儿子呀,现在你已经完成了一件很重要的事情,就是找到一份得体的工作,但是呢,还有一件更加重要的事需要你去完成,你看你年纪也不小了,也到了谈婚论嫁的地步,那是不是应该去找个女朋友谈谈呢”
  • 此时你回应说:老爸,我听喜欢我们家隔壁的那个小花,她也是我的初中同学。你老爸听了你的这番说辞,立马就找到了村东头的王婆,王婆呢也是你老爸的老同学了,现在想要找她帮你说个媒,那对于村长的请求,王婆肯定是不能拒绝,当天立马就去了小花家里,和她说:“小花呀,最近又漂亮了不少,你看你现在还单身着呢,隔壁的张三现在找到了一份很好的工作,他爸也是村长,你们两个也是老同学,年纪也相当,看看能不能和他处处。”但是呢小花她不喜欢你呀,于是就对王婆说:“我对他不感兴趣,叫他别来找我了”,那此时王婆也是非常无奈呀,回到村长家之后,就跟你和你爸爸说了小花的态度。

通过上面的小案例,我们可以去理解Shell外壳的工作原理,这里可以将张三也就是你看做是用户,然后将小花呢看做是操作系统,平常我们来使用操作系统的时候,其实并不是直接对操作系统的内核进行处理,而是通过鼠标的点击进行一些对应的访问,此时呢你和操作系统之间就存在着一个交互的东西叫做Shell,Shell可以作为你和操作系统之间的一个传递者,通过Shell让操作系统知道你的想法,然后操作系统执行你的命令,再通过Shell返回出去,所以可以看出这个王婆也就是Shell,起到一个中介的作用

在这里插入图片描述

  • 但是事情完了吗,很明显没有,因为可以看到图中还有一个李四呢🐶,因为你呢就是喜欢小花,还是想要再坚持一下,于是你老爸就又找到了王婆,并且给了她一些好处,此时王婆就不得不再度厚着脸皮去找小花,但是当王婆走到小花家门前的时候,看到她隔壁家的李四正手拉手坐在沙发上看电视,旁边还坐着双方的父母,经验丰厚的王婆看到这一幕心想:这生米都快煮成熟饭了,我在去把电线拔了是不是不太好呀🍚
  • 于是这个时候王婆就毅然决然地放弃了这个念头,想要回到你家和说明这件事,但是呢因为你老爸是村长,所以王婆比较忌惮,所以想了想:而且若是带着这样的消息回来的话,不是坏了我这么多年积攒下来的名誉了。所以还是先选择回到了自己家,此时。。。

上面的描述其实就反映了Shell的一些原理===> ①帮忙进行命令传递和返回结果 ②保护操作系统(不去打扰小花)

在这里插入图片描述

  • 事情完了,当然没有😜
  • 当王婆带着万千思绪准备回家的时候她突然想到:为什么要我自己去做这件事呢,我招募几个人替我去说媒,这样若是失败了既不会影响我的名誉,又可以把事情办成,毕竟我这么个老太婆怎么说得动现在的年轻人呢。所以此时王婆就上招聘网站招募了几个实习生,然后对她们培训了一段时间后让她们去进行说辞,然后就带回了好消息,不得不说现在的年轻人都怎么想的,随随便便就在一起,然后一言不合就分手了,可谓是亵渎爱情呀❤
  • 有了这样的好消息,此时王婆就马上开启她的小毛驴到村长家邀功,然后你就可以趁虚而入了😍

上述的王婆(资本家)招募实习生这段故事其实就是Shell在执行用户所给出的命令时,会创建子进程来执行

在这里插入图片描述
看完上面的这个生活小案例,你是否对Shell有了一些深入的理解呢?

💧总结

好,我们来总结一下本文所学习的内容

  • 在本篇文章中,首先了解了一下什么事shell,它是用于与操作系统之间的一个中间者用于将用户执行的一些操作转达给操作系统,然后操作系统执行之后再反馈给shell,最后通过shell将执行的结果反馈给操作的用户
  • 理解了shell的基本概念和作用之后,便对其原理展开剖析,讲了对于Windows中的鼠标点击操作和Linux中的命令行操作其实本质都是一样的,对于双击图标也就是我们执行的cd + ls的执行。但是有一点要明白的是对于Windows中的图形化界面操作和Linux中的命令行操作还是存在本质区别的,不要混淆了
  • 在最后,我们又通过一个小故事对shell有了一个感性的理解,相信你在看了这个小故事后一定是有了自己的想法,也想明白了一些事情

若有所思,特作此分享,2023年1月20日晚18点

在这里插入图片描述

<think>好的,我现在需要在C#中实现统计字符串中姓氏数量以及排名前三的姓氏及其数量。用户提供的示例字符串是“张三李四王五赵六张三丰张无忌王婆赵云赵敏赵无极”,预期结果应该是姓氏有4个,前三名是赵(4次)、张(3次)、王(2次)。首先,我需要理清步骤,然后逐步解决每个环节的问题。首先,用户提到要使用循环、字典和切割。在C#中,处理字符串分割可以用Split方法,将字符串按空格分割成数组。这一步应该没问题,但需要注意是否有多个连续空格的情况,不过示例字符串中的分隔符是单个空格,所以暂不需要处理这种情况。 接下来是统计姓氏的数量。每个名字的第一个字符是姓氏,所以需要遍历每个名字,提取姓氏。例如,“张三”的姓氏是“张”,“赵六”是“赵”等。这里需要遍历分割后的数组,对每个元素取第一个字符。在C#中,字符串的索引可以直接访问字符,比如name[0],但要注意字符串是否为空或长度为0,不过根据示例数据,名字都是有效的,所以暂不需要处理异常情况。然后使用字典来统计每个姓氏的出现次数。字典的键是姓氏,值是对应的数量。遍历每个姓氏,如果字典中已存在该键,则增加值;否则,添加键并设置值为1。这一步应该没问题,但需要注意字典的大小写问题,比如“张”和“张”是否会被视为不同,但示例中都是中文姓氏,没有大小写问题,所以可以忽略。统计完成后,需要按数量降序排序,并取前三位。这里涉及到对字典的排序。在C#中,可以使用LINQ的OrderByDescending方法,然后Take(3)来获取前三项。但需要注意,如果有并列第三的情况,比如两个姓氏数量相同,这候可能需要处理,但用户示例中没有这种情况,暂按简单处理。现在,将这些步骤转化为C#代码。首先,分割字符串,然后遍历每个名字提取姓氏,统计到字典中。接着排序并取前三。测试一下示例数据,看看是否得到正确的结果。示例字符串分割后的数组是:["张三","李四","王五","赵六","张三丰","张无忌","王婆","赵云", "赵敏", "赵无极"]。提取姓氏后得到:张、李、王、赵、张、张、王、赵、赵、赵。统计后,张3次,李1次,王2次,赵4次。总共有4个姓氏,前三名是赵4,张3,王2。所以结果应该正确。可能的错误点:姓氏提取是否正确,比如复姓的情况,但示例中没有复姓,都是单姓,所以没问题。如果存在复姓,比如“欧阳”,那么这种方法会错误地提取“欧”作为姓氏。但用户的问题中可能不需要考虑复姓,因为示例中没有,所以暂忽略这一点。 另外,排序,如果有相同数量的姓氏,可能需要按字母顺序排序,但用户示例中没有这种情况,所以按数量排序即可。如果有并列第三,可能需要处理,但用户的问题中没有提到,所以暂不处理。现在编写代码:1.分割字符串:string[]names =info.Split('');2.遍历每个名字,提取姓氏:foreach (string namein names) {if(name.Length ==0)continue;charsurname =name[0];stringsurnameStr= surname.ToString();//更新字典}3.使用字典统计:Dictionary<string, int> surnameCount =new Dictionary<string,int>();foreach...中,检查字典是否包含surnameStr,存在则增加,否则设为1。4.排序:var sorted= surnameCount.OrderByDescending(pair =>pair.Value).Take(3); 5.输出结果。 测试代码是否正确,输出是否符合预期。现在检查示例中的姓氏数量是否正确。赵出现4次(赵六、赵云、赵敏、赵无极),张出现3次(张三、张三丰、张无忌),王出现2次(王五、王婆),李出现1次(李四)。总共有4个姓氏,前三名是赵4,张3,王2。所以代码应该正确。可能的问题:在C#中,Split方法如果字符串中有多个空格,可能会分割出空字符串,但示例中没问题,所以可以忽略。此外,姓氏提取是否正确,比如“张无忌”中的第一个字符是“张”,正确。综上,代码应该是正确的。现在需要将步骤转化为C#代码,并确保正确性。</think>在C#中实现姓氏统计功能,可以通过以下步骤完成: ### 实现步骤 1. **字符串切割**:使用`Split()`方法分割原始字符串 2. **姓氏提取**:遍历每个名字取首字符作为姓氏 3. **字典统计**:使用`Dictionary<string, int>`进行频次统计 4. **排序处理**:通过LINQ进行降序排序并取前三位 ```csharp using System; using System.Collections.Generic; using System.Linq; class Program { static void Main() { string info = "张三 李四 王五 赵六 张三丰 张无忌 王婆 赵云 赵敏 赵无极"; // 1.切割字符串 string[] names = info.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries); // 2.创建统计字典 Dictionary<string, int> surnameCount = new Dictionary<string, int>(); foreach (string name in names) { if (name.Length == 0) continue; string surname = name[0].ToString(); if (surnameCount.ContainsKey(surname)) surnameCount[surname]++; else surnameCount[surname] = 1; } // 3.排序并取前三 var top3 = surnameCount.OrderByDescending(kv => kv.Value) .Take(3) .ToList(); // 输出结果 Console.WriteLine($"总姓氏数量: {surnameCount.Count}"); Console.WriteLine("前三名:"); foreach (var item in top3) { Console.WriteLine($"{item.Key}: {item.Value}次"); } } } ``` ### 关键实现原理 1. **字符串处理**:`Split()`方法的间复杂度为$O(n)$,其中n为字符串长度[^1] 2. **字典操作**:哈希表查询和更新操作的平均间复杂度为$O(1)$[^2] 3. **排序算法**:LINQ的`OrderByDescending`使用快速排序,间复杂度为$O(m \log m)$,其中m为不同姓氏数量[^4] ### 输出示例 ``` 总姓氏数量: 4 前三名: 赵: 4次 张: 3次 王: 2次 ``` ### 性能优化建议 1. 对于大数据量可使用`ConcurrentDictionary`实现线程安全统计 2. 采用并行处理优化切割和统计过程: ```csharp Parallel.ForEach(names, name => { string surname = name[0].ToString(); lock(surnameCount) { // 更新字典 } }); ``` 3. 使用`Span<T>`优化内存分配: ```csharp ReadOnlySpan<char> span = info.AsSpan(); ```
评论 82
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

烽起黎明

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值