Unix进程

文章目录

  • 命令行参数
  • 进程终止
    • 正常结束
    • 异常终止
    • exit和_exit
      • atexit
  • 环境变量
    • 环境变量性质
    • 环境表
    • shell中操作环境变量
      • 查看环境变量
      • 设置环境变量
    • 环境变量接口
      • 获取环境变量
      • 设置环境变量
    • 环境变量的继承性
  • 进程资源
    • shell命令查看进程的资源限制
  • 进程关系
    • 进程标识
    • 进程组
    • 会话
    • 控制终端
      • 控制终端ID
      • 前后台进程切换命令
      • jobs
      • bg
      • fg
    • 精灵进程(守护进程)
    • 闲逛进程
  • 进程创建
    • fork与写时复制
  • 进程等待
    • wait、waitpid
      • statloc参数详解
    • 僵尸进程与孤儿进程
  • 进程替换
    • exec函数解析
    • exec用户ID问题

命令行参数

ISO C标准的main函数具有2个参数,分别是argc和argv,它们标识==命令行参数的个数和命令行参数数组==,所谓命令行参数,就是通过命令行启动一个程序时所传入的参数。内核启动一个程序时调用一个特殊的例程——程序入口点进行命令行参数和环境变量的初始化,随后调用main函数执行用户逻辑。

./a.out 这是执行一个可执行程序最简单的方式,命令行参数个数为1,参数即./a.out

./a.out xxx yyy 命令行参数个数为3,参数分别为./a.out xxx yyy

命令行参数需要以空格作为分割符,shell会以空格为单位解析命令行参数。当程序运行时,它可以选择读取命令行参数进行特定的任务处理。

//简单加法器的实现
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char* argv[]){if(argc<3){printf("请至少输入2个操作数\n");return 0;}int res=0;for(int i=1;i<argc;++i){ //注意从下标为1处开始读取res+=atoi(argv[i]);}printf("求和的结果是%d\n",res);return 0;
}

运行效果展示image-20241113191304256

ISO C标准规定argv[argc]必须为NULL标识结束,因此for(int i=1;i<argc;++i) <==> for(int i=1;argv[i]!=NULL;++i)

进程终止

程序入口点在调用main函数后等待main函数返回,并进行结果处理。在用户源代码中通过main函数return来表示进程的结束。其实想要让一个进程结束的方式不只有在main函数中return。Unix环境下一共有8种进程终止的方式,其中5种是正常结束,3种是异常终止

正常结束

  1. main返回
  2. exit调用
  3. _exit调用
  4. 最后一个线程返回
  5. 最后一个线程pthread_exit调用

异常终止

  1. 调用abort
  2. 收到默认处理动作为终止进程的信号
  3. 最后一个线程对取消请求做出相应

本文只讨论正常结束的前3种情况,请注意main函数返回和exit调用是等价的

exit和_exit

exit是ISO C标准提供的一个接口,在程序的任意处调用exit函数都会导致进程结束;_exit是POSIX标准提供的一个接口,它是一个系统调用,会直接进入内核,在程序的任意处调用也会导致进程结束。

exit是对_exit的封装,相较于其的区别在于exit在进入内核之前会先调用一系列终止处理程序,其中包括刷新所有的C标准缓冲区。

exit和_exit都有一个整型类型的参数用于标识进程退出状态,宏常量EXIT_SUCCESS(扩展到0)标识进程正常结束且结果正确,EXIT_FAILURE(扩展到1)标识进程正常结束但结果错误

int exit(int status);
int _exit(int status);

atexit

可以通过atexit函数来自定义终止处理程序,执行exit时会按照atexit注册的函数逆向逐个调用。atexit必须传入一个无参数无返回值的函数

int atexit(void (*func)());
#include <stdio.h>
#include <stdlib.h>
void my_handler1(){printf("my_handler1\n");}
void my_handler2(){printf("my_handler2\n");}
void my_handler3(){printf("my_handler3\n");}
int main(){atexit(my_handler1);atexit(my_handler2);atexit(my_handler3);return 0;
}/*调用次序3->2->1*/

image-20241113195953789

环境变量

环境变量是操作系统层面的一种机制,用于向运行中的进程提供配置信息。它们是一组动态的键值对(key=value),可以在程序启动时或运行过程中被读取,从而影响程序的行为

环境变量性质

全局性:环境变量通常是全局的,意味着它们可以被任何进程访问,除非被明确地限制在一个特定的进程中。
动态性:环境变量可以在程序运行期间被更改,这使得程序可以根据环境的变化调整其行为。
继承性:当一个进程创建子进程时,子进程会继承父进程的环境变量副本,除非另有指定。

环境表

每一个进程都会收到一张环境表,它是一个字符指针数组,以NULL代表结束,全局变量environ指向环境表

image-20241114090609831

我们可以通过遍历environ所管理的环境表来获取所有的环境变量

#define _GNU_SOURCE
#include <unistd.h>
#include <stdio.h>
int main(){for(int i=0;environ[i]!=NULL;++i){printf("%s\n",environ[i]);}return 0;
}

部分Unix系统对main函数做出了扩展,允许传入第三个参数标识环境表(Linux就支持)

int main(int agrc,char* argv[],char* env[]);

也可以通过遍历env来获取所有的环境变量

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

2种方式效果等价

shell中操作环境变量

以Linux为例

查看环境变量

  1. echo ${KEY} ::=查看指定的环境变量值
  2. env\printenv ::=查看所有的环境变量值

设置环境变量

  1. export {KEY}={VALUE} ::=设置会话级别的环境变量,当会话关闭之后失效

环境变量接口

获取环境变量

char* getenv(const char* key);

如果存在环境变量key则返回对应的值,否则返回一个空指针

设置环境变量

int putenv(const char* kv);
int setenv(const char* key,const char* value,int mode);
int unsetenv(const char* name);
  • putenv无则新增,有则更新
  • setenv根据mode决定对已存在的环境变量的处理方式。非0则更新,0则保留
  • unsetenv删除环境变量

进程所设置的环境变量只对当前进程或者其子进程有效,并不影响其他进程

环境变量的继承性

putenv可以被子进程所继承,在CGI机制中往往父进程可以通过设置环境变量的方式向子进程传递必要的参数。

//环境变量继承性实验
int main(){putenv("OPEARND1=1000");putenv("OPEARND2=1000");pid_t pid=fork(); //表示创建一个子进程if(pid==0){printf("计算结果%d\n",atoi(getenv("OPEARND1"))+atoi(getenv("OPEARND2")));}else{printf("父进程设置环境变量完毕\n");}return 0;
}

进程资源

任何一个进程都具有一定的资源限制,例如占用CPU的最大时间,数据段的最大长度,文件最大字节数,打开文件上限等等,有些业务逻辑需要受限至于进程的资源限制,这时候查询和修改进程资源限制就显得很有必要。XSI扩展了2个接口用于查询和修改进程资源限制

#include <sys/time.h>
#include <sys/resource.h>
int getrlimit(int resource,struct rlimit* rlptr);
int setrlimit(int resource,const struct rlimit* rlptr);

typedef long unsigned int rlim_t;

struct rlimit{

​ rlim_t rlim_cur; //称为软限制,标识当前进程的限制值

​ rlim_t rlim_max; //称为硬限制,标识当前进程限制值的最大值

};

对于进程资源的修改操作并不是随意进行的,它必须遵守如下规则

  • root权限进程可以提高硬限制
  • 普通进程可以灵活调整软限制(不可超过硬限制) 和 降低硬限制(不可逆,调低了就回不去了)

eg

/*这是一个普通进程*/
#include <sys/time.h>
#include <sys/resource.h>
#include <stdio.h>
int main(){struct rlimit rl;getrlimit(RLIMIT_CORE,&rl); //获取当前进程核心转储文件最大值printf("soft rlimit_core:%lu\n",rl.rlim_cur);printf("hard rlimit_core:%lu\n",rl.rlim_max);getrlimit(RLIMIT_CPU,&rl); //获取当前进程占用CPU时间最大值printf("soft rlimit_cpu:%lu\n",rl.rlim_cur);printf("hard rlimit_core:%lu\n",rl.rlim_max);rl.rlim_cur=100;setrlimit(RLIMIT_CPU,&rl); //okrl.rlim_max=101;setrlimit(RLIMIT_CPU,&rl); //ok 普通进程可以降低硬限制
/*-----------------------------------------------------------------------*/rl.rlim_cur=200;setrlimit(RLIMIT_CPU,&rl); //error 软限制不可大于硬限制rl.rlim_max=200;setrlimit(RLIMIT_CPU,&rl); //error 普通进程不可抬高硬限制(之前降低过调不回去)return 0;
}

shell命令查看进程的资源限制

prlimit --pid={PROCESS_ID} --output=DESCRIPTION,RESOURCE,SOFT,HARD

进程关系

进程标识

内核需要管理每一个进程,需要通过一个唯一标识符来确定进程。进程的唯一标识符为称为==进程ID(PID),进程ID是一个非负整数,同一时刻不会存在进程ID相同的2个进程;每一个进程都有唯一的父进程,通过父进程ID(PPID)==所指示*(0号进程没有父进程,它是系统启动的第一个进程,负责进程调度,属于内核级进程)*;除此之外,一个进程还有许多标识信息,例如真实用户ID(UID),有效用户ID(EID),真实组ID(GID),有效组ID(EGID)。并且都具有相应的系统调用可以用于获取调用进程的相关标识信息。

#include <unistd.h>
pid_t getpid();
pid_t getppid();
pid_t getuid();
pid_t geteuid();
pid_t getgid();
pid_t getegid();//
pid_t getresuid();
pid_t getresgid();  //获取保留的用户ID和组ID,这2个接口不具备可移植性,但是Linux中有提供

进程组

多个进程可以构成一个进程组,通过进程组可以高效管理多个进程。每一个进程组都有一个组长进程,组长进程的进程ID等于进程组组ID==(PID=GID)==。在shell中可以通过管道连接符同时启动多个进程,这些进程会被归置为一组。

pid_t getpgrp(); //获取GID

进程组的生命周期随进程组中最后结束的进程,即只要进程组中存在一个进程,进程组就会存在。非组长进程可以加入别的进程组或者创建一个进程组自己成为组长。

int setpgid(pid_t pid,pid_t pgid);
//一个进程可以调用setpgid加入或创建一个进程组
//pid一般传入0标识进程自身

会话

image-20241114194210636

多个进程组成一个会话。进程可以通过调用setsid来创建一个新的会话,但是这个进程不能是组长进程(组长进程调用setsid会失败);如果调用成功,则调用进程会成为新会话里的唯一进程(会话首进程),会话也有标识即会话ID(SID),会话ID等于会话首进程ID

int setsid();
pid_t getsid(pid_t pid); 

getsid返回的是目标进程的进程组ID,而非会话ID,并且getsid函数作用域被限制在会话中,即一个进程通过getsid获得其他会话的进程组信息会失败

控制终端

一个会话可以具有一个控制终端,通过xshell远程登录Linux时其实就是建立一个带有控制终端的会话,用户可以通过控制终端与主机进行交互

带有控制终端的会话中进程组可以被划分为前台进程组后台进程组。其中前台进程组有且仅有一个,键盘发送信号就是发给前台进程组的(CTRL+C会强制结束当前会话的所有前台进程)

image-20241114194906877

一个非组长进程调用setsid创建新会话后默认没有控制终端。

控制终端ID

ps命令中有一个字段为TPGID,即控制终端ID,一般TPGID都不固定的。我们可以通过TPGID来判断一个进程是否属于前台进程,对于前台进程它的TPGID是非负的,而对于后台进程其TPGID为-1

前后台进程切换命令

jobs

jobs命令可以查看==当前会话==的后台进程信息,结果以组为单位呈现

image-20241114200106061

bg

bg命令可以将==一组==被CTRL+Z暂停的前台进程挂到后台继续运行

bg %{jobs_no}

fg

fg命令可以将==一组==后台进程挂到前台继续运行

fg %{jobs_no}

精灵进程(守护进程)

精灵进程也称为守护进程,是一种长时间运行的后台进程。它们通常在系统启动时启动,并持续运行直到系统关闭。精灵进程不与用户直接交互,而是执行特定的服务或任务。
特点

  1. 没有控制终端,通常在后台运行
  2. 生命周期长,通常从系统启动到关闭一直存在
  3. 通常作为系统服务运行,提供网络服务、定时任务等功能
  4. 创建时会调用fork()和setsid()等函数,确保自己独立于启动它的终端
  5. 通常会将当前工作目录更改为根目录,避免占用挂载的文件系统
  6. 设置文件权限掩码,确保创建的文件具有适当的权限
  7. 关闭不需要的文件描述符,避免占用系统资源

闲逛进程

闲逛进程也称为空闲进程或调度进程,是操作系统中的一个特殊进程。当系统中没有其他可运行的进程时,调度器会将CPU的控制权交给闲逛进程。闲逛进程的主要目的是确保CPU不会空转,从而防止CPU进入不受控制的状态。
特点

  1. 具有最低的优先级,只有当系统中没有其他可运行的进程时才会被调度
  2. 主要任务是让CPU保持忙碌状态,即使这种“忙碌”实际上是空闲的
  3. 通常执行一个简单的循环,使CPU进入低功耗状态
  4. 不消耗实际的计算资源,只是占用了CPU的时间片
  5. 在多处理器系统中,每个CPU核心通常都有一个对应的闲逛进程

进程创建

fork与写时复制

一个进程可以通过fork创建子进程,子进程会继承父进程的进程组ID(即子进程和父进程属于同一个进程组),内核会为fork生成的子进程生成一份父进程的地址空间快照,并且在子进程不进行数据修改操作时,子进程与父进程共享同一块物理内存;如果子进程对数据发生修改操作,则内核会建立被修改的数据的副本以确保子进程不会影响父进程,这种机制成为写时复制技术

值得注意的是,子进程会继承父进程的文件描述符表(重定位信息也会得到继承),并且==与父进程共享一个文件表项----->共享文件偏移量==,这意味着如果父子进程同时往一个文件中写入数据可能出现数据错乱的情况;但这个特性也有其优势,如果子进程向标准输出打印了一串信息后退出,假设父进程等待子进程结束,那么它能很自然得(不需要复杂的控制)将一些子进程的退出状态输出到标准输出上(父进程的输出信息一定是在子进程打印的信息之后的)

image-20241114202248187

进程等待

当一个进程正常或异常终止时,内核向其父进程发送SIGCHID信号,告诉父进程其子进程已经结束。父进程需要在合适的时机调用系统API获取子进程的退出信息并回收已经结束的子进程资源(子进程的大部分资源已经被内核回收,但是内核仍会保留子进程的进程控制块信息,因为这里面存储了子进程的退出信息)。标准为我们提供了wait和waitpid两个接口用于进程等待,进程调用wait和waitpid函数默认陷入阻塞直到子进程返回。

wait、waitpid

#include <sys/wait.h>
pid_t wait(int* statloc);
pid_t waitpid(pid_t pid,int* statloc,int options);

***wait:***阻塞等待任意子进程,如果不关心子进程的退出信息,参数写NULL,否则子进程的退出信息会被写入到statloc所指向的空间。

**waitpid:参数statloc与wait一致,默认为阻塞等待,如果将options参数设置为WNOHANG则为非阻塞等待。

waitpid的pid参数不同的值具有不同的意义

  1. pid==-1,等待任意子进程(如果options=0则退化为wait)
  2. pid>0,等待进程id等于pid的子进程
  3. pid==0,等待组ID为调用进程组ID的任意子进程
  4. pid<0,等待组ID为pid绝对值的任意子进程

statloc参数详解

statloc保存了子进程的退出状态,按照比特位划分成不同的段,每一个段具有不同的意义(正常退出码,异常结束信号,core文件是否生成)。标准提供了一组宏函数用来从statloc中获取相应的信息。

image-20241114204815202

image-20241114204636150

僵尸进程与孤儿进程

如果父进程始终不对子进程进行回收操作,那么子进程便会一直占用进程ID号,内核也会一直保留其进程控制块,但子进程不会占用CPU资源,此时的子进程处于一种僵死状态(Z),称为==僵尸进程==,僵尸进程应该被避免,因为它会造成内存资源的浪费。一种解决僵尸进程的方式是杀死父进程,让僵尸进程称为孤儿进程(如果父进程先于子进程结束,则子进程变成孤儿进程,但会很快被1号进程所领养),让1号进程称为其父进程,1号进程一定会调用进程等待函数回收子进程的资源。

进程替换

大部分情况下,fork函数创建新的子进程后,子进程会调用exec家族函数以执行另个程序。当进程调用一种exec函数时,该进程执行的程序完全替换为新程序,而新程序则从其main 函数开始执行。调用exec并不创建新进程,所以前后的进程ID并未改变。exec只是用磁盘上的一个新程序替换了当前进程的正文段、数据段、堆段和栈段。

exec会继承如下属性(重点关注框出来的)

image-20241114212035951

exec函数解析

image-20241114210623696

请注意以filename为参数的exec函数遵循如下规则:

  1. filename如果是绝对路径等价于pathname
  2. filename仅是一个文件名则从环境变量PATH和当前目录查找可执行文件

exec用户ID问题

编写程序时应该遵守==最小权限原则,进程在运行时对应的权限应该为当前能正常处理任务的最低权限==。最小权限原则可以避免进程因为权限过高而进行恶意读写的行为。exec所执行的可执行文件所需要的最小权限可能低于在调用exec之前的权限,为了能够降低权限,建议将目标可执行文件设置用户ID为,保证exec在进程替换后有效用户ID是可执行文件的用户ID。

image-20241114220125728

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

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

相关文章

供应链管理、一件代发系统功能及源码分享 PHP+Mysql

随着电商行业的不断发展&#xff0c;传统的库存管理模式已经逐渐无法满足市场需求。越来越多的企业选择“一件代发”模式&#xff0c;即商家不需要自己储备商品库存&#xff0c;而是将订单直接转给供应商&#xff0c;由供应商直接进行发货。这种方式极大地降低了企业的运营成本…

关于离散模型优化的一份介绍

离散模型优化是运筹学和计算机科学领域中的一个重要分支&#xff0c;它主要研究如何在有限的、通常是计数的决策变量空间中寻找最优解。这类问题通常出现在资源分配、生产计划、物流管理、网络设计等实际应用场景中。在这篇文章中就将介绍离散模型优化中关于线性规划等部分内容…

hadoop_yarn详解

YARN秒懂 YARN定义基础架构ResourceManagerNodeManagerApplicationMasterContainer 工作流程资源调度器FIFO SchedulerCapacity SchedulerFair Scheduler 常用命令 YARN定义 YARN&#xff08;Yet Another Resource Negotiator&#xff09;是Hadoop的一个框架&#xff0c;它负责…

【MYSQL】数据库日志 (了解即可)

一、错误日志 可以通过 tail查看文件的日志的&#xff0c;如果发生错误&#xff0c;就会在日志里出现问题。 二、二进制日志&#xff08;binlog&#xff09; BINLOG记录了insert delete update 以及 alter create drop 等语句。作用是灾难时的数据恢复&#xff0c;还有就是主…

接口测试整体框架

接口测试 1. 接口 接口&#xff0c;也叫api&#xff08;Application Programming Interface&#xff0c;应用程序编程接口&#xff09;&#xff0c;接口&#xff08;Interface&#xff09;是指不同软件组件或系统之间进行交互的点。接口定义了组件之间如何通信&#xff0c;包括…

递归搜索与回溯算法

递归搜索与回溯算法 名词解释 递归 在解决⼀个规模为n的问题时&#xff0c;如果满⾜以下条件&#xff0c;我们可以使⽤递归来解决&#xff1a; a. 问题可以被划分为规模更⼩的⼦问题&#xff0c;并且这些⼦问题具有与原问题相同的解决⽅法。 b. 当我们知道规模更⼩的⼦问题&…

基于java+SpringBoot+Vue的中小型医院网站设计与实现

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; Springboot mybatis Maven mysql5.7或8.0等等组成&#x…

图神经网络研究综述(GNN),非常详细收藏我这一篇就够了!

图神经网络由于其在处理非欧空间数据和复杂特征方面的优势&#xff0c;受到广泛关注并应用于推荐系统、知识图谱、交通道路分析等场景。 大规模图结构的不规则性、节点特征的复杂性以及训练样本的依赖性给图神经网络模型的计算效率、内存管理以及分布式系统中的通信开销带来巨…

36.安卓逆向-壳-脱壳实战

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a;图灵Python学院 本人写的内容纯属胡编乱造&#xff0c;全都是合成造假&#xff0c;仅仅只是为了娱乐&#xff0c;请不要盲目相信。第一…

办公耗材管理新纪元:系统化解企业挑战,助力高效运营

在当今竞争激烈的商业环境中&#xff0c;无论是大型企业还是中小型企业&#xff0c;办公耗材管理都是关乎企业运营效率与成本控制的关键环节。有效的办公耗材管理不仅能显著降低运营成本&#xff0c;还能提升整体工作效率&#xff0c;确保业务的顺畅进行。然而&#xff0c;许多…

2、 家庭网络发展现状

上一篇我们讲了了解家庭网络历史(https://blog.csdn.net/xld_hung/article/details/143639618?spm1001.2014.3001.5502),感兴趣的同学可以看对应的文章&#xff0c;本章我们主要讲家庭网络发展现状。 关于家庭网络发展现状&#xff0c;我们会从国内大户型和小户型的网络说起&…

时序论文20|ICLR20 可解释时间序列预测N-BEATS

论文标题&#xff1a;N-BEATS N EURAL BASIS EXPANSION ANALYSIS FOR INTERPRETABLE TIME SERIES FORECASTING 论文链接&#xff1a;https://arxiv.org/pdf/1905.10437.pdf 前言 为什么时间序列可解释很重要&#xff1f;时间序列的可解释性是确保模型预测结果可靠、透明且易…

hadoop_capacity-scheduler.xml

hadoop3.2.3capacity-scheduler.xml配置实例 <configuration><property><!-- 可以处于等待和运行状态的应用程序的最大数量 --><name>yarn.scheduler.capacity.maximum-applications</name><value>10000</value></property>&l…

小白必看:知识库搭建的详细拆解步骤

在当今信息爆炸的时代&#xff0c;企业知识库成为了企业积累、管理和分享知识的重要工具。对于初学者来说&#xff0c;搭建一个企业知识库可能看起来是一项复杂的任务&#xff0c;但通过以下步骤&#xff0c;即使是小白也能轻松上手。本文将详细拆解搭建企业知识库的步骤&#…

042 异步编排

文章目录 什么是异步Future异步编排1串行关系执行thenRunthenApplythenAcceptthenCompose 2聚合ANDthenCombinethenAcceptBothrunAfterBoth 3OR聚合applyToEiteracceptEitherrunAfterEither 4异常处理exceptionallywhenCompletehandle 异步开启1RunAsync:没有使用自定义线程池&…

【算法设计与分析】采用特征方程求解递归方程

文章目录 K阶常系数线性齐次递归方程K阶常系数线性【非】齐次递归方程例题例1&#xff1a;齐次无重根例2&#xff1a;齐次有重根例3&#xff1a;非齐次&#xff0c;g(n)是n的多项式例4&#xff1a;非齐次&#xff0c;g(n)是n的指数形式&#xff0c;a不是重根 练习其它求解递归方…

SAP ABAP开发学习——function alv复选框设置

1.关于报表复选框的创建 首先该报表要调用功能函数 这里参照SLIS_LAYOUT_ALV定义字段 参照来源 具体字段的定义 双击 双击这两个查看需要的字段 box_fieldname就是复选框 需要在内表定义需要的名称&#xff0c;这里使用‘BOX 相关内容完成

5.7 与 8.0 对相同文件的 LOAD DATA 语句结果不同

5.7 与 8.0 对相同文件的 LOAD DATA 语句结果不同 问题描述 某客户现场支持&#xff0c;由MySQL 5.7.21升级MySQL 8.0.25后&#xff0c;通过LOAD DATA导入文件&#xff0c;当同一会话连续导入不同的编码&#xff08;UTF8/GB18030&#xff09;文件时会出现乱码。数据库版本未升…

河南梦想城供配电项目-综合监控平台[智能运维+集中监控]

河南梦想城供配电项目-综合监控平台软件 可分为主机系统&#xff08;针对单个站房的实时监测&#xff09;和集中云平台&#xff08;针对多个站房的集中管理&#xff09;&#xff0c;可实现电气设备隐患预警&#xff0c;站房环境风险可控&#xff0c;系统与电力平台以IEC61850标…

每日计划-1114

完成 14. 最长公共前缀 #include <string> #include <vector>class Solution { public:string longestCommonPrefix(std::vector<std::string>& strs) {if (!strs.size()) {return "";}int length strs[0].size();int count strs.size();fo…