Linux之进程概念

 9efbcbc3d25747719da38c01b3fa9b4f.gif

                                                      作者主页:     作者主页

                                                      本篇博客专栏:Linux专栏

                                                      创作时间 :2024年9月28日

9efbcbc3d25747719da38c01b3fa9b4f.gif

基本概念:

进程说白了其实就是一个程序的执行实例,正在执行的程序。

内核层面来说,就是一个担当分配资源(CPU时间,内存)的实体

写过代码的都知道,当你的代码进行编译链接之后就会形成一个可执行的程序了,这个程序本质上是一个文件,是放在磁盘上的。当我们双击这个程序让他运行起来之后,本质上是让这个程序加载到内存当中去了,因为只有加载到内存当中去CPU才能对他进行逐语句执行,而一旦将这个程序加载到内存之后,我们就不应该叫他程序了,严格意义上应该称他为进程。

描述进程-PCB

系统中可以同时存在大量的进程,当我们使用ps aux命令时便可以看见此时存在的所有进程

当我们电脑开机时,打开的第一个程序其实就是操作系统(即操作系统是第一个加载到系统中的),我们都知道操作系统是管理工作的,其中一个就是进程管理,那么我们电脑上这么多的进程,操作系统是如何进行管理的呢?

这时我想首先告诉大家一个六字真言:就是先描述,再组织,操作系统管理也是如此,操作系统作为管理者是不需要直接和被管理者进行沟通的,当一个进程出现时,操作系统会直接对其进行描述,然后对他的管理其实就是对其描述信息的管理,进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合,这个就是PCB

操作系统将每一个进程进行描述,形成一个个的进程控制块(PCB),并且通过双链表的形式将他们连接起来。

这样,操作系统只要拿到这个双链表的头指针就可以对这个双链表进行管理,这样操作系统对各个进程的管理就变成了对双链表的管理。

例如我们创建一个进程时首先就是将改进程的代码和数据加载到内存,然后操作系统对此进程进行描述形成对应的进程控制块(PCB),并将这个PCB插入到双链表中,想要退出这个进程时就直接从双链表当中删去这个节点(PCB)即可。这样一来,操作系统对于进程的管理就变成了对一个双链表的增删查改。

task_struct --PCB的一种

进程控制块(PCB)是描述进程的,再c++中我们称之为面向对象,而在c语言当中我们称之为结构体,因为Linux是用C语言写的,当然PCB也是用c语言写的了,也就是用结构体来实现的。

  • PCB实际上是对进程控制块的统称,在Linux中描述进程的结构体叫做task_struct
  • task_struct是Linux中的一种数据结构,他会被装载到RAM(内存)里并包含进程的信息

task_struct 内容分类

task_struct 就是Linux中的进程控制块,它包含着以下的一些信息。

  • 标识符:描述本进程的唯一标识符,用来区别其他标识符
  • 状态:任务状态,退出代码,推出信号等
  • 优先级:相对于其他进程的优先级
  • 程序计数器:程序中即将被执行的下一条指令的地址
  • 内存地址:包含程序代码和进程相关的指针,还有和其他进程共享的内存块中的指针
  • 上下文数据:进程执行时处理器的寄存器中的数据
  • I/O状态信息:包含显示的I/O请求等
  • 记账信息:可能包含处理器时间的总和等
  • 其他信息

查看进程

通过系统目录来查看信息:

在根目录下有一个名为proc的系统文件夹,其中包含了大量进程信息,其中一些子目录的名字为数字

这些数字其实是一某一进程的PID,对应的文件夹中记录中对应进程的各种信息,想要查看直接输入ls /proc/对应的数字 即可

通过ps命令查看:

ps与对应的指令想叠加,便可以显示对应的进程的信息 

 ps aux | head -1 && ps aux | grep proc | grep -v grep

通过系统调用获得对应的进程的PID和PPID

通过系统调用函数,getpid和getppid两个函数分别可以获得对应进程的PID和PPID

当我们执行该程序之后,这个程序会不停的执行下去

我们通过ps命令得到对应进程的PID和PPID,就可以发现和getpid与getppid得到的值是一模一样的

通过系统调用创建进程-fork初始

fork函数创建子程序

加入fork函数之后,运行结果如下:

循环打印两行数据,这两行数据分别为:第一行是该进程的PID和PPID,第二行是fork创建的子进程的PID和PPID,我们可以发现fork创建的子进程的PPID是子进程的PID,说明这两个进程的关系为父子关系。

同样,操作系统也会为这个新建的进程创建PCB。

我们知道加载到内存中的数据和代码是属于父进程的,那么子进程的数据和代码又是从哪里来的呢?

我们可以看到,fork之前的代码是父进程自己执行的,之后的代码是父子进程都执行

需要注意的是,虽然父子进程共享代码了,但是其实是各自开辟空间(采用写时拷贝);

小贴士:使用fork函数创建子进程后就有了两个进程,这两个进程的调度顺序是不确定的,取决于操作系统调度算法具体的实现。

fork返回值:

1.如果fork函数创建子进程成功,那么在父进程中返回父进程的pid,子进程的返回0

2.如果创建失败,那么父进程的返回-1.

Linux运行状态:

一个进程都创建而产生到因撤销而销毁的整个生命期间,有时占有处理器执行,有时虽然可以运行但是分不到处理器。有时虽然有空闲处理器但是由于待某个时间的发生而无法执行,这一切就说明进程和程序有区别,进程是活动的且状态变化的,所以叫做进程

这里我们具体谈一下进程中的一些状态:

Linux源代码中对于一些状态的定义:

/*
* The task state array is a strange "bitmap" of
* reasons to sleep. Thus "running" is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
static const char *task_state_array[] = {"R (running)",       /*  0*/"S (sleeping)",      /*  1*/"D (disk sleep)",    /*  2*/"T (stopped)",       /*  4*/"T (tracing stop)",  /*  8*/"Z (zombie)",        /* 16*/"X (dead)"           /* 32*/
};

运行状态——R

一个程序处于运行状态,并不一定一定处于运行当中,一个进程处于R状态,表示处于运行当中或者处于运行队列(runqueue)当中,所有会存在多个进程同时处于R状态。

浅睡眠状态——S

一个进程处于浅睡眠状态,也叫可中断睡眠状态,意味着等待某件事情的完成,处于浅睡眠状态的进程随时可以被唤醒,也可以随时被杀掉

当我们在一个进程中加入sleep(100),意思就是在这里休息一百秒,此时编译运行之后就会出现浅睡眠状态。

深睡眠状态——D

一个进程处于深度睡眠状态,表示这个进程不可以被杀掉,即便是操作系统也是不可以的,只有这个进程自动唤醒才可以恢复,该进程也被称为不可中睡眠状态。

例如:某一进程对磁盘进行写入操作时,再写入期间,就会处于D状态,是无法被杀掉的,因为该进程必须磁盘回复是否写入成功,以做出相应的回应

暂停状态——T

在Linux中,我们可以通过向进程发送SIGSTOP信号使进程进入暂停状态(T),发送SIGCONT可以让处于暂停状态的进程继续运行。

僵尸状态——Z

当一个进程将要退出的时候,在系统层面,这个系统曾经申请的资源并不会被马上释放,而是暂时存储一段时间,以供操作系统或者其父进程进行读取,如果信息一直未被读取,则相关数据会一直存在,不会被释放掉的,如果一个进程等待着数据被读取,那么我们就说他正处在僵尸状态。

僵尸状态时应该存在的,因为我们调用一个程序时,调用方是应该知道这个完成情况的,所以僵尸状态必须是要存在的,以便后续的相关操作。

例如我们在编写程序时都会在最后写一个return 0,它的作用就是告诉操作系统这个程序顺利完成结束了。

在Linux中,我们可以通过echo $命令来获取最近的一次进程的退出码

死亡状态——X

死亡状态只是一个返回状态,当一个进程的信息被退出之后,该进程申请的资源会立即被释放,所以你不会在进程状态中看到死亡状态。

僵尸进程

前面我们已经说过僵尸状态的概念,相信大家也有了一个大致的了解,而处于僵尸状态的进程,就被称为僵尸进程。

例如,对于下面的代码,当程序执行五次之后,子进程便会退出,但是父进程不知道它退出了,那么此时子进程就处于僵尸状态。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{printf("I am running...\n");pid_t id = fork();if(id == 0){ //childint count = 5;while(count){printf("I am child...PID:%d, PPID:%d, count:%d\n", getpid(), getppid(), count);sleep(1);count--;}printf("child quit...\n");exit(1);}else if(id > 0){ //fatherwhile(1){printf("I am father...PID:%d, PPID:%d\n", getpid(), getppid());sleep(1);}}else{ //fork error}return 0;
} 

运行该代码之后,我们可以通过下面这个简单的脚本进行监控

while :; do ps axj | head -1 && ps axj | grep proc | grep -v grep;echo "######################";sleep 1;done

运行之后我们发现:当子进程退出之后,子进程就会编程僵尸状态。

僵尸进程的危害:

  1. 僵尸进程的退出状态会一直维持下去,因为它需要告诉父进程执行的相应的结果信息。但是父进程一直在不停的执行,所以子进程就一直处于僵尸状态
  2. 僵尸进程的信息会一直存在与task_struct(PCB)中,所以PCB就需要一直去维护
  3. 若一个父进程创建了多个子进程,并且不对其进行回收,那么就会造成资源浪费,因为数据结构对象本身就要占据内存
  4. 僵尸进程越来越多,申请的资源无法进行回收,那么僵尸进程越多,实际可用的资源就越少,也就是说僵尸进程会造成内存泄漏

孤儿进程:

孤儿进程是指在操作系统中,其父进程已经结束(正常或异常终止),但该进程本身还在继续运行的进程。当一个进程创建子进程后,如果父进程在子进程结束之前就已经退出,那么子进程就会成为孤儿进程。例如,一个父进程启动了一个子进程,之后父进程由于某种原因(如完成了它的任务或者遇到了错误而终止)退出,此时子进程就变成了孤儿进程。

对于以下代码,父进程执行五次后退出,子进程就变成了孤儿进程:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{printf("I am running...\n");pid_t id = fork();if(id == 0){ //childint count = 5;while(1){printf("I am child...PID:%d, PPID:%d\n", getpid(), getppid(), count);sleep(1);}}else if(id > 0){ //fatherint count = 5;while(count){printf("I am father...PID:%d, PPID:%d, count:%d\n", getpid(), getppid(), count);sleep(1);count--;}printf("father quit...\n");exit(0);}else{ //fork error}return 0;
} 

观察代码运行,会发现父进程退出后,子进程的PPID变成了1,这就说明他被一号进程领养了

进程优先级:


基本概念:

什么是进程优先级?优先级实际上就是进程获取某些资源的先后顺序,而进程优先级实际上就是进程获取CPU资源分配的先后顺序,就是指进程的优先权,优先权高的进程具有优先执行的权力

为什么要有进程优先级?

进程优先级存在的主要原因就是我们的资源是有限的,就像我们的电脑,一般都是单CPU的,一个CPU一次只能执行一个进程,而进程有多个,所以需要存在进程优先级,确定获取CPU资源的先后顺序。

查看系统进程

在Linux中输入ps -l可以看到以下的东西:

我列出其中重要的几个信息:

  • UID:代表着执行者的身份
  • PID:代表这个进程的代号
  • PPID:代表着这个进程由哪一个进程发展而来,即父进程的代号
  • PRI:代表这个进程的可被执行的优先级,值越小越早被执行
  • NI:代表着这个进程的nice值

PRI和NI

  • PRI代表着进程的优先级,这个值越小进程的优先级越高
  • NI代表着nice值,其表示进程可被执行的优先级的修正数值
  • PRI(new)=PRI(old)+NI;
  • 若NI值为负值,那么PRI变小,优先级变高
  • 调整进程的优先级,在Linux下就是调整NI,即nice值
  • NI的范围是-20-19,也就是说进程优先级一共分为四十个级别

注意:在Linux系统中,PRI默认的值是80,也就是PRI=80+NI。

当我们创建一个进程之后,我们可以通过ps -al查看进程的优先级。

通过top命令修改进程优先级

这里的top命令其实就相当于Windows中的任务管理器,可以调整进程优先级。

使用top命令后按r,然后输入要调整的进程的PID,然后调整后的nice值即可

注意:要想将nice调整为负值,需要加上sudo提升权限

通过renice调整进程优先级

输入 renice +更改后的nice +PID即可

四个重要概念:

竞争性:由于只有一个CPU,所以资源有限,会出现资源竞争,为了高效完成任务,合理分配CPU资源,所以会出现进程优先级

独立性:多进程之间运行需要独享各种资源,运行期间互不打扰

并发:即多个进程在一个进程下采用进程切换的方式,在一段时间段内,让多个进程都得以共同推进,称之为并发

并行:多个进程在多个CPU下同时进行

环境变量:

基本概念

环境变量一般是指在操作系统中指定操作系统运行环境的一些参数

常见环境变量

  • PATH:指定命令的搜索路径
  • HOME:指定用户的主工作目录
  • SHELL:当前shell,他的值一般是/bin/bash

查看当前环境变量的方法

使用echo来查看:

echo $NAME //NAME为待查看的环境的名称

测试PATH

大家有没有想过这样一个问题:为什么执行ls命令时不用带./,而在执行我们自己的可执行程序时就必须要带上?

容易理解的是,我们在执行一个程序的时候,必须要先找到他在哪里,既然不带ls就可以执行ls,说明操作系统可以找到他,而系统找不到我们的可执行程序,必须带上./来说明他在我们的当前目录下。

而系统就是通过环境变量PATH来找到ls的,查看环境变量PATH,可以看到下面内容:

可以看到很多路径,这些路径通过冒号隔开,然后执行ls命令时,系统会从左到右开始寻找ls命令

而ls命令确实存在与这些路径中的某个路径下面。

那我们可不可以让自己的可执行程序不带./就执行呢

两个方法:

  • 一个就是在系统默认的路径下创建这个程序,然后生成可执行程序或者将这个可执行程序拷贝到PATH的某一个路径下面
  • 将可执行程序所在的路径导入到PATH这个路径下面

部分环境变量说明:

set:显示本地定义的shell变量和环境变量

unset:清楚环境变量

通过代码来获取环境变量:

你知道main函数其实是由参数的嘛?

我们平常情况不会使用他,所以基本不会写出来。

在这里我们可以看到,调用main函数时向其传递了三个参数。

我们先来说说前两个参数

我们在Linux下写下这个程序并运行:

                                                                                                                                                                                 
#include <stdio.h>
int main(int argc,char *argv[])
{
for(int i=0;i<argc;i++)
{
printf("argv[%d]:%s\n",i,argv[i]);                                                                                                                                                                 }                                  return 0;                        }                                    

执行结果如下:

现在我们来说说main函数中的前两个参数,其中的第二个参数是一个字符指针数组,数组中第一个字符指针存储的是可执行程序的位置,其余字符指针存储的是所给的若干选项。最后一个指针为空,而前面那个第一个参数就代表着字符指针数组中有效元素的个数

下面我们来写一个简单的代码,这个代码运行起来之后会根据你所给的选项的不同给出不同的提示语句:

现在我们来说说main的第三个参数:

main的第三个参数实际上是接受的环境变量表

可通过他获得系统的环境变量:

通过系统调用获取环境变量:

程序地址空间:

下面我们来验证一下:

最后:

十分感谢你可以耐着性子把它读完和我可以坚持写到这里,送几句话,对你,也对我:

1.一个冷知识:
屏蔽力是一个人最顶级的能力,任何消耗你的人和事,多看一眼都是你的不对。

2.你不用变得很外向,内向挺好的,但需要你发言的时候,一定要勇敢。
正所谓:君子可内敛不可懦弱,面不公可起而论之。

3.成年人的世界,只筛选,不教育。

4.自律不是6点起床,7点准时学习,而是不管别人怎么说怎么看,你也会坚持去做,绝不打乱自己的节奏,是一种自我的恒心。

5.你开始炫耀自己,往往都是灾难的开始,就像老子在《道德经》里写到:光而不耀,静水流深。

最后如果觉得我写的还不错,请不要忘记点赞✌,收藏✌,加关注✌哦(。・ω・。)

愿我们一起加油,奔向更美好的未来,愿我们从懵懵懂懂的一枚菜鸟逐渐成为大佬。加油,为自己点赞!

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于大数据可视化的图书推荐及数据分析系统

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

Unity开发绘画板——02.创建项目

1.创建Unity工程 我们创建一个名为 DrawingBoard 的工程&#xff0c;然后先把必要的工程目录都创建一下&#xff1a; 主要包含了一下几个文件夹&#xff1a; Scripts &#xff1a;存放我们的代码文件 Scenes &#xff1a;工程默认会创建的&#xff0c;存放场景文件 Shaders &…

8621 二分查找

**思路&#xff1a;** 1. 读取输入的元素个数 n。 2. 读取有序数组 ST。 3. 读取要查找的关键字 key。 4. 使用折半查找法&#xff08;即二分查找&#xff09;在数组 ST 中查找 key 的位置。 5. 如果找到 key&#xff0c;输出其位置&#xff1b;如果未找到&#xff0c;输出 &qu…

[Linux]:线程(二)

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;Linux学习 贝蒂的主页&#xff1a;Betty’s blog 与Windows环境不同&#xff0c;我们在linux环境下需要通过指令进行各操作&…

自然语言处理实战项目:从基础到实战

自然语言处理实战项目&#xff1a;从基础到实战 自然语言处理&#xff08;Natural Language Processing, NLP&#xff09;是人工智能的重要分支&#xff0c;致力于让计算机能够理解、生成和处理人类语言。NLP 在搜索引擎、智能客服、语音助手等场景中扮演着关键角色。本文将带…

使用python进行自然语言处理的示例

程序功能 分词&#xff1a;将输入句子拆分为单词。 词性标注&#xff1a;为每个单词标注其词性。 命名实体识别&#xff1a;识别命名实体&#xff08;如人名、地名、组织等&#xff09;。 这段代码展示了如何用 nltk 进行基础的 NLP 任务&#xff0c;包括分词、词性标注和命名…

Django Web开发接口定义

Django Web 介绍 Django Web是一个Pyhton高级 Web 框架,实际上 Django 也可以做到前后端分离,即主要作为后端框架使用,不用模板渲染也是可行的。 Django Web 应用的运行流程,如下图所示: 此外,Django Web 在开发环境可以通过自带的服务器进行本地调试。但是该服务器不适…

Spring - @Import注解

文章目录 基本用法源码分析ConfigurationClassPostProcessorConfigurationClass SourceClassgetImportsprocessImports处理 ImportSelectorImportSelector 接口DeferredImportSelector 处理 ImportBeanDefinitionRegistrarImportBeanDefinitionRegistrar 接口 处理Configuratio…

从零预训练一个tiny-llama#Datawhale组队学习Task2

完整的教程请参考&#xff1a;datawhalechina/tiny-universe: 《大模型白盒子构建指南》&#xff1a;一个全手搓的Tiny-Universe (github.com) 这是Task2的学习任务 目录 Qwen-blog Tokenizer&#xff08;分词器&#xff09; Embedding&#xff08;嵌入&#xff09; RMS …

【2025】基于Django的鱼类科普网站(源码+文档+调试+答疑)

文章目录 一、基于Django的鱼类科普网站-项目介绍二、基于Django的鱼类科普网站-开发环境三、基于Django的鱼类科普网站-系统展示四、基于Django的鱼类科普网站-代码展示五、基于Django的鱼类科普网站-项目文档展示六、基于Django的鱼类科普网站-项目总结 大家可以帮忙点赞、收…

Codeforces Round 975 (Div. 2) A-C 题解

这次看到 C 题分数 1750 就开始害怕了&#xff0c;用小号打的比赛&#xff0c;一直觉得做不出来&#xff0c;最后才想到 A. Max Plus Size 题意 给你一些整数&#xff0c;选择一些涂成红色&#xff0c;两两不能相邻&#xff0c;你的得分为&#xff1a; [ 红色元素的个数 ] …

什么是 JWT?它是如何工作的?

松哥最近辅导了几个小伙伴秋招&#xff0c;有小伙伴在面小红书时遇到这个问题&#xff0c;这个问题想回答全面还是有些挑战&#xff0c;松哥结合之前的一篇旧文和大伙一起来聊聊。 一 无状态登录 1.1 什么是有状态 有状态服务&#xff0c;即服务端需要记录每次会话的客户端信…

努比亚z17努比亚NX563j原厂固件卡刷包下载_刷机ROM固件包下载-原厂ROM固件-安卓刷机固件网

努比亚z17努比亚NX563j原厂固件卡刷包下载_刷机ROM固件包下载-原厂ROM固件-安卓刷机固件网 统版本&#xff1a;官方软件作者&#xff1a;热心网友rom大小&#xff1a;911MB发布日期&#xff1a;2018-12-23 努比亚z17努比亚NX563j原厂固件卡刷包下载_刷机ROM固件包下载-原厂RO…

JVM相关的命令汇总

一、简介 虽然目前市场上有很多成熟的 JVM 可视化监控分析工具&#xff0c;但是所有的工具其实都依赖于 JDK 的接口和底层相关的命令&#xff0c;了解这些命令的使用对于在紧急情况下排查 JVM 相关的线上故障&#xff0c;会有更加直观的帮助。 下面一起来看看 JVM 常用的命令…

图像处理基础知识点简记

简单记录一下图像处理的基础知识点 一、取样 1、释义 图像的取样就是图像在空间上的离散化处理,即使空间上连续变化的图像离散化, 决定了图像的空间分辨率。 2、过程 简单描述一下图象取样的基本过程,首先用一个网格把待处理的图像覆盖,然后把每一小格上模拟图像的各个…

五、CAN总线

目录 一、基础知识 1、can介绍 2、CAN硬件电路 3、CAN电平标准 4、CAN收发器芯片介绍 5、CAN帧格式 ① CAN帧种类 ② CAN数据帧 ③ CAN遥控帧​编辑 ④ 位填充 ⑤ 波形实例 6、接收方数据采样 ① 接收方数据采样遇到的问题 ② 位时序 ③ 硬同步 ④ 再同步 ⑤ 波…

1.8 软件业务测试

欢迎大家订阅【软件测试】 专栏&#xff0c;开启你的软件测试学习之旅&#xff01; 文章目录 前言1 概述2 方法3 测试策略4 案例分析 前言 在软件开发生命周期中&#xff0c;业务测试扮演着至关重要的角色。本文详细讲解了业务测试的定义、目的、方法以及测试策略。 本篇文章参…

信息安全数学基础(22)素数模的同余式

前言 信息安全数学基础中的素数模的同余式是数论中的一个重要概念&#xff0c;它涉及到了素数、模运算以及同余关系等多个方面。 一、基本概念 素数&#xff1a;素数是指只能被1和它本身整除的大于1的自然数。素数在密码学中有着广泛的应用&#xff0c;如RSA加密算法就依赖于大…

订餐点餐|订餐系统基于java的订餐点餐系统小程序设计与实现(源码+数据库+文档)

订餐点餐系统小程序 目录 基于java的订餐点餐系统小程序设计与实现 一、前言 二、系统功能设计 三、系统实现 四、数据库设计 1、实体ER图 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xff1a;✌️大厂码农|毕设布…

9.29 LeetCode 3304、3300、3301

思路&#xff1a; ⭐进行无限次操作&#xff0c;但是 k 的取值小于 500 &#xff0c;所以当 word 的长度大于 500 时就可以停止操作进行取值了 如果字符为 ‘z’ &#xff0c;单独处理使其变为 ‘a’ 得到得到操作后的新字符串&#xff0c;和原字符串拼接 class Solution { …