学习系统编程No.9【文件操作】

引言:

北京时间:2023/3/23/6:34,可能是昨天充分意识到自己的摆烂,所以今天起的比较早一点吧!昨天摆烂的头号原因,笔试强训,加上今天4节课,可以说一整天都是课,所以能不能更新博客,完全取决于,能不能合理的规划好空闲时间,并且今天也还需要完成一份笔试强训,所以说今天想要更新博客可以说是难如登天!哈哈哈,不过不怕,咱是小强吗?就是造,所以今天让我们抓紧来学习一下新知识吧!深入bash自我实现,处理更多细节,完善简易bash,浅谈什么是文件操作,目标不大,内容不多,So,Let’s go!

在这里插入图片描述

复盘bash实现

上篇博客我们了解了7个进程程序替换接口,发现execve和其它的6个接口是不一样的,其余的6个接口都是通过封装execve这一个接口实现的,Linux系统本质要实现这么多系统调用接口的原因就是:因为C语言不支持函数重载的,所以为了更好的匹配场景使用,就需要提供更多的接口 ,来满足各种场景的使用,那个场景合适就调用那个接口,不像是C++,可以支持函数重载的方式去对同一个函数进行不同的参数传递,虽然底层的实现不变,但是在函数调用的时候,是比较方便的,因为所有的接口都是同一个名字,你只需要明白自己的参数就行了;所以可以从系统调用接口上的区别,明白,语言之间也是同理的,无论是C++、C、Java、Python,它们也只是在封装方式上不同而已,在操作系统看来,无论什么语言,只要能和操作系统交互,能够调用到操作系统的系统调用,就可以了,所以无论使用什么语言,在编写成代码的时候,它的本质也就不过是一个进程而已(系统调用接口才是本质)

并且在上篇博客中,我们将bash的基础给实现了,但是发现一个问题,就是我们自己实现的bash只能实现一些非常简单的指令(ls,pwd),像cd指令和export等指令都是不支持的,所以这篇博客,我们就来学习一下为什么我们自己实现的bash不支持,系统中的bash就可以支持呢?此时就涉及到了一个叫内置命令/内建命令的概念了,所以想要明白这个问题,就让我们把我们的bash给细节化一下,由浅入深的再来学习一下bash吧!

复习7大函数命名上的特点

l 代表的是参数以命令行列表的形式,一个一个的传递给程序替换函数
v代表的是直接将命令行参数,包括程序名(ls),包括程序选项(-a)以数组的形式直接传递给进程替换函数
p代表的是,在搜索程序时,只需携带程序名称就行,不需要包含路径
e代表的就是是否需要使用自定的环境变量,不想使用父进程也有方法把它的整个环境变量继承给你(也就是bash,无论是是否手动传递,它都有方法,把环境变量继承给你),当然更重要的是,我们可以手动实现环境变量传给程序替换函数

bash进阶:

输入的指令需要进行切割的原因是因为,程序替换函数只能一个一个程序指令的识别,不可以一下识别一串,所以要将一串给切割开,然后一个一个选项的识别

进阶第一点:
系统指令是带颜色的,而我们自己的bash指令是不带颜色的
可以通过which查看:which ls 发现是因为系统中的指令是因为有一个 --color = auto的选项
如下图:
在这里插入图片描述
所以我们也可以对我们的bash程序进行颜色的设定,如下图:

在这里插入图片描述

进阶第二点:
cd指令不能用,更改路径不会起效果
本质原因:路径为什么没有变化呢?原因:目前我们自己实现的bash可执行文件,它本质还是一个可执行程序,本质还是是Linux系统bash进程的子进程,这个身份关系一定要明白,所以我们在代码中执行想要执行相应的指令时,我们就不能给子进程去执行,而是要让父进程(bash)自己去执行,因为只有父进程才有权利执行,所以像cd这种只有父进程可以执行,而子进程无法执行的命令,我们就叫做内建命令/内置命令

但是我们会发现一个新的问题,就是应该如何让bash自己执行内置命令呢?
所以此时为了可以让bash自己执行内置命令,所以此时操作系统就为我们提供了一个接口,chdir 就一个参数const char* path;所以只要把路径字符串给给它就行了,它就会自己去调用,然后进行相应的路径切换,这样就很好的完成了cd指令的实现(在自己的bash文件中),成功返回0,失败返回-1;所以上述我们的理解都是正确的,只有系统的bash可以执行想cd这样的内置命令,就算是我们自己的bash文件中的父进程也是没有这个权利的,更别谈,bash文件中的子进程(也就是系统bash的孙子进程),如下图:
在这里插入图片描述

深入内置命令:
例如:我们的export指令
同理,此时这个export肯定是bash设置的,别的进程都是子进程,所以想要使用,就必须只能是继承bash,所以同理,如果想要在我们自己的bash文件中使用export指令,就一定需要使用系统调用的接口,getenv ,只有这样,我们才可以使用它在我们自己的bash文件中间接的完成export功能

但是最后发现,就算我们在自己的bash文件中使用了putenv把export导给bash执行,此时我们的bash文件也不可以使用export第一个猜测不行的原因:我们最后导入环境变量的时候,使用的是execvp这个接口,导致无法把父进程(bash)的环境变量继承给bash文件中的子进程,所以,在使用bash文件中的子进程的使用,应该要去调用execvpe这个接口,因为这个接口,不仅可以传execvpe对应的参数,此时最重要的就是它还可以传一个环境变量environ给给调用进程,当然此时也就是我们的bash文件中的子进程(所以此时这个子进程,就可以使用系统bash对应的所有环境变量),自然而然,由于上述我们已经把export给导入过了,所以此时就可以利用export接口往系统bash的环境变量导入数据,并且拿到系统bash的所有环境变量,所以就可以拿到我们想要导入的环境变量了,但是此时发现,我们还是不能成功,原因就是: execvpe这个接口和execvp本质上是一样的,因为系统bash的环境变量无论是使用哪个接口,此时它都是会被传递的,所以我们有没有传,其实本质都是一样的,所以无论是使用execvpe还是execvp都是一样的

所以其实问题的本质就是:我们把export通过putenv传给bash的时候,此时的这个putenv中的内容是变化的,会一直覆盖我导入的环境变量,导致出问题,所以,一般用户自定义的环境变量,要用户自己进行维护,不要用一个经常被覆盖的缓冲区来保存环境变量,所以需要我们自己来维护一段缓冲区,如下图:
在这里插入图片描述

所以综合上述的现象,此时就告诉了我们一个道理,就是,当我们导出环境变量的时候,我们就不能把这个环境变量放在一个会变化的字符串中,一定要让它可以被持久保存(涉及指针指向的地址问题),

并且这边要记住,使用env指令,就可以访问我们系统中的所有环境变量;并且明白一个点,就是子进程只能执行部分程序,有的程序只有父进程可以执行,例如:export

总结:其实我们之前学习到的所有的(几乎)环境变量命令,都是内置命令,需要让系统bash自己去执行这个操作,因为这些操作都是需要调用真正的系统调用接口才可以完成的,例如上述所说的:putenv/chdir或者是我们的自定义函数,showEnv(),都是bash自己维护的环境变量,只有bash把这些环境变量维护好了,以后在bash执行指令,也就是子进程的时候(例如:我们自己实现的bash文件),才会将自写环境变量通过execve接口等的方式,把这个环境变量继承下去,所以想要在我们自己实现的bash文件中使用系统bash的所有环境变量,就必须利用好系统调用接口

进阶第三点:
echo命令的实现,同理,如下图:
在这里插入图片描述

总结:通过mybash的编写,此时我们充分理解了什么是环境变量和父子之间的继承关系是怎么样的,并且可以更加合理的去使用那7大函数,并且更加的明白了什么是内置命令,因为我们会明白,如果想要执行普通的命令,让我们的子进程 去执行就行了,但是如果想要执行内置命令,此时就必须要让父进程,也就是bash自己去执行才行,所以内置命令也就是相当于就是bash的函数


bash细节化实现

#include<assert.h>
    2 #include<iostream>
    3 #include<stdlib.h>
    4 #include<stdio.h>
    5 #include<unistd.h>
    6 #include<string.h>
    7 #include<sys/wait.h>
    8 #include<sys/types.h>
    9 
   10 
   11 #define MAX 1024
   12 #define ARGC 64
   13 #define SEP  " "
   14 int split(char* commandstr,char* argv[])//注意,此时是不需要使用二级指针的,因为数组传参的时候降维了,所以此时这样写就是等于在使用下面那个数组
   15 {
   16    assert(commandstr && argv);
   17    argv[0]=strtok(commandstr,SEP);//切割成功,此时就是会把相应的子串保存在argv指针数组中,但是要注意strtok的目的就是为了把空格替换成\0
   18    if(argv[0]==NULL)
   19    {
   20      return -1;
   21    }
   22    int i =1;
   23    while(argv[++i] = strtok(NULL,SEP));//最后切失败了,argv[i]就是0,while自然就不满足了,strtok把NULL赋值给argv[i],转换一下argv[i]就是等于0
   24   // int i = 1;
   25   // while(1)
   26   // {
   27   //   argv[i]=strtok(NULL,SEP);
   28   //   if(argv[i]==NULL)
   29   //   {
   30   //     break;
   31   //   }
   32   //   ++i;
   33   // }
   34    return 0;                                                                                                                                                             
   35 }
   36 
   37 void debugPrint(char** argv)
   38{
   39   for(int i =0;argv[i];++i)                                                                                                                                              
   40   {
   41     printf("%d:%s\n",i,argv[i]);
   42   }
   43 }
   44 
   45 void showEnv()
   46 {
   47   extern char** environ;
   48   for(int i =0;environ[i];++i)
   49   {
   50     printf("%d:%s\n",i,environ[i]);
   51   }
   52 }
   53 
   54 int main()
   55 {
   56   int last_exit=0;
   57   char myenv[32][256];
   58   int env_index = 0;
   59 //  extern char** environ;//声明环境变量(系统中的所有)
   60   while(1)//第一步理解:进程一定是一个死循环,因为要一直支持输入数据
   61   {
   62      char commandstr[MAX]={0};
   63      char* argv[ARGC]={NULL};//这个就是环境变量的知识了,就是用来存储参数选项的
   64      printf("[wuweixin@mymachine 我的bash实现]# ");//这边由于我们没有写\n,所以本身是还在输出缓冲区的,所以需要我们刷新一下
   65      fflush(stdout);//不能有\n,不然不规范,所以刷新标准输出缓冲区就行
   66      char* str =  fgets(commandstr,sizeof(commandstr) - 1,stdin);//此时使用C语言实现,所以在获取一行字符串的时候,我们使用的是fgets函数(如果想要知道fgets,可以去查一下)
   67      assert(str);(void)str;//防止获取字符串失败,所以检查一下,并且为了防止在release版本也能使用,所以就加上后面这句
   68      //此时上述的那个减一的目的是,防止不知道使用的C语言接口,还是系统接口,因为若果是C接口,它就会自己补\0,如果是系统接口,此时就不会补\0
   69      //printf("%s\n",commandstr);//注意,此时的这个如果我们家了\n,它就会换行两次,因为,我们在输入字符的时候,它默认是把我们回车那下也算进去了
   70      //所以解决方法如下:
   71      commandstr[strlen(commandstr)-1]='\0';//例:获取的是,abcd\n\0 ,所以此时的目的就是要把\n用\0覆盖掉,但是要注意,下标是从0开始的,所以想要覆盖\n,此时需要减一一下
   72      //所以此时我们自己的命令行就很健康了
   73      //此时就是第二步:切割字符串(因为指令选项之间是按照空格间隔的)
   74       pid_t id = fork();//但是要注意:子进程只能跑部分的指令,不是所有指令都可以通过子进程实现的,例如:export,向进程中写入环境变量(此时子进程就完成不了)
   75      assert(id>=0);(void)id;                                                                                                                                             
   76      //例:"ls -a -l" 需要切割成 "ls""-a""-l" 就是要让它在输入的时候,可以按照空格区分, "ls"\0"-a"\0"-l"\0 
   77      //实现原理,使用指针数组搞定,以下标原理
   78      //所以这边你可以使用strtok,也可以直接使用C++中的find函数,找到空格,然后把空格替换成\0
   79      //strtok使用原理:strtok(str," ");切割之后,就传 strtok(NULL," ")
   80      int n =split(commandstr,argv);//所以此时执行完这个函数,此时就切割好了
   81      if(n != 0)
   82      {
   83        continue;//切失败了就继续切,反正一定要切成功
   84      }
   85     // debugPrint(argv);
   86      if(strcmp(argv[0],"cd")==0)
   87      {
   88        if(argv[1]!=NULL)//因为此时实现的是cd指令,所以第二个参数一定是要有的,cd不是ls这种功能性的,是目的性的
   89        {
   90           chdir(argv[1]);//此时就是这个系统接口的特性,可以直接到达我们传的这个路径,当然此时的argv[1]也就是我们想要返回的那个路径
   91           continue;//目的拦截在父进程,不执行fork函数,导致子进程也会执行
   92        }
   93      }
   94      else if(strcmp(argv[0],"export")==0)
   95      {
   96          if(argv[1]!=NULL)
   97          {//此时我们有了自己的自定义env,此时就不要使用putenv了,因为putenv中的内容会被fgets中的内容给覆盖
   98          //   putenv(argv[1]);//此时就是将我们的export使用putenv导入到系统的bash进程进行处理,让真正的bash去完成这个工作,间接导致我们的bash文件完成这个工作 
   99            strcpy(myenv[env_index],argv[1]);//此时就是现将argv中保存的字符给拷贝一份,然后就不怕被覆盖了
  100            putenv(myenv[env_index++]);//因为此时这个下标是单独保存,所以就不会因为gets,导致被commandstr给覆盖掉了
  101            continue;
  102          }
  103      }
  104      else if(strcmp(argv[0],"env")==0)
  105      {
  106        showEnv();
  107        continue;
  108      }
  109      else if(strcmp(argv[0],"echo")==0)
  110      {
  111        const char* target_env=NULL;
  112        if(argv[1][0]=='$')//因为使用echo后面跟的一定是$才行,但是此时要注意数据类型,此时的argv代表的是一个指针数组,所以想要用里面的数据进行判断就一定要加上解引用
  113        {
  114          if(argv[1][1]=='?')//因为此时输入$?的时候是不需要空格的,所以可以看做是一个字符,然后就可以看成是一个二维数组
  115          {
  116            printf("%d\n",last_exit);
  117            continue;
  118          }
  119          else 
  120          {
  121             target_env=getenv(argv[1]+1);//使用getenv来获取特定的环境变量,此时因为这些都是在调用函数,并且这些函数的参数本质都是指针,所以直接传地址没关系
  122             
  123          }  
  124        }
  125        if(target_env!=NULL)
  126        {
  127          printf("%s=%s\n",argv[1]+1,target_env);
  128        }
  129        continue;
  130      }
  131 
  132      if(strcmp(argv[0],"ls")==0)//此时这句代码就是识别到,如果指令是ls,就特殊处理一下,目的:加个颜色
  133      {
  134         int pos = 0;
  135         while(argv[pos])
  136         {
  137           ++pos;
  138         }
  139         argv[pos++]= (char*)"--color=auto";
  140         argv[pos]=NULL;
  141      }
  142 
  143      if(id == 0)
  144      {
  145          //child
  146          execvp(argv[0],argv);//直接将环境变量传给子进程,让子进程去替换系统中argv环变量中的指令
  147          exit(1);
  148      }
  149      else 
  150      {
  151        //parent
  152         int status = 0;
  153         pid_t ret = waitpid(id,&status,0);
  154         if(ret>0)
  155         {
  156           last_exit=WEXITSTATUS(status);//目的每次获取进程退出码
  157         }
  158      }
  159 
  160   }
  161 
  162   return 0;
  163 }


文件操作

文件操作我们肯定是非常的熟悉的,因为我们在学习C语言的时候,我们就学习过如何进行文件操作,例:如何使用和文件有关的相关函数接口,所以此时我们学习成本也是不高的,但是如果在系统层面看待文件操作,那么就需要结合之前有关进程的知识来结合理解,所以和系统调用一般,文件操作也是同理,不是语言问题,而是系统问题,所有的语言,无论是C++、java、C、Pathon,这些语言都是有文件操作的,但是这些文件操作想的方法都是不一样的,本质上是因为对操作系统的文件程序驱动的系统调用接口的封装不一样,但是底层都是取调用操作系统的系统调用接口,所以在语言层面和操作系统层面,文件操作是有区别的

从语言层面看文件操作

首先明白 文件=内容+属性 ,是针对文件的操作,对内容的操作,对属性的操作,并且要明白,一个文件是需要被提前加载到内存中的,那么此时加载的是文件的内容,还是文件的属性呢?

所以打开文件的本质就是,将我们需要的文件属性加载到内存中,并且操作系统内部一定会同时存在大量的被打开文件,所以此时就有一个问题:那么操作系统需不需要把这些被打开的文件进行管理呢?简简单单还是那句话,先描述,再组织,
所以先描述再组织的本质也就是构建一个合适的结构体出来(所以管理的本质也就是构建一个结构体出来),并且文件结构体中就有文件的属性,所以此时的结构体就是通过文件的属性构成的,例如:此时操作系统就是通过一个叫struct file的结构体进行对加载到内存的文件进行管理,并且加载文件时,一定是文件的属性先被加载到内存(文件=内容+属性),所以此时struct file结构体中用于存储的就是各个文件的属性,通过struct file* next;指针的形式,所以可以初步了解,文件结构体大致是一个链表形式的数据结构,所以总,操作系统对内存中文件的管理,就类似于是对链表的增删查改

所以上述虽然是一个推导出来理论,但本质也就是一个对文件对象管理的建模

明白了每一个被打开的文件(被加载到内存的文件),都要在OS内对应文件对象的struct结构体,可以将所有的struct file结构体使用某种数据结构链接起来,在os内部,对被打开文件进行管理,就被转换成为了对链表的增删查改

结论:文件被打开,OS要为被打开的文件,创建对应的内核数据结构 struct file (跟操作系统创景进程pcb一个道理)


并且明白, 什么是被打开的文件(是谁在打开呢?)当然是操作系统,因为只有它有这个能力,那么是谁让它打开的呢?可以说是用户,准确的说是进程,用户写了一个代码,生成进程,最后操作系统打开,执行文件,首先就是生成进程

所以文件操作的本质都是被打开文件和进程之间的关系,我们学习的目标也就是进程和被打开文件之间的关系,所以按照内存来看,就是struct task_struct;和struct file;之间的关系,也就是进程控制块和文件对象之间的关系

语言层面看文件操作

C语言中最基本的文件操作函数,fopen,fclose,fput

简简单单证明一个点:如果默认只是打开一个文件,文件中的内容是会自动被清空的,同时,每次进行写入的时候,都会从最开始进行写入,如下图:
在这里插入图片描述
所以打开一个文件第一步先清空文件中的内容,然后第二步才是对文件进行写入(当然这是因为我们写入的方式是“w”)
所以如果我们使用的是“a”的话,那么此时表示的就是追加,此时就不会对文件内容进行清空,而是不断的对文件内容进行追加,所以具体的文件操作是可以按照我们自己的需求来实现的

下图就是C语言中部分的文件操作:
在这里插入图片描述

系统层面看文件操作

打开文件接口如下:
在这里插入图片描述

第一个注意点,为什么返回值是整形(int)

来一个话题: 操作系统一般是如何让用户给自己传递标志位的
首先操作系统有一个接口,int=32个比特位,我们可以调用一个比特位表示一个标志位,一个整数就可以同时传递32个标志位,所以我们就可以使用比特位的方式向一个系统接口传递标志位,本质也就是我们之前学的位图结构起的作用,

在这里插入图片描述

总结:文件操作的内容将持续更细,如:文件系统、动静态库

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【CE进阶】lua脚本使用

▒ 目录 ▒&#x1f6eb; 导读需求开发环境1️⃣ 脚本窗口Lua ScriptLua EngineAuto assemble2️⃣ 全局变量3️⃣ 进程当前打开的进程ID系统的进程列表系统的顶部窗口列表4️⃣ 线程5️⃣ 输入设备6️⃣ 屏幕7️⃣ 剪贴板&#x1f6ec; 文章小结&#x1f4d6; 参考资料&#x…

算法的时间复杂度和空间复杂度

目录 1 如何衡量一个算法的好坏 2.时间复杂度 2.1 时间复杂度的概念 2.2 大O的渐进表示法 2.3常见代码举例 2.3.1 Func2 O(N) 2.3.2 Func3 O(MN) 2.3.3 Func4 O(1) 2.3.4 Func5 strchr O(N) 2.3.5 Func6 冒泡排序 O(N^2) 2.3.6 Func7 二分…

菜鸟刷题Day6

⭐作者&#xff1a;别动我的饭 ⭐专栏&#xff1a;菜鸟刷题 ⭐标语&#xff1a;悟已往之不谏&#xff0c;知来者之可追 一.链表内指定区间反转&#xff1a;链表内指定区间反转_牛客题霸_牛客网 (nowcoder.com) 描述 将一个节点数为 size 链表 m 位置到 n 位置之间的区间反转…

如何用深度强化学习做单元测试代码生成

设计一个用强化学习来生成单元测试代码的系统需要考虑以下几个方面&#xff1a; Agent&#xff1a;强化学习算法中的智能体&#xff0c;它需要接收当前环境状态&#xff0c;根据策略选择相应的动作并执行。 State&#xff1a;描述当前环境状态的特征。在这个问题中&#xff0c…

电脑长按电源键强行关机,对SSD有伤害吗?SSD 掉盘之殇

说到“按住电源键强制关机”的操作&#xff0c;想必大家都不会陌生&#xff0c;毕竟在电脑蓝屏或者电脑死机的时候&#xff0c;我们总是束手无策。而且&#xff0c;身边的人在遇到同样的情况时&#xff0c;往往都是选择长按电源键强制关机&#xff0c;所以当我们遇到同样的情况…

【算法】回溯法详解

一、概述 回溯法在包含的所有可能解的解空间树中&#xff0c;从根节点出发&#xff0c;按照深度有限的策略进行搜索&#xff0c;对于解空间树的某个结点&#xff0c;如果该节点满足问题的约束条件&#xff0c;则进入该子树继续进行搜索&#xff0c;否则将以该节点为根节点进行…

【算法】一文详解贪心法

一、概述 贪心法将一个复杂问题分解为一系列较为简单的局部最优解&#xff0c;每一步都是对当前解的一个扩展&#xff0c;直到获得问题的完全解。贪心法的典型应用时求解最优化问题&#xff0c;而且即使是非最优解&#xff0c;最终得出的解也和最优解比较近似 1.1 贪心法设计…

【多线程】常见的锁策略

✨个人主页&#xff1a;bit me&#x1f447; ✨当前专栏&#xff1a;Java EE初阶&#x1f447; ✨每日一语&#xff1a;老当益壮&#xff0c;宁移白首之心&#xff1b;穷且益坚&#xff0c;不坠青云之志。 目 录&#x1f3f3;️一. 乐观锁 vs 悲观锁&#x1f3f4;二. 普通的互斥…

清晰概括:进程与线程间的区别的联系

相关阅读&#xff1a; &#x1f517;通俗简介&#xff1a;操作系统之进程的管理与调度&#x1f517;如何使用 jconsole 查看Java进程中线程的详细信息&#xff1f; 目录 一、进程与线程 1、进程 2、线程 二、进程与线程之间的区别和联系 1、区别 2、联系 一、进程与线程 …

程序员接私活一定要知道的事情,我走的弯路你们都别走了

文章目录前言一、程序员私活的种类1.兼职职位众包2.自由职业者驻场3.项目整包二、这3种私活可以接1.有熟人2.七分熟的项目3.需求明确的项目三、这3种私活不要接1.主动找上门的中介单2.一味强调项目简单好做3.外行人给你拉的项目四、接单的渠道1.线下渠道2.线上渠道3.比较靠谱的…

计网之HTTP协议和Fiddler的使用

文章目录一. HTTP概述和fidder的使用1. 什么是HTTP2. 抓包工具fidder的使用2.1 注意事项2.2 fidder的使用二. HTTP协议格式1. HTTP请求格式1.1 基本格式1.2 认识URL1.3 方法2. 请求报头关键字段3. HTTP响应格式3.1 基本格式3.2 状态码一. HTTP概述和fidder的使用 1. 什么是HTT…

cpu中缓存简介

一级缓存是什么&#xff1a; 一级缓存都内置在CPU内部并与CPU同速运行&#xff0c;可以有效的提高CPU的运行效率。一级缓存越大&#xff0c;CPU的运行效率越高&#xff0c;但受到CPU内部结构的限制&#xff0c;一级缓存的容量都很小。 CPU缓存&#xff08;Cache Memory&#xf…

【设计模式】23种设计模式之七大原则

【设计模式】23种设计模式之七大原则什么是设计模式的原则1、单一职责原则基本介绍案例分析注意事项2、接口隔离原则基本介绍案例分析代码实现3、依赖倒转原则基本介绍案例分析依赖传递的三种方式注意事项4、里氏替换原则关于继承性的思考和说明基本介绍案例分析5、开闭原则ocp…

冲击蓝桥杯-并查集,前缀和,字符串

目录 前言 一、并查集 1、并查集的合并&#xff08;带路径压缩&#xff09; 2、询问是否为同一个集合 3、例题 二、前缀和 1 、前缀和是什么 2、经典题目 三- 字符串处理 1、字符串的插入 2、字符串转化为int类型 3、字符反转 前言 并查集合前缀&#xff0c;字符串…

Python让ChatGPT全自动改写生成文章教程

ChatGPT是一个在自然语言处理领域非常先进的文本生成模型&#xff0c;它能够产生高质量、连贯的文章。它受到了广泛的关注&#xff0c;因为它可以自动生成大量的文本&#xff0c;从而减轻了人工写作的负担。怎么使用chatgpt批量改写文章&#xff1f;最简单的方式就是找到一家接…

「Vue面试题」vue要做权限管理该怎么做?如果控制到按钮级别的权限怎么做?

文章目录一、是什么二、如何做接口权限路由权限控制菜单权限方案一方案二按钮权限方案一方案二小结参考文章一、是什么 权限是对特定资源的访问许可&#xff0c;所谓权限控制&#xff0c;也就是确保用户只能访问到被分配的资源 而前端权限归根结底是请求的发起权&#xff0c;…

刷题之最长公共/上升子序列问题

目录 一、最长公共子序列问题&#xff08;LCS&#xff09; 1、题目 2、题目解读 ​编辑 3、代码 四、多写一题 五、应用 二、最长上升子序列问题&#xff08;LIS&#xff09; 1、题目 2、题目解读 3、代码 四、多写一道 Ⅰ、题目解读 Ⅱ、代码 一、最长公共子序列问题&…

刷题训练营之栈与队列

文章目录前言一、用队列实现栈1.题目介绍2.思路3.代码二、用栈实现队列1.题目介绍2.思路3.代码前言 本题是在栈与队列的基础上&#xff0c;为巩固两者而出的题&#xff0c;所以基本是在实现了栈与队列的基础上做的&#xff0c;如果没有栈与队列的基础&#xff0c;请看我之前的…

Nginx的漏洞浮现

本文参考https://vulhub.org/#/environments/nginx/nginx_parsing_vulnerability/环境搭建均是采用docker拉取环境请移步到参考。一、Nginx的配置错误案列1. CRLF注入漏洞配置错误文件error1.confrootubuntu-virtual-machine:/vulhub/vulhub-master/nginx/insecure-configurati…

数据结构中的堆

一、树的重要知识点 节点的度&#xff1a;一个节点含有的子树的个数称为该节点的度&#xff08;有几个孩子&#xff09;叶节点或终端节点:度为0的节点称为叶节点&#xff1b;如上图&#xff1a;B、C、H、I...等节点为叶节点&#xff08;0个孩子&#xff09;非终端节点或分支节点…