当前位置: 首页 > article >正文

shell的模拟实现 ─── linux第16课

目录

第一版只能维护命令行参数表+创建子进程, 执行非内建命令

第一版的执行结果:

 第二版能维护命令行参数表+执行cd命令 ,判断了是否是自建命令(mysell自己执行自建命令,可以对环境变量发生改变),子进程执行其他命令.

第二版执行结果:

第三版 模拟真实shell从系统文件中获取环境变量,维护命令行参数表+维护环境变量表(execvpe)

第三版运行结果:


在shell的命令行中输入命令,会有两种执行命令的途径

  1.         shell自己执行
  2.         shell创建子进程(fork ,exit ,waitpid,exec) ,子进程去执行
  • shell自己执行的命令是自建命令(bulit command)
  • 子进程执行的是非自建命令

第一版只能维护命令行参数表+创建子进程, 执行非内建命令

        我们先创建了命令行提示符 ,获取了命令行的内容,维护了命令行参数表,创建了子进程进行命令的执行

​1 #include<iostream>2 #include<cstdio>3 #include<stdlib.h>4 #include<cstring>5 #include<string>6 #include<unistd.h>7 #include<sys/types.h>8 #include<sys/wait.h>9 10 using namespace std;11 const int charsize =1024;//命令行提示符的大小12 const int gargvnum =64;//命令行参数列表的大小13 14 //全局的15 char* gargv[gargvnum];16 int gargc;17 18 19 int lastcode = 0;20 21 string GetUsrName()22 {23     string name =getenv("USER");24     return name.empty()?  "None" : name;25 }26 string GetHostName()27 {28     string name =getenv("HOSTNAME");29     return name.empty()?  "None" : name;30 }31 string GetPwd()32 {33     string name =getenv("PWD");34     return name.empty()?  "None" : name;35 }36 string MakeCommandLine()37 {  //[root@hcss-ecs-1f3a lesson17]#  38     char CommandLine[charsize];39     snprintf(CommandLine ,charsize,"[%s@%s %s]# ",\40         GetUsrName().c_str(),GetHostName().c_str(),GetPwd().c_str());41     return CommandLine;42 }43 void PrintCommandLine()//1.打印命令行提示符44 {45     printf("%s",MakeCommandLine().c_str());46     fflush(stdout);47 }48 49 50 bool GetCommand(char Command_buff[] ,int size)//2.获取命令51 {52     //将命令输出到字符数组中53     //ls -a -l -n54     char*result =fgets(Command_buff,size,stdin);55     if(!result)56     {57         return false;58     }59     Command_buff[strlen(Command_buff)-1]= 0;//fgets会将回车(\n)也输入,将回车置零60     if(strlen(Command_buff) == 0) return false;//命令行就输入了回车,strlen遇0(\0)会停下来61     return true;62 }63 64  void ParseCommand(char Command_buff[] ,int size)//3.分析命令65 {66     memset(gargv ,0,sizeof(gargv));67     gargc=0;68     const char* SEP =" ";69     gargv[gargc++] = strtok(Command_buff ,SEP);//strtok 会在字符串中查找分隔符,并将分隔符替换为 \0,从而将字符串分割成多个70     while((bool)(gargv[gargc++] = strtok(nullptr,SEP)));//strtok后续使用要用nullptr代替元字符串71     gargc--;72 }73 74 void debug()75 {76     printf("argc: %d\n", gargc);77     for(int i = 0; gargv[i]; i++)78     {79         printf("argv[%d]: %s\n", i, gargv[i]);80     }81 }85 bool ExecuteCommand()//4.执行命令86 {87     pid_t id =fork();88     if(id ==0)89     {//子进程90         int ret =execvp(gargv[0],gargv);91         if(ret ==-1) cout<<"子进程出错\n"<<endl;92         exit(1);93     }94     int status =0;95     pid_t rid =waitpid(id,&status ,0);96     if(rid >0)97     {98         if(WIFEXITED(status))99         {
100            lastcode=WEXITSTATUS(status);
101         }
102         else lastcode =100;
103         return true;
104     }
105     return false;
106 
107 }
108 
109 int main()
110 {
111     char Command_buff[charsize];
112     while(true)
113     {
114         PrintCommandLine();//1.打印命令行提示符
115 
116         if(!GetCommand(Command_buff,charsize))//2.获取命令
117         {
118             continue;
119         }
120         ParseCommand(Command_buff ,charsize);//3.分析命令
121         //debug();
122         ExecuteCommand();//4.执行命令
123     }
124     return 0;
125 }​

第一版的执行结果:

  • 第一版在执行cd ..时,是改变了子进程的cwd,子进程执行完又退了,无法影响下面的进程,cd.. 不能让子进程执行.
  • 所以cd .. 或cd / 是自建命令 ,需要shell自己执行,以便可以影响到下面的进程(例如在/ 目录下创建文件)

 第二版能维护命令行参数表+执行cd命令 ,判断了是否是自建命令(mysell自己执行自建命令,可以对环境变量发生改变),子进程执行其他命令.

在执行创建子进程前判断,命令是否是内建命令(是否是cd 命令),是否要创建子进程.

getpwd不再是从环境变量中拿,从pcb中拿(因为pcb中是实时的),拿完更新环境变量(命令行提示符每次运行都会刷新,借此来维护cd 后的环境变量).

    #include<iostream>2 #include<cstdio>3 #include<stdlib.h>4 #include<cstring>5 #include<string>6 #include<unistd.h>7 #include<sys/types.h>8 #include<sys/wait.h>9 10 using namespace std;11 const int charsize =1024;12 const int gargvnum =64;14 //全局的15 char* gargv[gargvnum];16 int gargc;17 19 20 21 //全局的当前shell的工作路径(定义到全局不会被销毁)22 char pwd[charsize];23 char pwdenv[charsize];24 25 int lastcode = 0;26 27 string GetUsrName()28 {29     string name =getenv("USER");30     return name.empty()?  "None" : name;31 }32 string GetHostName()33 {34     string name =getenv("HOSTNAME");35     return name.empty()?  "None" : name;36 }string GetPwd()38 {39     //string name =getenv("PWD");40     //return name.empty()?  "None" : name;41 42 43     //从pcb中直接拿pwd44     if(nullptr ==  getcwd(pwd,sizeof(pwd))) return "None";45     //拿到后还需要更新环境变量中的pwd46     snprintf(pwdenv,sizeof(pwdenv),"PWD=%s",pwd);47     putenv(pwdenv);48     return pwd;49 50 51 }52 string MakeCommandLine()53 {  //[root@hcss-ecs-1f3a lesson17]#  54     char CommandLine[charsize];55     snprintf(CommandLine ,charsize,"[%s@%s %s]# ",\56         GetUsrName().c_str(),GetHostName().c_str(),GetPwd().c_str());57     return CommandLine;58 }59 void PrintCommandLine()//1.打印命令行提示符60 {61     printf("%s",MakeCommandLine().c_str());62     fflush(stdout);63 }64 65 66 bool GetCommand(char Command_buff[] ,int size)//2.获取命令67 {68     //将命令输出到字符数组中69     //ls -a -l -n70     char*result =fgets(Command_buff,size,stdin);71     if(!result)72     {73         return false;74     }75     Command_buff[strlen(Command_buff)-1]= 0;//fgets会将回车(\n)也输入76     if(strlen(Command_buff) == 0) return false;//strlen遇0(\0)会停下来77     return true;78 }80  void ParseCommand(char Command_buff[] ,int size)//3.分析命令81 {82     memset(gargv ,0,sizeof(gargv));83     gargc=0;84     const char* SEP =" ";85     gargv[gargc++] = strtok(Command_buff ,SEP);//strtok 会在字符串中查找分隔符,并将分隔符替换为 \0,从而将字符串分割成多个86     while((bool)(gargv[gargc++] = strtok(nullptr,SEP)));//strtok后续使用要用nullptr代替元字符串87     gargc--;88 }89 90 void debug()91 {92     printf("argc: %d\n", gargc);93     for(int i = 0; gargv[i]; i++)94     {95         printf("argv[%d]: %s\n", i, gargv[i]);96     }97 }98 99 
100 
101 bool ExecuteCommand()//4.执行命令
102 {
103     pid_t id =fork();
104     if(id ==0)
105     {//子进程
106         int ret =execvp(gargv[0],gargv);
107         if(ret ==-1) cout<<"子进程出错\n"<<endl;
108         exit(1);
109     }
110     int status =0;
111     pid_t rid =waitpid(id,&status ,0);
112     if(rid >0)
113     {
114         if(WIFEXITED(status))
115         {
116            lastcode=WEXITSTATUS(status);
117         }
118         else lastcode =100;
119         return true;
120     }return false;
122 
123 }
124 
125 //内建命令的执行(调用函数,改变状态)
126 bool CheckandExecBuiltCommand()
127 {   //使用穷举法找内建命令
128     if(0 == strcmp(gargv[0],"cd"))
129     {
130         if(gargc == 2)
131         {
132             chdir(gargv[1]);
133         }
134         return true;
135     }
136     return false;
137 
138 }
139 
140 int main()
141 {
142     char Command_buff[charsize];
143     while(true)
144     {
145         PrintCommandLine();//1.打印命令行提示符
146 
147         if(!GetCommand(Command_buff,charsize))//2.获取命令
148         {
149             continue;
150         }
151         ParseCommand(Command_buff ,charsize);//3.分析命令
152         //debug();
153         //判断是否是内建命令,shell自己执行
154         if(CheckandExecBuiltCommand())//是内建命令并执行
155         {
156             continue;
157         }
158         //不是内建命令,创建子进程执行
159         ExecuteCommand();//4.执行命令
160     }
161     return 0;
162 }

第二版执行结果:

第三版 模拟真实shell从系统文件中获取环境变量,维护命令行参数表+维护环境变量表(execvpe)

  • myshell前面的两版都是从系统shell中获取的环境变量表
  • 实际上,系统shell开启时,是从系统文件中获取环境变量表,但是这个过程涉及shell脚本(比较难搞,意义不大),
  • 所以我们用将系统shell的环境变量表手动拷贝到myshell中的过程来模拟系统shell开启时,是从系统文件中获取环境变量表

  • 要让myshell执行的子进程的环境变量与myshell一致(不与系统shell一致)使用execvpe
  • 系统的shell维护了两张表(命令行参数表+环境变量表)

第三版拷贝了环境变量表,维护了环境变量表(execvpe),增加了内置命令(export  echo env)

设置了退出码(lastcode)

第三版运行结果:

#include<iostream>2 #include<cstdio>3 #include<stdlib.h>4 #include<cstring>5 #include<string>6 #include<unistd.h>7 #include<sys/types.h>8 #include<sys/wait.h>9 10 using namespace std;11 const int charsize =1024;12 const int gargvnum =64;13 const int envnum =64;14 //全局的15 char* gargv[gargvnum];16 int gargc;17 18 char* genv[envnum];19 20 21 //全局的当前shell的工作路径(定义到全局不会被销毁)22 char pwd[charsize];23 char pwdenv[charsize];24 25 //全局的进程返回码26 int lastcode = 0;27 28 string GetUsrName()29 {30     string name =getenv("USER");31     return name.empty()?  "None" : name;32 }33 string GetHostName()34 {35     string name =getenv("HOSTNAME");36     return name.empty()?  "None" : name;37 }
string GetPwd()39 {40     //string name =getenv("PWD");41     //return name.empty()?  "None" : name;42 43 44     //从pcb中直接拿pwd45     if(nullptr ==  getcwd(pwd,sizeof(pwd))) return "None";46     //拿到后还需要更新环境变量中的pwd47     snprintf(pwdenv,sizeof(pwdenv),"PWD=%s",pwd);48     putenv(pwdenv);49     return pwd;50 51 52 }53 string MakeCommandLine()54 {  //[root@hcss-ecs-1f3a lesson17]#  55     char CommandLine[charsize];56     snprintf(CommandLine ,charsize,"[%s@%s %s]# ",\57         GetUsrName().c_str(),GetHostName().c_str(),GetPwd().c_str());58     return CommandLine;59 }60 void PrintCommandLine()//1.打印命令行提示符61 {62     printf("%s",MakeCommandLine().c_str());63     fflush(stdout);64 }
bool GetCommand(char Command_buff[] ,int size)//2.获取命令68 {69     //将命令输出到字符数组中70     //ls -a -l -n71     char*result =fgets(Command_buff,size,stdin);72     if(!result)73     {74         return false;75     }76     Command_buff[strlen(Command_buff)-1]= 0;//fgets会将回车(\n)也输入77     if(strlen(Command_buff) == 0) return false;//strlen遇0(\0)会停下来78     return true;79 }80 81  void ParseCommand(char Command_buff[] ,int size)//3.分析命令82 {83     memset(gargv ,0,sizeof(gargv));84     gargc=0;85     const char* SEP =" ";86     gargv[gargc++] = strtok(Command_buff ,SEP);//strtok 会在字符串中查找分隔符,并将分隔符替换为 \0,从而将字符串分割成多个87     while((bool)(gargv[gargc++] = strtok(nullptr,SEP)));//strtok后续使用要用nullptr代替元字符串88     gargc--;89 }90 91 void debug()92 {93     printf("argc: %d\n", gargc);94     for(int i = 0; gargv[i]; i++)95     {96         printf("argv[%d]: %s\n", i, gargv[i]);97     }98 }
bool ExecuteCommand()//4.执行命令
103 {
104     pid_t id =fork();
105     if(id ==0)
106     {//子进程
107         int ret =execvpe(gargv[0],gargv,genv);
108         if(ret ==-1) cout<<"子进程出错\n"<<endl;
109         exit(1);
110     }
111     int status =0;
112     pid_t rid =waitpid(id,&status ,0);
113     if(rid >0)
114     {
115         if(WIFEXITED(status))//子进程正常的返回
116         {
117            lastcode=WEXITSTATUS(status);
118         }
119         else lastcode = 100;//子进程异常
120         return true;
121     }
122     return false;
123 
124 }
125 
126 void AddEnv(char* item)
127 {
128     int index =0;
129     while(genv[index])
130     {
131         index++;
132     }
133     genv[index]=(char*)malloc(strlen(item)+1);
134     strncpy(genv[index] ,item,strlen(item)+1);
135     genv[++index]= nullptr;
136 }
//内建命令的执行(调用函数,改变状态)
138 bool CheckandExecBuiltCommand()
139 {   //使用穷举法找内建命令
140     if(0 == strcmp(gargv[0],"cd"))
141     {
142         if(gargc == 2)
143         {
144             chdir(gargv[1]);
145             lastcode= 0;
146         }
147         else
148         {
149             lastcode=1;
150         }
151         return true;
152     }
153     else if(0 ==strcmp(gargv[0] ,"export"))
154     {
155         if(gargc ==2)
156         {
157              AddEnv(gargv[1]);
158             lastcode = 0;
159         }
160         else
161         {
162             lastcode= 1;
163         }
164         return true;
165     }
166     else if(0 == strcmp(gargv[0],"env"))
167     {
168         for(int i=0 ;genv[i];i++)
169         {
170             printf("%s\n",genv[i]);
171 
172         }
173         lastcode =0;
174         return true;
175     }
176     else if(0 ==strcmp(gargv[0] ,"echo"))
177     {
178         if(gargc ==2)
179         {
180             //echo $?
181             //echo $PATH
182             //echo hello
183             if(gargv[1][0] == '$')
184             {
185                  if(gargv[1][1] == '?')
186                  {
187                     printf("%d\n",lastcode);
188                     lastcode =0;
189                  }
190             }
191             else
192             {
193                 printf("%s\n",gargv[1]);
194                 lastcode =0;
195             }
196         }
197         else
198         {
199             lastcode=1;
200         }
201         return true;
202 
203     }
204     return false;
205 
206 }
207 
208 // 作为一个shell,获取环境变量应该从系统的配置来
209 //  我们今天就直接从父shell中获取环境变量   
210 void IniEnv()
211 {
212     extern char** environ;
213     int index=0;
214     while(environ[index])
215     {
216         genv[index]=(char*)malloc(strlen(environ[index])+1);//strlen不含\0
217         strncpy(genv[index],environ[index],strlen(environ[index])+1);
218         index++;
219     }
220     genv[index]= nullptr;
221 
222 }
223 
224 int main()
225 {
226     IniEnv();
227     char Command_buff[charsize];
228     while(true)
229     {
230         PrintCommandLine();//1.打印命令行提示符
231 
232         if(!GetCommand(Command_buff,charsize))//2.获取命令
233         {
234             continue;
235         }
236         ParseCommand(Command_buff ,charsize);//3.分析命令
237         //debug();
238         //判断是否是内建命令,shell自己执行
239         if(CheckandExecBuiltCommand())//是内建命令并执行
240         {
241             continue;
242         }
243         //不是内建命令,创建子进程执行
244         ExecuteCommand();//4.执行命令
245     }
246     return 0;
247 }

相关文章:

shell的模拟实现 ─── linux第16课

目录 第一版只能维护命令行参数表创建子进程, 执行非内建命令 第一版的执行结果: 第二版能维护命令行参数表执行cd命令 ,判断了是否是自建命令(mysell自己执行自建命令,可以对环境变量发生改变),子进程执行其他命令. 第二版执行结果: 第三版 模拟真实shell从系统文件中获取环…...

游戏引擎学习第153天

仓库:https://gitee.com/mrxiao_com/2d_game_3 回顾 目前正在进行的是一个比较大的系统调整&#xff0c;原本预计今天会继续深入这个改动&#xff0c;但实际上在昨天的开发中&#xff0c;我们已经完成了大部分的代码编写&#xff0c;并且运行之后几乎一切都能正常工作&#x…...

理解C语言中的extern关键字

在C语言编程中&#xff0c;extern关键字是一个非常重要的概念&#xff0c;尤其在多文件编程和全局变量的使用中。本文将详细解释extern的作用、用法以及常见的应用场景。 1. extern关键字的作用 extern关键字用于声明一个变量或函数是在其他文件中定义的。它告诉编译器&#x…...

【MyBatis Plus 逻辑删除详解】

文章目录 MyBatis Plus 逻辑删除详解前言什么是逻辑删除&#xff1f;MyBatis Plus 中的逻辑删除1. 添加逻辑删除字段2. 实体类的配置3. 配置 MyBatis Plus4. 使用逻辑删除5. 查询逻辑删除的记录 MyBatis Plus 逻辑删除详解 前言 MyBatis Plus 是一个强大的持久化框架&#xf…...

latex问题汇总

latex问题汇总 环境问题1 环境 texlive2024 TeXstudio 4.8.6 (git 4.8.6) 问题1 编译过程有如下错 ! Misplaced alignment tab character &. l.173 International Conference on Infrared &Millimeter Waves, 2004: 667--... I cant figure out why you would wa…...

基于Redis实现限流

限流尽可能在满足需求的情况下越简单越好&#xff01; 1、基于Redsi的increment方法实现固定窗口限流 Redis的increment方法保证并发线程安全窗口尽可能越小越好(太大可能某一小段时间就打满请求剩下的都拿不到令牌了)这个原理其实就是用当前时间戳然后除窗口大小 在这个窗口大…...

力扣练习之确定两个字符串是否接近

目录 题目&#xff1a; 题解&#xff1a; 详细题解 题目&#xff1a; 如果可以使用以下操作从一个字符串得到另一个字符串&#xff0c;则认为两个字符串 接近 &#xff1a; 操作 1&#xff1a;交换任意两个 现有 字符。 例如&#xff0c;abcde -> aecdb 操作 2&#xff1…...

大三下找C++开发实习的感受分享

目录 找实习的过程 阶段一&#xff1a;投简历 阶段二&#xff1a;准备面试 阶段三&#xff1a;面试中 阶段四&#xff1a;面试结束后 面试真题 总结 找实习的过程 阶段一&#xff1a;投简历 第一次找实习还是使用BOSS这个软件进行投简历&#xff0c;这个过程其实挺难说…...

基于hive的电信离线用户的行为分析系统

标题:基于hive的电信离线用户的行为分析系统 内容:1.摘要 随着电信行业的快速发展&#xff0c;用户行为数据呈现出海量、复杂的特点。为了深入了解用户行为模式&#xff0c;提升电信服务质量和精准营销能力&#xff0c;本研究旨在构建基于 Hive 的电信离线用户行为分析系统。通…...

Makefile——make工具编译STM32工程

一、Makefile相关指令 1.1、变量 符号含义替换追加:恒等于 1.2、隐含规则 符号含义%.o任意的.o文件*.o所有的.o文件 1.3、通配符 符号含义$^所有依赖文件$所有目标文件$<所有依赖文件的第一个文件 1.4、编译器指令常用参数功能说明 符号含义举例-E预处理&#xff0c;…...

Java EE 进阶:SpringBoot 配置⽂件

什么是配置文件 “配置文件”是一个用来保护程序或者系统设置信息的文件&#xff0c;它的作用是让程序在启动或者运行中&#xff0c;能够读取这些设置并按预期进行工作&#xff0c;而不需要手动的设置。 Spring Boot 配置文件 设置服务器端口、编码格式配置数据库连接控制日…...

【redis】五种数据类型和编码方式

文章目录 五种数据类型编码方式stringhashlistsetzset查询内部编码 五种数据类型 字符串&#xff1a;Java 中的 String哈希&#xff1a;Java 中的 HashMap列表&#xff1a;Java 中的 List集合&#xff1a;Java 中的 Set有序集合&#xff1a;除了存 member 之外&#xff0c;还有…...

基于Python的电商销售数据分析与可视化系统实

一、系统架构设计 1.1系统流程图 #mermaid-svg-Pdo9oZWrVHNuOoTT {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-Pdo9oZWrVHNuOoTT .error-icon{fill:#552222;}#mermaid-svg-Pdo9oZWrVHNuOoTT .error-text{fill:#5…...

色板在数据可视化中的创新应用

色板在数据可视化中的创新应用&#xff1a;基于色彩感知理论的优化实践 引言 在数据可视化领域&#xff0c;色彩编码系统的设计已成为决定信息传递效能的核心要素。根据《Nature》期刊2024年发布的视觉认知研究&#xff0c;人类大脑对色彩的识别速度比形状快40%&#xff0c;色…...

EB-Cable许可管理中的数据安全与隐私保护

在数字化时代&#xff0c;数据安全与隐私保护已成为企业关注的重中之重。作为专业的电缆管理软件&#xff0c;EB-Cable许可管理不仅在功能丰富和操作便捷方面表现出色&#xff0c;更在数据安全与隐私保护方面为用户提供了坚实的保障。本文将详细介绍EB-Cable许可管理在数据安全…...

解决ubuntu(jetpack)系统下系统盘存储不够的

以下是可以安全清理的内容及操作步骤&#xff0c;按优先级和风险从低到高排序&#xff1a; 1. 清理日志文件&#xff08;低风险&#xff09; /var/log/syslog (7.1G) # 清空syslog文件&#xff08;不删除文件本身&#xff09; sudo truncate -s 0 /var/log/syslog# 或限制sys…...

【无人机路径规划】基于麻雀搜索算法(SSA)的无人机路径规划(Matlab)

效果一览 代码获取私信博主基于麻雀搜索算法&#xff08;SSA&#xff09;的无人机路径规划&#xff08;Matlab&#xff09; 一、算法背景与核心思想 麻雀搜索算法&#xff08;Sparrow Search Algorithm, SSA&#xff09;是一种受麻雀群体觅食行为启发的元启发式算法&#xff0…...

STM32_GPIO系统外设学习

按照STM32MCUWIKI、参考手册的外设介绍----->CubeF4的软件包中相关的Exmple代码----->CubeMX设置截图加深理解记忆 资料链接&#xff1a;嵌入式开发_硬软件的环境搭建 我的飞书文档-GPIO篇 如果觉得内容不错&#xff0c;欢迎给我的飞书文档点赞。同时如果有什么意见或…...

使用Java爬虫根据关键词获取衣联网商品列表:实战指南

在电商领域&#xff0c;通过关键词搜索商品并获取商品列表是常见的需求。衣联网作为知名的电商平台&#xff0c;提供了丰富的服装商品资源。本文将详细介绍如何使用Java编写爬虫程序&#xff0c;根据关键词获取衣联网商品列表&#xff0c;并确保爬虫行为符合平台规范。 一、环…...

【操作系统安全】任务1:操作系统部署

目录 一、VMware Workstation Pro 17 部署 二、VMware Workstation 联网方式 三、VMware 虚拟机安装流程 四、操作系统介绍 五、Kali 操作系统安装 六、Windows 系统安装 七、Windows 系统网络配置 八、Linux 网络配置 CSDN 原创主页&#xff1a;不羁https://blog.csd…...

下载安装启动 VMware 个人免费版本

一、进入官网并登录账号下载软件 进入官网 [ https://www.vmware.com ]&#xff0c;点击Products&#xff0c;将页面划到最底下&#xff0c;点击 “SEE DESKTOP HYPERVISORS”按钮。 然后点击 Desktop hypevisor &#xff0c;会出现如下界面&#xff0c;可以根据自己的操作系…...

C#+AForge 实现视频录制

C#AForge 实现视频录制 ​ 在C#中&#xff0c;使用AForge 库实现视频录制功能是一个比较直接的过程。AForge 是一个开源的.NET框架&#xff0c;提供了许多用于处理图像和视频的类库。 开发步骤 安装AForge库 ​ 首先&#xff0c;确保你的项目中已经安装了 AForge.Video和AFo…...

doris:外表统计信息

外表统计信息的收集方式和收集内容与内表基本一致&#xff0c;目前支持对 Hive&#xff0c;Iceberg 和 Hudi 等外部表的收集。 自 2.0.3 版本之后&#xff0c;Hive 外表支持了自动和采样收集。 注意事项​ HMS 类型的 Iceberg 和 Hudi 外表&#xff0c;以及 JDBC 外表只支持手…...

SAP SD学习笔记31 - 销售BOM

上一篇讲 前受金处理(预付款处理)。 SAP SD学习笔记29 - 前受金处理(预收款处理)_fplt 付款申请与sd 数据表的关联关系-CSDN博客 本章继续讲SAP SD模块的其他知识&#xff1a;销售BOM。 销售BOM在现场还是会用到的。 目录 1&#xff0c;销售BOM概要 2&#xff0c;受注BOM的…...

大数据学习(63)- Zookeeper详解

&&大数据学习&& &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 用力所能及&#xff0c;改变世界。 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4dd;支持一下博主哦&#x1f91e; &#x1f…...

嵌入式八股C语言---面向对象篇

面向对象与面向过程 面向过程 就是把整个业务逻辑分成多个步骤,每步或每一个功能都可以使用一个函数来实现面向对象 对象是类的实例化,此时一个类就内部有属性和相应的方法 封装 在C语言里实现封装就是实现一个结构体,里面包括的成员变量和函数指针,然后在构造函数中,为结构体…...

Android UI性能优化

Android UI性能优化 一、UI性能优化基础 1.1 UI渲染原理 Android系统的UI渲染是通过一个被称为"UI线程"或"主线程"的单线程模型来完成的。系统会以16ms(约60fps)的固定时间间隔发送VSYNC信号,触发UI的渲染流程。如果一帧的处理时间超过16ms,就会出现丢…...

C# ListView设置标题头背景颜色和字体颜色

一、向ListView 添加数据 for (int i 1; i < 5; i) {ListViewItem litem new ListViewItem("data:"i);lv_WarnList.Items.Add(litem); }如果需要在ListView中绑定实体类对象的话&#xff0c;需要将数据放在Tag属性里 for (int i 1; i < 5; i) {AngleData …...

数字统计(信息学奥赛一本通-1096)

【题目描述】 请统计某个给定范围[L, R]的所有整数中&#xff0c;数字2出现的次数。比如给定范围[2, 22]&#xff0c;数字2在数2中出现了1次&#xff0c;在数12中出现1次&#xff0c;在数20中出现1次&#xff0c;在数21中出现1次&#xff0c;在数22中出现2次&#xff0c;所以数…...

嵌入式 ARM Linux 系统构成(6):应用层(Application Layer)

目录 一、应用层概述 二、应用层的核心组成 2.1 主应用程序&#xff08;Main Applications&#xff09; 2.2 系统服务&#xff08;System Services&#xff09; 2.3 用户界面&#xff08;User Interface&#xff09; 2.4 脚本与自动化工具 2.5 第三方库与框架 2.6 通信…...